Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ExportPkcs12 #112569

Merged
merged 19 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add X509Certificate2Collection ExportPkcs12
  • Loading branch information
vcsjones committed Feb 14, 2025
commit 1d6182b968e457bba43d585ff16cfd368fc7d44b
Original file line number Diff line number Diff line change
Expand Up @@ -3322,6 +3322,8 @@ public void AddRange(System.Security.Cryptography.X509Certificates.X509Certifica
public byte[]? Export(System.Security.Cryptography.X509Certificates.X509ContentType contentType) { throw null; }
public byte[]? Export(System.Security.Cryptography.X509Certificates.X509ContentType contentType, string? password) { throw null; }
public string ExportCertificatePems() { throw null; }
public byte[] ExportPkcs12(System.Security.Cryptography.PbeParameters exportParameters, string? password) { throw null; }
public byte[] ExportPkcs12(System.Security.Cryptography.X509Certificates.Pkcs12ExportPbeParameters exportParameters, string? password) { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
public string ExportPkcs7Pem() { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,5 +404,48 @@ internal static PbeParameters MapExportParametersToPbeParameters(Pkcs12ExportPbe
_ => throw new CryptographicException(),
};
}

internal static void VerifyExportParameters(Pkcs12ExportPbeParameters exportParameters)
{
if (exportParameters is < Pkcs12ExportPbeParameters.Default or > Pkcs12ExportPbeParameters.Pbes2Aes256Sha256)
{
throw new ArgumentOutOfRangeException(nameof(exportParameters));
}
}

internal static void VerifyExportParameters(PbeParameters exportParameters)
{
if (exportParameters.EncryptionAlgorithm is
PbeEncryptionAlgorithm.Aes128Cbc or PbeEncryptionAlgorithm.Aes192Cbc or PbeEncryptionAlgorithm.Aes256Cbc)
{
switch (exportParameters.HashAlgorithm.Name)
{
case HashAlgorithmNames.SHA1:
case HashAlgorithmNames.SHA256:
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
return;
case null or "":
throw new CryptographicException(SR.Cryptography_HashAlgorithmNameNullOrEmpty);
default:
// Let SHA-3 fall in to default since SHA-3 has not been brought up for PKCS12.
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownAlgorithmIdentifier, exportParameters.HashAlgorithm.Name));
}
}
else if (exportParameters.EncryptionAlgorithm is PbeEncryptionAlgorithm.TripleDes3KeyPkcs12)
{
switch (exportParameters.HashAlgorithm.Name)
{
case HashAlgorithmNames.SHA1:
return;
case null or "":
throw new CryptographicException(SR.Cryptography_HashAlgorithmNameNullOrEmpty);
default:
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownAlgorithmIdentifier, exportParameters.HashAlgorithm.Name));
}
}

