Skip to content

Commit 4112020

Browse files
authored
Ensure 3DES Exports are actually 3DES on all versions of Windows
1 parent 0205063 commit 4112020

File tree

1 file changed

+35
-36
lines changed

1 file changed

+35
-36
lines changed

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Windows.Export.cs

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ public void MoveTo(X509Certificate2Collection collection)
7575
}
7676

7777
case X509ContentType.Pkcs12:
78-
byte[] pkcs12 = ExportPkcs12(false, password, out bool aes256Sha256);
79-
Debug.Assert(!aes256Sha256);
80-
return pkcs12;
78+
return ExportPkcs12Core(null, password);
8179

8280
case X509ContentType.SerializedStore:
8381
return SaveToMemoryStore(Interop.Crypt32.CertStoreSaveAs.CERT_STORE_SAVE_AS_STORE);
@@ -92,55 +90,54 @@ public void MoveTo(X509Certificate2Collection collection)
9290

9391
public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, SafePasswordHandle password)
9492
{
95-
bool tryAes256sha256 = exportParameters is
96-
Pkcs12ExportPbeParameters.Default or
97-
Pkcs12ExportPbeParameters.Pbes2Aes256Sha256;
98-
99-
byte[] exported = ExportPkcs12(tryAes256sha256, password, out bool aes256Sha256);
100-
101-
// What we asked for and what we got are the same - so return it as-is.
102-
if (tryAes256sha256 == aes256Sha256)
103-
{
104-
return exported;
105-
}
106-
107-
if (!tryAes256sha256)
108-
{
109-
Debug.Fail("3DES PKCS12 export failed.");
110-
throw new CryptographicException();
111-
}
112-
113-
return ReEncryptAndSealPkcs12(exported, password, Helpers.WindowsAesPbe);
93+
return ExportPkcs12Core(exportParameters, password);
11494
}
11595

11696
public byte[] ExportPkcs12(PbeParameters exportParameters, SafePasswordHandle password)
11797
{
118-
byte[] exported = ExportPkcs12(preferAes256Sha256: true, password, out _);
98+
byte[] exported = ExportPkcs12Core(null, password);
11999
return ReEncryptAndSealPkcs12(exported, password, exportParameters);
120100
}
121101

122-
private unsafe byte[] ExportPkcs12(bool preferAes256Sha256, SafePasswordHandle password, out bool aes256Sha256)
102+
private unsafe byte[] ExportPkcs12Core(Pkcs12ExportPbeParameters? exportParameters, SafePasswordHandle password)
123103
{
124104
Interop.Crypt32.DATA_BLOB dataBlob = new Interop.Crypt32.DATA_BLOB(IntPtr.Zero, 0);
125105
Interop.Crypt32.PFXExportFlags flags =
126106
Interop.Crypt32.PFXExportFlags.EXPORT_PRIVATE_KEYS |
127107
Interop.Crypt32.PFXExportFlags.REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY;
128108

129109
Interop.Crypt32.PKCS12_PBES2_EXPORT_PARAMS* exportParams = null;
130-
aes256Sha256 = preferAes256Sha256 && s_supportsAes256Sha256;
110+
PbeParameters? reEncodeParameters = null;
131111

132-
if (aes256Sha256)
112+
if (exportParameters is Pkcs12ExportPbeParameters.Pbes2Aes256Sha256 or Pkcs12ExportPbeParameters.Default)
133113
{
134-
flags |= Interop.Crypt32.PFXExportFlags.PKCS12_EXPORT_PBES2_PARAMS;
135-
// PKCS12_PBES2_ALG_AES256_SHA256
136-
char* algStr = stackalloc char[] { 'A', 'E', 'S', '2', '5', '6', '-', 'S', 'H', 'A', '2', '5', '6', '\0' };
137-
Interop.Crypt32.PKCS12_PBES2_EXPORT_PARAMS p = new()
114+
if (s_supportsAes256Sha256)
138115
{
139-
dwSize = (uint)Marshal.SizeOf<Interop.Crypt32.PKCS12_PBES2_EXPORT_PARAMS>(),
140-
hNcryptDescriptor = 0,
141-
pwszPbes2Alg = algStr,
142-
};
143-
exportParams = &p;
116+
flags |= Interop.Crypt32.PFXExportFlags.PKCS12_EXPORT_PBES2_PARAMS;
117+
// PKCS12_PBES2_ALG_AES256_SHA256
118+
char* algStr = stackalloc char[] { 'A', 'E', 'S', '2', '5', '6', '-', 'S', 'H', 'A', '2', '5', '6', '\0' };
119+
Interop.Crypt32.PKCS12_PBES2_EXPORT_PARAMS p = new()
120+
{
121+
dwSize = (uint)Marshal.SizeOf<Interop.Crypt32.PKCS12_PBES2_EXPORT_PARAMS>(),
122+
hNcryptDescriptor = 0,
123+
pwszPbes2Alg = algStr,
124+
};
125+
exportParams = &p;
126+
}
127+
else
128+
{
129+
reEncodeParameters = Helpers.WindowsAesPbe;
130+
}
131+
}
132+
else if (exportParameters == Pkcs12ExportPbeParameters.Pkcs12TripleDesSha1)
133+
{
134+
// Older Windows is not guaranteed to export in 3DES. If 3DES was asked for explicitly, then re-encode
135+
// it as 3DES.
136+
reEncodeParameters = Helpers.Windows3desPbe;
137+
}
138+
else
139+
{
140+
Debug.Assert(exportParameters is null);
144141
}
145142

146143
if (!Interop.Crypt32.PFXExportCertStoreEx(_certStore, ref dataBlob, password, exportParams, flags))
@@ -160,7 +157,9 @@ private unsafe byte[] ExportPkcs12(bool preferAes256Sha256, SafePasswordHandle p
160157
}
161158
}
162159

163-
return pbEncoded;
160+
return reEncodeParameters is not null ?
161+
ReEncryptAndSealPkcs12(pbEncoded, password, reEncodeParameters) :
162+
pbEncoded;
164163
}
165164

166165
private static byte[] ReEncryptAndSealPkcs12(byte[] pkcs12, SafePasswordHandle password, PbeParameters newPbeParameters)

0 commit comments

Comments
 (0)