throw new CryptographicException(SR.Format(SR.Cryptography_UnknownAlgorithmIdentifier, exportParameters.EncryptionAlgorithm));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ public virtual byte[] Export(X509ContentType contentType, SecureString? password
/// </summary>
/// <param name="exportParameters">The algorithm parameters to use for the export.</param>
/// <param name="password">The password to use for the export.</param>
/// <returns>A byte array containing the encoded certificate and private key.</returns>
/// <returns>A byte array containing the encoded PKCS#12.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="exportParameters"/> is not a valid value.
/// </exception>
Expand All @@ -361,7 +361,7 @@ public virtual byte[] Export(X509ContentType contentType, SecureString? password
/// </exception>
public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, string? password)
{
VerifyExportParameters(exportParameters);
Helpers.VerifyExportParameters(exportParameters);

if (Pal is null)
throw new CryptographicException(ErrorCode.E_POINTER); // Consistent with existing Export method.
Expand All @@ -377,7 +377,7 @@ public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, string? p
/// </summary>
/// <param name="exportParameters">The algorithm parameters to use for the export.</param>
/// <param name="password">The password to use for the export.</param>
/// <returns>A byte array containing the encoded certificate and private key.</returns>
/// <returns>A byte array containing the encoded PKCS#12.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="exportParameters"/> is <see langword="null"/> .
/// </exception>
Expand All @@ -399,7 +399,7 @@ public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, string? p
public byte[] ExportPkcs12(PbeParameters exportParameters, string? password)
{
ArgumentNullException.ThrowIfNull(exportParameters);
VerifyExportParameters(exportParameters);
Helpers.VerifyExportParameters(exportParameters);

if (Pal is null)
throw new CryptographicException(ErrorCode.E_POINTER); // Consistent with existing Export method.
Expand Down Expand Up @@ -751,48 +751,5 @@ private static void VerifyContentType(X509ContentType contentType)
if (!(contentType == X509ContentType.Cert || contentType == X509ContentType.SerializedCert || contentType == X509ContentType.Pkcs12))
throw new CryptographicException(SR.Cryptography_X509_InvalidContentType);
}

private static void VerifyExportParameters(Pkcs12ExportPbeParameters exportParameters)
{
if (exportParameters is < Pkcs12ExportPbeParameters.Default or > Pkcs12ExportPbeParameters.Pbes2Aes256Sha256)
{
throw new ArgumentOutOfRangeException(nameof(exportParameters));
}
}

private static void VerifyExportParameters(PbeParameters exportParameters)
{
if (exportParameters.EncryptionAlgorithm is
PbeEncryptionAlgorithm.Aes128Cbc or PbeEncryptionAlgorithm.Aes192Cbc or PbeEncryptionAlgorithm.Aes256Cbc)
{
switch (exportParameters.HashAlgorithm.Name)
{
case HashAlgorithmNames.SHA1:
case HashAlgorithmNames.SHA256:
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
return;
case null or "":
throw new CryptographicException(SR.Cryptography_HashAlgorithmNameNullOrEmpty);
default:
// Let SHA-3 fall in to default since SHA-3 has not been brought up for PKCS12.
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownAlgorithmIdentifier, exportParameters.HashAlgorithm.Name));
}
}
else if (exportParameters.EncryptionAlgorithm is PbeEncryptionAlgorithm.TripleDes3KeyPkcs12)
{
switch (exportParameters.HashAlgorithm.Name)
{
case HashAlgorithmNames.SHA1:
return;
case null or "":
throw new CryptographicException(SR.Cryptography_HashAlgorithmNameNullOrEmpty);
default:
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownAlgorithmIdentifier, exportParameters.HashAlgorithm.Name));
}
}

throw new CryptographicException(SR.Format(SR.Cryptography_UnknownAlgorithmIdentifier, exportParameters.EncryptionAlgorithm));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,63 @@ public bool Contains(X509Certificate2 certificate)
}
}

/// <summary>
/// Exports the certificate and private key in PKCS#12 / PFX format.
/// </summary>
/// <param name="exportParameters">The algorithm parameters to use for the export.</param>
/// <param name="password">The password to use for the export.</param>
/// <returns>A byte array containing the encoded PKCS#12.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="exportParameters"/> is not a valid value.
/// </exception>
/// <exception cref="CryptographicException">
/// <para>The export operation failed.</para>
/// </exception>
public byte[] ExportPkcs12(Pkcs12ExportPbeParameters exportParameters, string? password)
{
Helpers.VerifyExportParameters(exportParameters);

using (SafePasswordHandle safePasswordHandle = new(password, passwordProvided: true))
using (IExportPal storePal = StorePal.LinkFromCertificateCollection(this))
{
return storePal.ExportPkcs12(exportParameters, safePasswordHandle);
}
}

/// <summary>
/// Exports the certificates and private keys in PKCS#12 / PFX format.
/// </summary>
/// <param name="exportParameters">The algorithm parameters to use for the export.</param>
/// <param name="password">The password to use for the export.</param>
/// <returns>A byte array containing the encoded PKCS#12.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="exportParameters"/> is <see langword="null"/> .
/// </exception>
/// <exception cref="CryptographicException">
/// <para>The export operation failed.</para>
/// <para>-or-</para>
/// <para>
/// <paramref name="exportParameters"/> contains an invalid or unsupported hash algorithm for
/// <see cref="PbeParameters.HashAlgorithm"/>.
/// </para>
/// <para>-or-</para>
/// <para>
/// <paramref name="exportParameters"/> contains an invalid encryption algorithm for
/// <see cref="PbeParameters.EncryptionAlgorithm"/>.
/// </para>
/// </exception>
public byte[] ExportPkcs12(PbeParameters exportParameters, string? password)
{
ArgumentNullException.ThrowIfNull(exportParameters);
Helpers.VerifyExportParameters(exportParameters);

using (SafePasswordHandle safePasswordHandle = new(password, passwordProvided: true))
using (IExportPal storePal = StorePal.LinkFromCertificateCollection(this))
{
return storePal.ExportPkcs12(exportParameters, safePasswordHandle);
}
}

public byte[]? Export(X509ContentType contentType, string? password)
{
using (var safePasswordHandle = new SafePasswordHandle(password, passwordProvided: true))
Expand Down