From e037d1e4d476bf7d28e622355af44baa419880d7 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Sat, 6 Jun 2020 15:36:16 -0400 Subject: [PATCH 01/10] Import X509 certificate and collections from PEM. --- .../Internal/Cryptography/PemEnumerator.cs | 60 +++ .../System/Security/Cryptography/PemLabels.cs | 1 + ....Security.Cryptography.X509Certificates.cs | 6 + .../src/Resources/Strings.resx | 6 + ...urity.Cryptography.X509Certificates.csproj | 6 +- .../X509Certificates/X509Certificate2.cs | 345 ++++++++++++++++++ .../X509Certificate2Collection.cs | 90 +++++ .../tests/CollectionTests.cs | 50 +++ .../tests/PemTestData.cs | 275 ++++++++++++++ ...Cryptography.X509Certificates.Tests.csproj | 2 + .../tests/X509Certificate2PemTests.cs | 274 ++++++++++++++ 11 files changed, 1114 insertions(+), 1 deletion(-) create mode 100644 src/libraries/Common/src/Internal/Cryptography/PemEnumerator.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/tests/PemTestData.cs create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs diff --git a/src/libraries/Common/src/Internal/Cryptography/PemEnumerator.cs b/src/libraries/Common/src/Internal/Cryptography/PemEnumerator.cs new file mode 100644 index 00000000000000..9622f7d712ebbd --- /dev/null +++ b/src/libraries/Common/src/Internal/Cryptography/PemEnumerator.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace Internal.Cryptography +{ + internal readonly ref struct PemEnumerator + { + private readonly ReadOnlySpan _contents; + + public PemEnumerator(ReadOnlySpan contents) + { + _contents = contents; + } + + public Enumerator GetEnumerator() => new Enumerator(_contents); + + internal ref struct Enumerator + { + private ReadOnlySpan _contents; + private PemFields _pemFields; + + public Enumerator(ReadOnlySpan contents) + { + _contents = contents; + _pemFields = default; + } + + public PemFieldItem Current => new PemFieldItem(_contents, _pemFields); + + public bool MoveNext() + { + _contents = _contents[_pemFields.Location.End..]; + return PemEncoding.TryFind(_contents, out _pemFields); + } + + internal readonly ref struct PemFieldItem + { + private readonly ReadOnlySpan _contents; + private readonly PemFields _pemFields; + + public PemFieldItem(ReadOnlySpan contents, PemFields pemFields) + { + _contents = contents; + _pemFields = pemFields; + } + + public void Deconstruct(out ReadOnlySpan contents, out PemFields pemFields) + { + contents = _contents; + pemFields = _pemFields; + } + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs b/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs index 5adf4ab5d83d96..99dca98bca2d8a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs @@ -12,5 +12,6 @@ internal static class PemLabels internal const string RsaPublicKey = "RSA PUBLIC KEY"; internal const string RsaPrivateKey = "RSA PRIVATE KEY"; internal const string EcPrivateKey = "EC PRIVATE KEY"; + internal const string X509Certificate = "CERTIFICATE"; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs index e34a984d66bd36..2e175633880c1e 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/ref/System.Security.Cryptography.X509Certificates.cs @@ -234,6 +234,10 @@ public X509Certificate2(string fileName, string? password, System.Security.Crypt public System.Security.Cryptography.X509Certificates.X500DistinguishedName SubjectName { get { throw null; } } public string Thumbprint { get { throw null; } } public int Version { get { throw null; } } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 CreateFromEncryptedPem(System.ReadOnlySpan certPem, System.ReadOnlySpan keyPem, System.ReadOnlySpan password) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 CreateFromEncryptedPemFile(string certPemFilePath, System.ReadOnlySpan password, string? keyPemFilePath = null) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 CreateFromPem(System.ReadOnlySpan certPem, System.ReadOnlySpan keyPem) { throw null; } + public static System.Security.Cryptography.X509Certificates.X509Certificate2 CreateFromPemFile(string certPemFilePath, string? keyPemFilePath = null) { throw null; } public static System.Security.Cryptography.X509Certificates.X509ContentType GetCertContentType(byte[] rawData) { throw null; } public static System.Security.Cryptography.X509Certificates.X509ContentType GetCertContentType(string fileName) { throw null; } public string GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType nameType, bool forIssuer) { throw null; } @@ -269,6 +273,8 @@ public void Import(byte[] rawData) { } public void Import(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } public void Import(string fileName) { } public void Import(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + public void ImportFromPem(System.ReadOnlySpan certPem) { } + public void ImportFromPemFile(string certPemFilePath) { } public void Insert(int index, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } public void Remove(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } public void RemoveRange(System.Security.Cryptography.X509Certificates.X509Certificate2Collection certificates) { } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx index bcbe019c37abc1..8b67e79c56ac9f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx @@ -292,6 +292,12 @@ The platform does not have a definition for an X509 certificate store named '{0}' with a StoreLocation of '{1}', and does not support creating it. + + The certificate contents do not contain a PEM with a CERTIFICATE label, or the content is malformed. + + + The key contents do not contain a PEM, the content is malformed, or the key does not match the certificate algorithm. + Enumeration has not started. Call MoveNext. diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 6507510a915f16..7ee5e30391cdf4 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -24,6 +24,10 @@ Link="Common\System\Security\Cryptography\KeyBlobHelpers.cs" /> + + Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.xml @@ -679,6 +683,6 @@ - + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index 4239392ffa136a..8c65f162fe55f9 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -5,6 +5,7 @@ using Internal.Cryptography; using Internal.Cryptography.Pal; using System; +using System.Diagnostics; using System.IO; using System.Runtime.Serialization; using System.Security; @@ -22,6 +23,9 @@ public class X509Certificate2 : X509Certificate private volatile PublicKey? _lazyPublicKey; private volatile AsymmetricAlgorithm? _lazyPrivateKey; private volatile X509ExtensionCollection? _lazyExtensions; + private static readonly string[] s_EcPublicKeyPrivateKeyLabels = { PemLabels.EcPrivateKey, PemLabels.Pkcs8PrivateKey }; + private static readonly string[] s_RsaPublicKeyPrivateKeyLabels = { PemLabels.RsaPrivateKey, PemLabels.Pkcs8PrivateKey }; + private static readonly string[] s_DsaPublicKeyPrivateKeyLabels = { PemLabels.Pkcs8PrivateKey }; public override void Reset() { @@ -628,6 +632,347 @@ public bool Verify() } } + /// + /// Creates a new X509 certificate from the file contents of an RFC 7468 PEM-encoded + /// certificate and private key. + /// + /// The path for the PEM-encoded X509 certificate. + /// + /// If specified, the path for the PEM-encoded private key. + /// If unspecified, the file will be used to load + /// the private key. + /// + /// A new certificate with the private key. + /// + /// + /// The contents of the file path in do not contain + /// a PEM-encoded certificate, or it is malformed. + /// + /// -or- + /// + /// The contents of the file path in do not contain a + /// PEM-encoded private key, or it is malformed. + /// + /// -or- + /// + /// The contents of the file path in contains + /// a key that does not match the public key in the certificate. + /// + /// + /// + /// The path specified in is null. + /// + /// + /// The certificate uses an unknown public key algorithm. + /// + /// + /// + /// See for additional documentation about + /// exceptions that can be thrown. + /// + /// + /// The SubjectPublicKeyInfo from the certificate determines what PEM labels are accepted for the private key. + /// For RSA certificates, accepted private key PEM labels are "RSA PRIVATE KEY" and "PRIVATE KEY". + /// For ECDSA certificates, accepted private key PEM labels are "EC PRIVATE KEY" and "PRIVATE KEY". + /// For DSA certificates, the accepted private key PEM label is "PRIVATE KEY". + /// + /// PEM-encoded items that have a different label are ignored. + /// + /// Combined PEM-encoded certificates and keys do not require a specific order. For the certificate, the + /// the first certificate with a CERTIFICATE label is loaded. For the private key, the first private + /// key with an acceptable label is loaded. More advanced scenarios for loading certificates and + /// private keys can leverage to enumerate + /// PEM-encoded values and apply any custom loading behavior. + /// + /// + /// For password protected PEM-encoded keys, use to specify a password. + /// + /// + public static X509Certificate2 CreateFromPemFile(string certPemFilePath, string? keyPemFilePath = default) + { + if (certPemFilePath is null) + throw new ArgumentNullException(nameof(certPemFilePath)); + + ReadOnlySpan certContents = File.ReadAllText(certPemFilePath); + ReadOnlySpan keyContents = keyPemFilePath is null ? certContents : File.ReadAllText(keyPemFilePath); + + return CreateFromPem(certContents, keyContents); + } + + /// + /// Creates a new X509 certificate from the file contents of an RFC 7468 PEM-encoded + /// certificate and password protected private key. + /// + /// The path for the PEM-encoded X509 certificate. + /// + /// If specified, the path for the password protected PEM-encoded private key. + /// If unspecified, the file will be used to load + /// the private key. + /// + /// The password for the encrypted PEM. + /// A new certificate with the private key. + /// + /// + /// The contents of the file path in do not contain + /// a PEM-encoded certificate, or it is malformed. + /// + /// -or- + /// + /// The contents of the file path in do not contain a + /// password protected PEM-encoded private key, or it is malformed. + /// + /// -or- + /// + /// The contents of the file path in contains + /// a key that does not match the public key in the certificate. + /// + /// + /// + /// The path specified in is null. + /// + /// + /// The certificate uses an unknown public key algorithm. + /// -or- + /// The password specified for the private key is incorrect. + /// + /// + /// + /// See for additional documentation about + /// exceptions that can be thrown. + /// + /// + /// Password protected PEM-encoded keys are always expected to have the PEM label "ENCRYPTED PRIVATE KEY". + /// + /// PEM-encoded items that have a different label are ignored. + /// + /// Combined PEM-encoded certificates and keys do not require a specific order. For the certificate, the + /// the first certificate with a CERTIFICATE label is loaded. For the private key, the first private + /// key with the label "ENCRYPTED PRIVATE KEY" is loaded. More advanced scenarios for loading certificates and + /// private keys can leverage to enumerate + /// PEM-encoded values and apply any custom loading behavior. + /// + /// + /// For PEM-encoded keys without a password, use . + /// + /// + public static X509Certificate2 CreateFromEncryptedPemFile(string certPemFilePath, ReadOnlySpan password, string? keyPemFilePath = default) + { + if (certPemFilePath is null) + throw new ArgumentNullException(nameof(certPemFilePath)); + + ReadOnlySpan certContents = File.ReadAllText(certPemFilePath); + ReadOnlySpan keyContents = keyPemFilePath is null ? certContents : File.ReadAllText(keyPemFilePath); + + return CreateFromEncryptedPem(certContents, keyContents, password); + } + + /// + /// Creates a new X509 certificate from the contents of an RFC 7468 PEM-encoded certificate and private key. + /// + /// The text of the PEM-encoded X509 certificate. + /// The text of the PEM-encoded private key. + /// A new certificate with the private key. + /// + /// The contents of do not contain a PEM-encoded certificate, or it is malformed. + /// -or- + /// The contents of do not contain a PEM-encoded private key, or it is malformed. + /// -or- + /// The contents of contains a key that does not match the public key in the certificate. + /// + /// + /// The certificate uses an unknown public key algorithm. + /// + /// + /// + /// The SubjectPublicKeyInfo from the certificate determines what PEM labels are accepted for the private key. + /// For RSA certificates, accepted private key PEM labels are "RSA PRIVATE KEY" and "PRIVATE KEY". + /// For ECDSA certificates, accepted private key PEM labels are "EC PRIVATE KEY" and "PRIVATE KEY". + /// For DSA certificates, the accepted private key PEM label is "PRIVATE KEY". + /// + /// PEM-encoded items that have a different label are ignored. + /// + /// If the PEM-encoded certificate and private key are in the same text, use the same + /// string for both and , such as: + /// + /// CreateFromPem(combinedCertAndKey, combinedCertAndKey); + /// + /// Combined PEM-encoded certificates and keys do not require a specific order. For the certificate, the + /// the first certificate with a CERTIFICATE label is loaded. For the private key, the first private + /// key with an acceptable label is loaded. More advanced scenarios for loading certificates and + /// private keys can leverage to enumerate + /// PEM-encoded values and apply any custom loading behavior. + /// + /// + /// For password protected PEM-encoded keys, use to specify a password. + /// + /// + public static X509Certificate2 CreateFromPem(ReadOnlySpan certPem, ReadOnlySpan keyPem) + { + using (X509Certificate2 certificate = ExtractCertificateFromPem(certPem)) + { + string keyAlgorithm = certificate.GetKeyAlgorithm(); + + switch (keyAlgorithm) + { + case Oids.Rsa: + using (RSA key = ExtractKeyFromPem(keyPem, s_RsaPublicKeyPrivateKeyLabels, RSA.Create)) + { + return certificate.CopyWithPrivateKey(key); + } + case Oids.EcPublicKey: + using (ECDsa key = ExtractKeyFromPem(keyPem, s_EcPublicKeyPrivateKeyLabels, ECDsa.Create)) + { + return certificate.CopyWithPrivateKey(key); + } + case Oids.Dsa: + using (DSA key = ExtractKeyFromPem(keyPem, s_DsaPublicKeyPrivateKeyLabels, DSA.Create)) + { + return certificate.CopyWithPrivateKey(key); + } + default: + throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, keyAlgorithm)); + } + } + } + + /// + /// Creates a new X509 certificate from the contents of an RFC 7468 PEM-encoded + /// certificate and password protected private key. + /// + /// The text of the PEM-encoded X509 certificate. + /// The text of the password protected PEM-encoded private key. + /// The password for the encrypted PEM. + /// A new certificate with the private key. + /// + /// The contents of do not contain a PEM-encoded certificate, or it is malformed. + /// -or- + /// + /// The contents of do not contain a password protected PEM-encoded private key, + /// or it is malformed. + /// + /// -or- + /// The contents of contains a key that does not match the public key in the certificate. + /// + /// + /// The certificate uses an unknown public key algorithm. + /// -or- + /// The password specified for the private key is incorrect. + /// + /// + /// + /// Password protected PEM-encoded keys are always expected to have the PEM label "ENCRYPTED PRIVATE KEY". + /// + /// PEM-encoded items that have a different label are ignored. + /// + /// If the PEM-encoded certificate and private key are in the same text, use the same + /// string for both and , such as: + /// + /// CreateFromEncryptedPem(combinedCertAndKey, combinedCertAndKey, theKeyPassword); + /// + /// Combined PEM-encoded certificates and keys do not require a specific order. For the certificate, the + /// the first certificate with a CERTIFICATE label is loaded. For the private key, the first private + /// key with the label "ENCRYPTED PRIVATE KEY" is loaded. More advanced scenarios for loading certificates and + /// private keys can leverage to enumerate + /// PEM-encoded values and apply any custom loading behavior. + /// + /// + /// For PEM-encoded keys without a password, use . + /// + /// + public static X509Certificate2 CreateFromEncryptedPem(ReadOnlySpan certPem, ReadOnlySpan keyPem, ReadOnlySpan password) + { + using (X509Certificate2 certificate = ExtractCertificateFromPem(certPem)) + { + string keyAlgorithm = certificate.GetKeyAlgorithm(); + + switch (keyAlgorithm) + { + case Oids.Rsa: + using (RSA key = ExtractKeyFromEncryptedPem(keyPem, password, RSA.Create)) + { + return certificate.CopyWithPrivateKey(key); + } + case Oids.EcPublicKey: + using (ECDsa key = ExtractKeyFromEncryptedPem(keyPem, password, ECDsa.Create)) + { + return certificate.CopyWithPrivateKey(key); + } + case Oids.Dsa: + using (DSA key = ExtractKeyFromEncryptedPem(keyPem, password, DSA.Create)) + { + return certificate.CopyWithPrivateKey(key); + } + default: + throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, keyAlgorithm)); + } + } + } + + private static X509Certificate2 ExtractCertificateFromPem(ReadOnlySpan certPem) + { + foreach ((ReadOnlySpan contents, PemFields fields) in new PemEnumerator(certPem)) + { + ReadOnlySpan label = contents[fields.Label]; + + if (label.SequenceEqual(PemLabels.X509Certificate)) + { + // We verify below that every byte is written to. + byte[] certBytes = GC.AllocateUninitializedArray(fields.DecodedDataLength); + + if (!Convert.TryFromBase64Chars(contents[fields.Base64Data], certBytes, out int bytesWritten) + || bytesWritten != fields.DecodedDataLength) + { + // The contents should have already been validated by the PEM reader. + throw new ArgumentException(SR.Cryptography_X509_NoPemCertificate, nameof(certPem)); + } + + return new X509Certificate2(certBytes); + } + } + + throw new ArgumentException(SR.Cryptography_X509_NoPemCertificate, nameof(certPem)); + } + + private static TAlg ExtractKeyFromPem(ReadOnlySpan keyPem, string[] labels, Func factory) where TAlg : AsymmetricAlgorithm + { + foreach ((ReadOnlySpan contents, PemFields fields) in new PemEnumerator(keyPem)) + { + ReadOnlySpan label = contents[fields.Label]; + + foreach (string eligibleLabel in labels) + { + if (label.SequenceEqual(eligibleLabel)) + { + TAlg key = factory(); + key.ImportFromPem(contents[fields.Location]); + return key; + } + } + } + + throw new ArgumentException(SR.Cryptography_X509_NoOrMismatchedPemKey, nameof(keyPem)); + } + + private static TAlg ExtractKeyFromEncryptedPem( + ReadOnlySpan keyPem, + ReadOnlySpan password, + Func factory) where TAlg : AsymmetricAlgorithm + { + foreach ((ReadOnlySpan contents, PemFields fields) in new PemEnumerator(keyPem)) + { + ReadOnlySpan label = contents[fields.Label]; + + if (label.SequenceEqual(PemLabels.EncryptedPkcs8PrivateKey)) + { + TAlg key = factory(); + key.ImportFromEncryptedPem(contents[fields.Location], password); + return key; + } + } + + throw new ArgumentException(SR.Cryptography_X509_NoOrMismatchedPemKey, nameof(keyPem)); + } + private static X509Extension? CreateCustomExtensionIfAny(Oid oid) => oid.Value switch { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs index 101bfdd79d027e..0fc7c3a9cbed45 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Internal.Cryptography; using Internal.Cryptography.Pal; using Microsoft.Win32.SafeHandles; @@ -229,5 +230,94 @@ public void RemoveRange(X509Certificate2Collection certificates) throw; } } + + /// + /// Imports a collection of RFC 7468 PEM-encoded certificates. + /// + /// The path for the PEM-encoded X509 certificate collection. + /// + /// + /// See for additional documentation about + /// exceptions that can be thrown. + /// + /// + /// PEM-encoded items with a CERTIFICATE PEM label will be imported. PEM items + /// with other labels will be ignored. + /// + /// + /// More advanced scenarios for loading certificates and + /// can leverage to enumerate + /// PEM-encoded values and apply any custom loading behavior. + /// + /// + /// + /// The decoded contents of a PEM are invalid or corrupt and could not be imported. + /// + /// + /// is null. + /// + public void ImportFromPemFile(string certPemFilePath) + { + if (certPemFilePath is null) + throw new ArgumentNullException(nameof(certPemFilePath)); + + ReadOnlySpan contents = System.IO.File.ReadAllText(certPemFilePath); + ImportFromPem(contents); + } + + /// + /// Imports a collection of RFC 7468 PEM-encoded certificates. + /// + /// The text of the PEM-encoded X509 certificate collection. + /// + /// + /// PEM-encoded items with a CERTIFICATE PEM label will be imported. PEM items + /// with other labels will be ignored. + /// + /// + /// More advanced scenarios for loading certificates and + /// can leverage to enumerate + /// PEM-encoded values and apply any custom loading behavior. + /// + /// + /// + /// The decoded contents of a PEM are invalid or corrupt and could not be imported. + /// + public void ImportFromPem(ReadOnlySpan certPem) + { + int added = 0; + + try + { + foreach ((ReadOnlySpan contents, PemFields fields) in new PemEnumerator(certPem)) + { + ReadOnlySpan label = contents[fields.Label]; + + if (label.SequenceEqual(PemLabels.X509Certificate)) + { + // We verify below that every byte is written to. + byte[] certBytes = GC.AllocateUninitializedArray(fields.DecodedDataLength); + + if (!Convert.TryFromBase64Chars(contents[fields.Base64Data], certBytes, out int bytesWritten) + || bytesWritten != fields.DecodedDataLength) + { + // The contents should have already been validated by the PEM reader. + throw new ArgumentException(SR.Cryptography_X509_NoPemCertificate, nameof(certPem)); + } + + Import(certBytes); + added++; + } + } + } + catch + { + for (int i = 0; i < added; i++) + { + RemoveAt(Count - 1); + } + throw; + } + } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index d280c9b53c941d..de0f5bc1bc8ad4 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -6,6 +6,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Text; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests @@ -1426,6 +1427,55 @@ public static void SerializedCertDisposeDoesNotRemoveKeyFile() } } + [Fact] + public static void ImportFromPem_SingleCertificate_Success() + { + X509Certificate2Collection cc = new X509Certificate2Collection(); + cc.ImportFromPem(PemTestData.ECDsaCertificate); + Assert.Single(cc); + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cc[0].Thumbprint); + } + + [Fact] + public static void ImportFromPem_SingleCertificate_IgnoresUnrelatedPems_Success() + { + string pemAggregate = PemTestData.ECDsaPkcs8Key + PemTestData.ECDsaCertificate; + X509Certificate2Collection cc = new X509Certificate2Collection(); + cc.ImportFromPem(pemAggregate); + Assert.Single(cc); + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cc[0].Thumbprint); + } + + [Fact] + public static void ImportFromPem_MultiplePems_Success() + { + string pemAggregate = PemTestData.RsaCertificate + PemTestData.ECDsaCertificate; + X509Certificate2Collection cc = new X509Certificate2Collection(); + cc.ImportFromPem(pemAggregate); + Assert.Equal(2, cc.Count); + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cc[0].Thumbprint); + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cc[1].Thumbprint); + } + + [Fact] + public static void ImportFromPem_Exception_AllOrNothing() + { + X509Certificate2Collection cc = new X509Certificate2Collection(); + cc.ImportFromPem(PemTestData.DsaCertificate); + + StringBuilder builder = new StringBuilder(); + builder.AppendLine(PemTestData.RsaCertificate); + builder.AppendLine(@" +-----BEGIN CERTIFICATE----- +MIII +-----END CERTIFICATE-----"); + builder.AppendLine(PemTestData.ECDsaCertificate); + + Assert.ThrowsAny(() => cc.ImportFromPem(builder.ToString())); + Assert.Single(cc); + Assert.Equal("35052C549E4E7805E4EA204C2BE7F4BC19B88EC8", cc[0].Thumbprint); + } + private static void TestExportSingleCert_SecureStringPassword(X509ContentType ct) { using (var pfxCer = new X509Certificate2(TestData.PfxData, TestData.CreatePfxDataPasswordSecureString(), Cert.EphemeralIfPossible)) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PemTestData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PemTestData.cs new file mode 100644 index 00000000000000..0307583cf43afe --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PemTestData.cs @@ -0,0 +1,275 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + internal static class PemTestData + { + public const string RsaCertificate = @" +-----BEGIN CERTIFICATE----- +MIIDNjCCAh4CCQCu2+oEr9yAxDANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJV +UzERMA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkFsZXhhbmRyaWExEDAOBgNV +BAoMB0NvbnRvc28xFDASBgNVBAsMC0VuZ2luZWVyaW5nMB4XDTIwMDYwODE3MDk1 +M1oXDTIwMDcwODE3MDk1M1owXTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdp +bmlhMRMwEQYDVQQHDApBbGV4YW5kcmlhMRAwDgYDVQQKDAdDb250b3NvMRQwEgYD +VQQLDAtFbmdpbmVlcmluZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AL/yBbH8MoxLuApvUhWMMV+5h+v15xY9or+TxyAkwwtP0mLKMWPeHZm2S4OT8JCn +fJmz7J+7J4Nf3VB0HYDN+41uEEwNX3Caps30Kt17dcJm3/sG9uBB7eiQdbj2HNSS +nzxYK7d2mKnWwZlHg0I7/ZR5TMM8eCdJpESsHRpF5dPGgdWFgUzbdi73Yyk5/PFM +lauHPM0d4TWHoWnY0yh7Y08PMY/4MH7HoMVt+mbpV2d3DRC8WS0jhU+mbCqce4St +BWTCmR+ObuHFdMSVx7o88MWmdteZXCX8N6ohPwAl9W02pOa5Vq37xwuUZ1VvT9G2 +ndi+VRRgN51HCmG1qB1LhtsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIBtwgcLn +7LT+gfqqDsaZcMjpsHMu2TamR9inoRdxwoJKnG7dIe8tWL+nGxsN38DjjcZhb3y0 +Ilqi0e7LYqb0QyXAuS+Su2uJjypgvNd4stj+4Pl1EfU1rpzed4CA1O0pap5m0U++ +2YmWNNBmSZxcUi2ge09ZqrKm78a7Vtrpy8bNcixb+szrPSUFWh7WOXBCABusZ/OY +MnzZBXtQtQzDCtJb6IxevxAGod1XxInXQaB+nDnG4MD3v4MZQYgTI76+cAPxS6nx +pzJ/tfFzq7OAGBkrxcdmzqb1/caPWINKzwbDAEuNX2yeThP8eVrbRvGciy7LNzvR +EA0/67lEfPmRow== +-----END CERTIFICATE-----"; + + public const string ECDsaCertificate = @" +-----BEGIN CERTIFICATE----- +MIICDzCCAbWgAwIBAgIUeohSwWS4OT31fY5xvUxTEv07e8IwCgYIKoZIzj0EAwIw +XTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRMwEQYDVQQHDApBbGV4 +YW5kcmlhMRAwDgYDVQQKDAdDb250b3NvMRQwEgYDVQQLDAtEZXZlbG9wbWVudDAe +Fw0yMDA2MDgxOTE4NDVaFw0yMTA2MDgxOTE4NDVaMF0xCzAJBgNVBAYTAlVTMREw +DwYDVQQIDAhWaXJnaW5pYTETMBEGA1UEBwwKQWxleGFuZHJpYTEQMA4GA1UECgwH +Q29udG9zbzEUMBIGA1UECwwLRGV2ZWxvcG1lbnQwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAAQyB0wIKKfk5lmK4Z907qQnPsRRXh3TrU/VPMCTHxuBwoZqFBSE7gGm +JWLTnGwZ0MGMACP+N1HK4dU1S9VNoNw9o1MwUTAdBgNVHQ4EFgQUPzDLKQI9EfTa +rnMMO0/4p8rNwZgwHwYDVR0jBBgwFoAUPzDLKQI9EfTarnMMO0/4p8rNwZgwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiAwq18UD2DCufaMGTsz4JOQ +1vRqLI4hMLsIUQNyYgzbcgIhAJsB3qgv5WGyshlav98MPORcdCmYfkIvUCal0oPX +Wesb +-----END CERTIFICATE-----"; + + public const string ECDsaPkcs8Key = @" +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIg/wHlL4nNyuLFbK +00Ga7N5LbP/2YCgEXqBiB3el8syhRANCAAQyB0wIKKfk5lmK4Z907qQnPsRRXh3T +rU/VPMCTHxuBwoZqFBSE7gGmJWLTnGwZ0MGMACP+N1HK4dU1S9VNoNw9 +-----END PRIVATE KEY-----"; + + public const string ECDsaEncryptedPkcs8Key = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAh5RmzN7/AXZgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIQHgahqqSQKcEgZCvEBMgW8a7IXmT+weI +0mlM4AcTELDkE+SEKpYC5qVF4ZDyrw4OmnVLkSPiu0GUwgJIopazWOQfetMdgC5Q +n5pYHoRm9bek0s6TK9eoaTIA+M2T0MMNM0fWXWcYaT5B2/4Uv+mMEYgIRncFe1c1 +FEixDW6ObZIXVBbxl+zK1KwtCpdewXE4HRX/qpBrPhB8z2s= +-----END ENCRYPTED PRIVATE KEY-----"; + + public const string ECDsaECPrivateKey = @" +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICIP8B5S+JzcrixWytNBmuzeS2z/9mAoBF6gYgd3pfLMoAoGCCqGSM49 +AwEHoUQDQgAEMgdMCCin5OZZiuGfdO6kJz7EUV4d061P1TzAkx8bgcKGahQUhO4B +piVi05xsGdDBjAAj/jdRyuHVNUvVTaDcPQ== +-----END EC PRIVATE KEY-----"; + + public const string RsaPkcs1Key = @" +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAv/IFsfwyjEu4Cm9SFYwxX7mH6/XnFj2iv5PHICTDC0/SYsox +Y94dmbZLg5PwkKd8mbPsn7sng1/dUHQdgM37jW4QTA1fcJqmzfQq3Xt1wmbf+wb2 +4EHt6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9lHlMwzx4J0mkRKwdGkXl08aB1YWB +TNt2LvdjKTn88UyVq4c8zR3hNYehadjTKHtjTw8xj/gwfsegxW36ZulXZ3cNELxZ +LSOFT6ZsKpx7hK0FZMKZH45u4cV0xJXHujzwxaZ215lcJfw3qiE/ACX1bTak5rlW +rfvHC5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG2wIDAQABAoIBAEavE5XVr6+meqGt +GOdCdzQvGHS2W2D/VZ2DCAM4RnM1893ZY5LJStE+JlTP9/jtFJ9teKfhvc1NUiy8 +ddjnAcm1TF8VVZ4b9W1GizqAqn7qb3T7vZIb9UZ9XDy+tSM601Tfi0nGbLWuliCi +Cx4rBVjVyoTjEcQ2BD4du6HfN6FihzCu+DV6Dsm4Ar6orWRCYJ0v1doMYdGzOaoJ +SwcWCtAYkJ+krfYz37xlrFJfppMgYy67q040XBV7PuyjkGtt1GcNO98pw0puiCAs +VP+u+vnngNCjSqXlVSAZL1ISJylUxKkNRynABNBTfdGeRWCuhwc/M0vuEeX8A5Ce +NlDA1MECgYEA5iGjpQkyeXcsSP6zqE4LhA0L20YoCsnYnyout/Hn4C4AtUH3Yt2o +MvpRne7JxmGxmhSJkud+z6LfbaAKLU/s86wslOeclwlMkFz+CK75AFCDNecNgFCR +jENNBFIou329+/Rx7+fbGsgHfMwz2Cdv98hKCOmX3W3pZRCW+gVLMtECgYEA1YWG +q1v4M2QdB3f8hKiElicvlyNwVW7rHgbcsUiDPdGSLg/fREZeA3C4bnV+Mi8LlRDJ ++PoZyqML+xWhQDGWK+7r0iWuvFPsbCwk0BRTCYue0p7pCsUEjS0vVQPk0O1fLlOH +UdLtyyuYegsAtn5XSMN7+LeM0zyP+OWVfqWkEesCgYBSiYQYv+izedOPRpKG7Z7h +uJAlD89ytxwTUdy5qnBAjh9A4yzn75nQ1siI/Uiu9wDswyroXlC0BbVeqwSbZcwV +RQ4kRcF6xiIIsOGHmcHCpB27KmhEOiFJjiXEQ/dJ73pBMFXg9mY1/8H3t4FsqBBX +bSVoduc5yp7n2YBcoaNr0QKBgCPloPBqM94f9KluyKtc0X2U9PFJ6fbTAQA5Ux0S +/c2E0DiiPnzx/5hAeSFI64BwXFghTHNpSLDCnJ8H0eZC7+ZO8qKP50KOMW82NLIu +2I8ARCFQygkfelZpxE1crDlbzuaw8E0XUxcXKzlJZENKFk6LXuo/oZNZ2TKVFn8G +RgElAoGAMsD758wEk2bVTJFNISizHLHQ4yyCYgJSjmUqtP/GqK0qvo4Kz5TSm/2D +H7b+ao0a48b+i4EyuDTVT/CeJ3RoQzXvayc9WuipV/ewRBYRsEVzZwFO6D4aD+jI +a+7OpVqFLMtAySKTg0vPwcRF4OxQ/4YBE1e+ZGnx+vVTpkE3gjw= +-----END RSA PRIVATE KEY-----"; + + public const string RsaPkcs1PublicKey = @" + -----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEAv/IFsfwyjEu4Cm9SFYwxX7mH6/XnFj2iv5PHICTDC0/SYsoxY94d +mbZLg5PwkKd8mbPsn7sng1/dUHQdgM37jW4QTA1fcJqmzfQq3Xt1wmbf+wb24EHt +6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9lHlMwzx4J0mkRKwdGkXl08aB1YWBTNt2 +LvdjKTn88UyVq4c8zR3hNYehadjTKHtjTw8xj/gwfsegxW36ZulXZ3cNELxZLSOF +T6ZsKpx7hK0FZMKZH45u4cV0xJXHujzwxaZ215lcJfw3qiE/ACX1bTak5rlWrfvH +C5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG2wIDAQAB +-----END RSA PUBLIC KEY-----"; + + public const string RsaPkcs8PublicKey = @" +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/IFsfwyjEu4Cm9SFYwx +X7mH6/XnFj2iv5PHICTDC0/SYsoxY94dmbZLg5PwkKd8mbPsn7sng1/dUHQdgM37 +jW4QTA1fcJqmzfQq3Xt1wmbf+wb24EHt6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9 +lHlMwzx4J0mkRKwdGkXl08aB1YWBTNt2LvdjKTn88UyVq4c8zR3hNYehadjTKHtj +Tw8xj/gwfsegxW36ZulXZ3cNELxZLSOFT6ZsKpx7hK0FZMKZH45u4cV0xJXHujzw +xaZ215lcJfw3qiE/ACX1bTak5rlWrfvHC5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG +2wIDAQAB +-----END PUBLIC KEY-----"; + + public const string RsaPkcs8Key = @" +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/8gWx/DKMS7gK +b1IVjDFfuYfr9ecWPaK/k8cgJMMLT9JiyjFj3h2ZtkuDk/CQp3yZs+yfuyeDX91Q +dB2AzfuNbhBMDV9wmqbN9Crde3XCZt/7BvbgQe3okHW49hzUkp88WCu3dpip1sGZ +R4NCO/2UeUzDPHgnSaRErB0aReXTxoHVhYFM23Yu92MpOfzxTJWrhzzNHeE1h6Fp +2NMoe2NPDzGP+DB+x6DFbfpm6Vdndw0QvFktI4VPpmwqnHuErQVkwpkfjm7hxXTE +lce6PPDFpnbXmVwl/DeqIT8AJfVtNqTmuVat+8cLlGdVb0/Rtp3YvlUUYDedRwph +tagdS4bbAgMBAAECggEARq8TldWvr6Z6oa0Y50J3NC8YdLZbYP9VnYMIAzhGczXz +3dljkslK0T4mVM/3+O0Un214p+G9zU1SLLx12OcBybVMXxVVnhv1bUaLOoCqfupv +dPu9khv1Rn1cPL61IzrTVN+LScZsta6WIKILHisFWNXKhOMRxDYEPh27od83oWKH +MK74NXoOybgCvqitZEJgnS/V2gxh0bM5qglLBxYK0BiQn6St9jPfvGWsUl+mkyBj +LrurTjRcFXs+7KOQa23UZw073ynDSm6IICxU/676+eeA0KNKpeVVIBkvUhInKVTE +qQ1HKcAE0FN90Z5FYK6HBz8zS+4R5fwDkJ42UMDUwQKBgQDmIaOlCTJ5dyxI/rOo +TguEDQvbRigKydifKi638efgLgC1Qfdi3agy+lGd7snGYbGaFImS537Pot9toAot +T+zzrCyU55yXCUyQXP4IrvkAUIM15w2AUJGMQ00EUii7fb379HHv59sayAd8zDPY +J2/3yEoI6ZfdbellEJb6BUsy0QKBgQDVhYarW/gzZB0Hd/yEqISWJy+XI3BVbuse +BtyxSIM90ZIuD99ERl4DcLhudX4yLwuVEMn4+hnKowv7FaFAMZYr7uvSJa68U+xs +LCTQFFMJi57SnukKxQSNLS9VA+TQ7V8uU4dR0u3LK5h6CwC2fldIw3v4t4zTPI/4 +5ZV+paQR6wKBgFKJhBi/6LN5049GkobtnuG4kCUPz3K3HBNR3LmqcECOH0DjLOfv +mdDWyIj9SK73AOzDKuheULQFtV6rBJtlzBVFDiRFwXrGIgiw4YeZwcKkHbsqaEQ6 +IUmOJcRD90nvekEwVeD2ZjX/wfe3gWyoEFdtJWh25znKnufZgFyho2vRAoGAI+Wg +8Goz3h/0qW7Iq1zRfZT08Unp9tMBADlTHRL9zYTQOKI+fPH/mEB5IUjrgHBcWCFM +c2lIsMKcnwfR5kLv5k7yoo/nQo4xbzY0si7YjwBEIVDKCR96VmnETVysOVvO5rDw +TRdTFxcrOUlkQ0oWTote6j+hk1nZMpUWfwZGASUCgYAywPvnzASTZtVMkU0hKLMc +sdDjLIJiAlKOZSq0/8aorSq+jgrPlNKb/YMftv5qjRrjxv6LgTK4NNVP8J4ndGhD +Ne9rJz1a6KlX97BEFhGwRXNnAU7oPhoP6Mhr7s6lWoUsy0DJIpODS8/BxEXg7FD/ +hgETV75kafH69VOmQTeCPA== +-----END PRIVATE KEY-----"; + + // password is `test`. + public const string RsaEncryptedPkcs8Key = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFGTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIozMG90LwJXECAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECK51Ahgtvr/LBIIEyO38HrNbUgAI95h6 +JolbqSkwpPeDfQsxmRlWsqtlin8K63U5gCZS/I1nkkkC13xXHB6WOQ0DrYIGv5pk +ceKiGYNki5UMxw7voXBw2F4pih07iBiATW0uKQnlNxdxbaxH4AEVyxaYTmfI6een +cPyVpgjk8BqZWIrzrcCNq0MfSkI7X1u2VRpni79xETMkdBDeNA5KqwwmeDs8EpRR +laWj2u/s+fpYkR0G1HgrJswMX38Ybp5GBu9i+JJUleDY/ByFRwaEmaMyArdhcI5V +Fp5Bl4vFFsrB0gXf8tApKN3Bmhou2gpO5VbRp3j1iHU27JCa8yBr9KOyrZ8UZrIz +bBcwjHTDOUOp1hwRVu1RUij+b33Bf4IPSobesGovIUgIe0iV+luOZGgitITb5Q4Q +Rup+q4v/M6E2JLry2zDz/FHOmtk83Og+lp9kczHjpGbj7OrGqOyT8YGqpLbmgD6q +vsuus8nCTEpAWG0rmI3RMsOR/nHdQkhgRb8U74vMIFhKfupUVAhLRvB9reKx44p0 +k5TUS+YYxh2LdZLquUeYcjMKFbRjSnzO/6N5vo8/Y/AV646bTSzrySmNaOUYArvl +A4l/GMZOFjODfjj0/p2AGE4q5kC9cKmPS99TfqD+l4IBncw9TD/tzi+c/+R+WSy9 +evT2cwQE26byo7703B7mn5Y1e0068gitPUhx5mudEloVN8Hg7N+opZpMWLrCAMEx +6kyXbWSdnrSLqv/LTSKXq2WoBk1tJ+/vfsOYdvXHU7oSje8ZoCj70OHsk3f9q8eH +b6R/+hvOJBg4UTtljXJ2BpF8yq34GPLuINYtBuMh/wo7iKQzz9AxdlfSn/EaR8Bz +MnFJlQo76Ie0LSuiHMqMunF9z6TweHMQI37IocBqpOMDRo+jz+Q0AQTe/H8PhQk0 +tGN68tPCpgkuY4apjWcNv/6dxm9/ZpR0ZvbA2DXSnxK4p1BQUEeUX1bTrb5DKL6a +KqRPokfoUbdyikGc80+TOk9SJ95if1+2XwT8gN+NLSagCRnJPWdZaOQqInYcaaZ+ +fXWPymGWEUjfIa9L71YMTOaVJ73oooC0mGFBKxMlGNamhRSqXUnX8OMJ5Zzflajw +HkgsooaGaL0CjHn7h1zFdctHyJTjRLamKe4f0drBcREz9U3sU5DQ31+hScqtJp+C +k/MZgiRRJudfi2tjNFDa28iP9U0wygPZ0RvD9IXBpYZH7KroYMtT2ysK0q16EJc3 +UqhwwEpCj6rPcVktEh/sYmeS6HL19VL03hwdxkKdmQ/7PjI8/vCy96AeIxgpBEJm +ziWCZffAXkbYwSNL2YIHronuvyt4sfjCctnoQUIBoCOQIwPF6yfNKbpL3ihPg55I +EsaecMRl/FDCbew/NqXY1ujA/ntpSMCcE4SkKKIGmzinlINw3BdfLU8bVzlPknCY +3W5ZH9Z5FV4SsEXmrRWFcAKb5kd1hvEl7XtOWpRvOtDbk9WrCko/xY2fgDXyYm25 +fl2Y9C69W24gloqGX1EOOCv1sOnUBaOoS0QnGaTPoo7aos+TNah6vB3qjylwxFFE +YTdGR9lY5wKHcJeFATIIcQyL+cziHNJQCwM839Q3uc0Sj6JvVm0sCHd/VUgzhfzX +16v5sI3D+15h+GTFjA== +-----END ENCRYPTED PRIVATE KEY-----"; + + public const string Ed25519Certificate = @" +-----BEGIN CERTIFICATE----- +MIICFzCCAcmgAwIBAgIUXX/A8D+9ewGf98c9FjYwAAhRIsswBQYDK2VwMIGAMQsw +CQYDVQQGEwJVUzEcMBoGA1UECAwTRGlzdGljdCBvZiBDb2x1bWJpYTETMBEGA1UE +BwwKV2FzaGluZ3RvbjEUMBIGA1UECgwLRGV2ZWxvcG1lbnQxFDASBgNVBAsMC0Rl +dmVsb3BtZW50MRIwEAYDVQQDDAlUZXN0IENlcnQwHhcNMjAwNDI4MTUwMjMwWhcN +MjEwNDI4MTUwMjMwWjCBgDELMAkGA1UEBhMCVVMxHDAaBgNVBAgME0Rpc3RpY3Qg +b2YgQ29sdW1iaWExEzARBgNVBAcMCldhc2hpbmd0b24xFDASBgNVBAoMC0RldmVs +b3BtZW50MRQwEgYDVQQLDAtEZXZlbG9wbWVudDESMBAGA1UEAwwJVGVzdCBDZXJ0 +MCowBQYDK2VwAyEAJ0WQ+SKMG8q3g9alCNzq0t3vjAeN295rq6IdaFj0NmSjUzBR +MB0GA1UdDgQWBBTUdW0AAtdSTqb1Oc6J2Pfvsk4bpjAfBgNVHSMEGDAWgBTUdW0A +AtdSTqb1Oc6J2Pfvsk4bpjAPBgNVHRMBAf8EBTADAQH/MAUGAytlcANBAOjstGTq +0fmzySS+DjSNfvkB9IXmaGd3XGIM8SWTKCnFi7L+mXhDBgd9wLO3H+EulBiuKtIo +mf0l9zwCygC+aA8= +-----END CERTIFICATE-----"; + + + public const string OtherRsaPkcs1Key = @" +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA05yPUFo642M7gUYBYZgp9Itk/yPcrLcisUImPvQAsrrCeQDR +kKxo1rNApfEZAK5UffEL7kbYFRY7v0mhwWY4K2g17FJ5Bd7W54Sj5ziDzk5LKvvN +dxjZIIDi69z+AZYKcPWu4D6Znbi8isDeWv8mUTeFcA8Ine4YuvsAJ6b6IHGuz0Kr +qCZeE6319wjA6Bh14UziIPxOm1LLJ8Vlwq7dtngk034kUyliO9kWJg7aNatthn5U +q3nSUUCarhjfMFu0GhBy0TFRfsQmKNLOBMtrtx058aVbzeR3XO6pYhVsdF1cwhoP +SXPCYpgc9ZslH9u9RjQoP/0+vRuT3jBfHFKtvQIDAQABAoIBADufE93+3jKtBdoB +gGgf+Eo3cChW0Vk0bCjnS2FXXE7/QcXYDjOl8A/2B1P53yKK+7FUVhk3irA+SG03 +8MRN6auJPBAumHyn8YLfo1KFYNWix5j/wz84GA0JY2YzKLoHrT9waWozGRMQNscd +WkjnNMOTUhxlj/b3W65rA/soF7dF7Vohb4Ab251apJi/k/IBkv6gVUUH6adhPSxG +piEY4AZ0Vpngf14nI5gesStFLBhJqBtHbMeicdQbHJzg9OjNpQzNGCQzCx7EIteM +LJ/DZ+r6S9qm/m5qvgxALc0SMTi58RgIf0JwWpTrUuEEOfO0qgjKZoi8As8yFR27 +cU0n+MECgYEA7m5U81M+d/nV116Ln1+s9t0NbaR0Vzk68WsHok54lSUTItcsuCxo +CpCp7B0ARi8KzG3W9Tv+NPS64JZ7iYj/ST6w02IjgpbldpJXmPl4wV57uUOPsTq8 +hReeGEcXGN5wYTCo6jiWNmiLR4CNScmFjxcjwcXSGwac5+Sn/MttT5ECgYEA4zRR +JPaDqluVhGhu+uK5Jhb1tqeTlruSxorRuN2GAApaf354+SdfQffD73zqCpXDdtbq +uT7/Qu6tniR1bEFpLKSGeH/ayLYNfxTy9m9aC6ccxTmEhbDSntY/HHo50CtWJOWz +F5O4R6aeE5rqWfHiB+Ya9dDhdVPor5wUyPUOfW0CgYBMGw2kqaKb+zRFzZj1oz17 +gu3BXKgCG3N0Efza0v9sY+wqx9Iva4U+MhT7F/q5bFSfEkR1/NNUpfVssLv4F7Gc ++JTKtF2vVmkiIu4xFxhzaKxHY4hfQudf+DzvdOmrd30ZmMWiFbPk5BPpG+B9eATY +usMgG/vHwqGc54CzkV9v8QKBgHlveP9ckrr3AE8o0khd7b+h/eqGXqft0WE0ySsZ +m4lh/0StgFMK7CsCFkNmbGED8tkNvZ8NQLmxgDJKIkieHWyy8vxsua8VPtlxhPqa +QXKA+yuetmoOPESRFmJOIaBVyVEnRDWRyqjhMRQhdKhmU/0My9QetKJVGsThk2pl +MD1xAoGBANrkobUHP4sUIGenNfWld1As4tmKXRDTv2HHVKOCvIfF8XY5jMQBf96W +Tx7VNbqFa2IEKZW07Q3TmLgD4cplE+XxIKGRGIie3PECiQdO+EieEtPe+nFLDUrb +w4qGwpDllP58gBySGj3BQS8PG8d9fuOlCSfOQIW10dE8U9H0wwlE +-----END RSA PRIVATE KEY-----"; + + public const string DsaCertificate = @" +-----BEGIN CERTIFICATE----- +MIIDWTCCAxWgAwIBAgIUFRQGA90GHC74cNK/hNzQDi7XJFYwCwYJYIZIAWUDBAMC +MF0xCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhWaXJnaW5pYTETMBEGA1UEBwwKQWxl +eGFuZHJpYTEQMA4GA1UECgwHQ29udG9zbzEUMBIGA1UECwwLRGV2ZWxvcG1lbnQw +HhcNMjAwNjE5MTkyODIwWhcNMjAwNzE5MTkyODIwWjBdMQswCQYDVQQGEwJVUzER +MA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkFsZXhhbmRyaWExEDAOBgNVBAoM +B0NvbnRvc28xFDASBgNVBAsMC0RldmVsb3BtZW50MIIBtjCCASsGByqGSM44BAEw +ggEeAoGBAJyiyioeXx1O98gRCMEjlPKMpr79KrcDkoroghtuXO1U6Cx34pBRjOQm +QLDPqSOriEo5VuG6SJc/ppfZx9TrSrzqB26hKTUmiaOKmwpfIfzpi72wgsZeMOtU +7JQ+FThfGyS8VxGh6G0h7xw26B/9ALxRw25zO1cy9ZJs0EY3hsHzAhUA/4dpclsc +k8K+SkWBTcPfU+x7wTUCgYB4LP6UvrvIiiFPxhk7AEGMMr0MhcJ3hhsgKWukUqIY +sJKBM5MpKCnej5BHvnLXdKodIxygcKR4dJX7BRv69L+2RJk+UrYL1qBco5HpUslu +mA0e3gNdwRLoOoGD14dn1LD1LdESsyMgwfHHJ0RRkYwacgCVXsvHv/eAkA8qq136 +dwOBhAACgYAHltgzkK3zD8yGdcGY0YgvN5l3lna1voLmcK+XtmehjMVy7OSSFICN +KybLBOvO8paydhCb1J0klkLPAoAjgP2cEd+KueeRyJpx+jD1MsjIEXIn5jtjXdUH +d0JJmHWAyHdNzmhXrXC7JLnj4ri7xMAV3GZGDpAnYvvL0LiXzFyomqNTMFEwHQYD +VR0OBBYEFF1l4ZrF3ND05CjGd//ev0dJLCB7MB8GA1UdIwQYMBaAFF1l4ZrF3ND0 +5CjGd//ev0dJLCB7MA8GA1UdEwEB/wQFMAMBAf8wCwYJYIZIAWUDBAMCAzEAMC4C +FQD6plYf60MDCvMjf1yQ8SBaFX3YYwIVAKqRQklh2b0Qhv+US222hb8xySJV +-----END CERTIFICATE-----"; + + public const string DsaPkcs8Key = @" +-----BEGIN PRIVATE KEY----- +MIIBSwIBADCCASsGByqGSM44BAEwggEeAoGBAJyiyioeXx1O98gRCMEjlPKMpr79 +KrcDkoroghtuXO1U6Cx34pBRjOQmQLDPqSOriEo5VuG6SJc/ppfZx9TrSrzqB26h +KTUmiaOKmwpfIfzpi72wgsZeMOtU7JQ+FThfGyS8VxGh6G0h7xw26B/9ALxRw25z +O1cy9ZJs0EY3hsHzAhUA/4dpclsck8K+SkWBTcPfU+x7wTUCgYB4LP6UvrvIiiFP +xhk7AEGMMr0MhcJ3hhsgKWukUqIYsJKBM5MpKCnej5BHvnLXdKodIxygcKR4dJX7 +BRv69L+2RJk+UrYL1qBco5HpUslumA0e3gNdwRLoOoGD14dn1LD1LdESsyMgwfHH +J0RRkYwacgCVXsvHv/eAkA8qq136dwQXAhUA216Tqp4OvdUBNv8QLv8Z5QPopGQ= +-----END PRIVATE KEY-----"; + + public const string DsaEncryptedPkcs8Key = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBoTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQI+PhdT1Kk/SkCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECGV1ZmaiQtz2BIIBUA/6pNqTkXpkOLlI +22Lh0cm5+/foDRh3qTrAOSHHHV0Dz1xYvYMa9MFzONatLf55Rpb2ZPji3hXwUQfn +gOJeTBRTaMNz5LaKJiOIWj0qDckhgKt9cmgiBzVTvXO4pERp1uz5zcvaUOKj2TSv +ljxishj76MYQftIGMMkJQKf4OsHubCopuKUbzTPgJt0FuF4eT37+tiEMgbYrmA6p +REPE0vT1aY+LYdJLV/Dax/l4lMvYmQYOWs9TCLPlI5RZQxxte6zbcA13ESg/qLE3 +4Mx8xgXrPvCxp3h8KBKNMaJR1xzpr7UQOpkI9qja++3cJAl6O/0mdeqZct0V9Z8P +a3+wyUWo58z5sOPNdJHIMV6qw6m3w+IQoCJC7EbV0+Pyo5eSU5zbgm7YWZ9Yx6l8 +g1mCP4Q6Tqe6LjKfBsZAmYWSfKqoTKRjC3ocJMt53tIDpB5jFw== +-----END ENCRYPTED PRIVATE KEY-----"; + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj index be18ed29c4ca65..4f5cd6437886a3 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj @@ -27,6 +27,7 @@ + @@ -37,6 +38,7 @@ + ("certPem", () => + X509Certificate2.CreateFromPem(default, default)); + } + + [Fact] + public static void CreateFromPem_ArgumentException_MalformedCertificate() + { + const string CertContents = @" +-----BEGIN CERTIFICATE----- +MII +-----END CERTIFICATE----- +"; + AssertExtensions.Throws("certPem", () => + X509Certificate2.CreateFromPem(CertContents, default)); + } + + [Fact] + public static void CreateFromPem_CryptographicException_InvalidKeyAlgorithm() + { + CryptographicException ce = Assert.Throws(() => + X509Certificate2.CreateFromPem(PemTestData.Ed25519Certificate, default)); + + Assert.Contains("'1.3.101.112'", ce.Message); + } + + [Fact] + public static void CreateFromPem_ArgumentException_NoKey() + { + AssertExtensions.Throws("keyPem", () => + X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, default)); + } + + [Fact] + public static void CreateFromPem_ArgumentException_MalformedKey() + { + const string CertContents = @" +-----BEGIN RSA PRIVATE KEY----- +MII +-----END RSA PRIVATE KEY----- +"; + AssertExtensions.Throws("keyPem", () => + X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, CertContents)); + } + + [Fact] + public static void CreateFromPem_Rsa_Pkcs1_Success() + { + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, PemTestData.RsaPkcs1Key)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Rsa_Pkcs8_Success() + { + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, PemTestData.RsaPkcs8Key)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaPkcs8Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Rsa_Aggregate_Pkcs8_Success() + { + string pemAggregate = PemTestData.RsaCertificate + PemTestData.RsaPkcs8Key; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(pemAggregate, pemAggregate)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaPkcs8Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Rsa_Aggregate_Pkcs1_Success() + { + string pemAggregate = PemTestData.RsaCertificate + PemTestData.RsaPkcs1Key; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(pemAggregate, pemAggregate)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Rsa_LoadsFirstCertificate_Success() + { + string certAggregate = PemTestData.RsaCertificate + PemTestData.ECDsaCertificate; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(certAggregate, PemTestData.RsaPkcs1Key)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Rsa_IgnoresNonMatchingAlgorithmKeys_Success() + { + string keyAggregate = PemTestData.ECDsaECPrivateKey + PemTestData.RsaPkcs1Key; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, keyAggregate)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Rsa_IgnoresPkcs1PublicKey_Success() + { + string keyAggregate = PemTestData.RsaPkcs1PublicKey + PemTestData.RsaPkcs1Key; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, keyAggregate)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Rsa_IgnoresPkcs8PublicKey_Success() + { + string keyAggregate = PemTestData.RsaPkcs8PublicKey + PemTestData.RsaPkcs1Key; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, keyAggregate)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Rsa_KeyMismatch_Fail() + { + AssertExtensions.Throws("privateKey", () => + X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, PemTestData.OtherRsaPkcs1Key)); + } + + [Fact] + public static void CreateFromEncryptedPem_Rsa_Success() + { + X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPem( + PemTestData.RsaCertificate, + PemTestData.RsaEncryptedPkcs8Key, + "test"); + + using (cert) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(PemTestData.RsaEncryptedPkcs8Key, cert.GetRSAPrivateKey, "test"); + } + } + + [Fact] + public static void CreateFromEncryptedPem_Rsa_InvalidPassword_Fail() + { + CryptographicException ce = Assert.Throws(() => + X509Certificate2.CreateFromEncryptedPem(PemTestData.RsaCertificate, PemTestData.RsaEncryptedPkcs8Key, "florp")); + + Assert.Contains("password may be incorrect", ce.Message); + } + + [Fact] + public static void CreateFromEncryptedPem_Rsa_IgnoresUnencryptedPem_Fail() + { + AssertExtensions.Throws("keyPem", () => + X509Certificate2.CreateFromEncryptedPem(PemTestData.RsaCertificate, PemTestData.RsaPkcs8Key, "test")); + } + + [Fact] + public static void CreateFromPem_ECDsa_ECPrivateKey_Success() + { + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.ECDsaCertificate, PemTestData.ECDsaECPrivateKey)) + { + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cert.Thumbprint); + AssertKeysMatch(PemTestData.ECDsaECPrivateKey, cert.GetECDsaPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_ECDsa_Pkcs8_Success() + { + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.ECDsaCertificate, PemTestData.ECDsaPkcs8Key)) + { + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cert.Thumbprint); + AssertKeysMatch(PemTestData.ECDsaPkcs8Key, cert.GetECDsaPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_ECDsa_EncryptedPkcs8_Success() + { + X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPem( + PemTestData.ECDsaCertificate, + PemTestData.ECDsaEncryptedPkcs8Key, + "test"); + + using (cert) + { + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cert.Thumbprint); + AssertKeysMatch(PemTestData.ECDsaEncryptedPkcs8Key, cert.GetECDsaPrivateKey, "test"); + } + } + + [Fact] + public static void CreateFromPem_Dsa_Pkcs8_Success() + { + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.DsaCertificate, PemTestData.DsaPkcs8Key)) + { + Assert.Equal("35052C549E4E7805E4EA204C2BE7F4BC19B88EC8", cert.Thumbprint); + AssertKeysMatch(PemTestData.DsaPkcs8Key, cert.GetDSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPem_Dsa_EncryptedPkcs8_Success() + { + X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPem( + PemTestData.DsaCertificate, + PemTestData.DsaEncryptedPkcs8Key, + "test"); + + using (cert) + { + Assert.Equal("35052C549E4E7805E4EA204C2BE7F4BC19B88EC8", cert.Thumbprint); + AssertKeysMatch(PemTestData.DsaEncryptedPkcs8Key, cert.GetDSAPrivateKey, "test"); + } + } + + private static void AssertKeysMatch(string keyPem, Func keyLoader, string password = null) where T : AsymmetricAlgorithm + { + AsymmetricAlgorithm key = keyLoader(); + Assert.NotNull(key); + AsymmetricAlgorithm alg = key switch + { + RSA => RSA.Create(), + DSA => DSA.Create(), + ECDsa => ECDsa.Create(), + _ => null + }; + + using (key) + using (alg) + { + if (password is null) + { + alg.ImportFromPem(keyPem); + } + else + { + alg.ImportFromEncryptedPem(keyPem, password); + } + + ReadOnlySpan pemPkcs8 = alg.ExportPkcs8PrivateKey(); + ReadOnlySpan keyPkcs8 = key.ExportPkcs8PrivateKey(); + Assert.True(pemPkcs8.SequenceEqual(keyPkcs8)); + } + } + } +} From d1d0a6860e893c6b07d9f93e233fbb680e6c8ae8 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 12:54:19 -0400 Subject: [PATCH 02/10] Apply suggestions from review feedback. Co-authored-by: Jeremy Barton --- .../Cryptography/X509Certificates/X509Certificate2.cs | 6 +++--- .../X509Certificates/X509Certificate2Collection.cs | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index 8c65f162fe55f9..2fbdf9f76d6f6d 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -660,7 +660,7 @@ public bool Verify() /// /// /// - /// The path specified in is null. + /// is . /// /// /// The certificate uses an unknown public key algorithm. @@ -728,7 +728,7 @@ public static X509Certificate2 CreateFromPemFile(string certPemFilePath, string? /// /// /// - /// The path specified in is null. + /// is . /// /// /// The certificate uses an unknown public key algorithm. @@ -922,7 +922,7 @@ private static X509Certificate2 ExtractCertificateFromPem(ReadOnlySpan cer if (!Convert.TryFromBase64Chars(contents[fields.Base64Data], certBytes, out int bytesWritten) || bytesWritten != fields.DecodedDataLength) { - // The contents should have already been validated by the PEM reader. + Debug.Fail("The contents should have already been validated by the PEM reader."); throw new ArgumentException(SR.Cryptography_X509_NoPemCertificate, nameof(certPem)); } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs index 0fc7c3a9cbed45..f2fb9300beea04 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs @@ -5,6 +5,7 @@ using Internal.Cryptography; using Internal.Cryptography.Pal; using Microsoft.Win32.SafeHandles; +using System.Diagnostics; namespace System.Security.Cryptography.X509Certificates { @@ -254,7 +255,7 @@ public void RemoveRange(X509Certificate2Collection certificates) /// The decoded contents of a PEM are invalid or corrupt and could not be imported. /// /// - /// is null. + /// is . /// public void ImportFromPemFile(string certPemFilePath) { @@ -301,7 +302,7 @@ public void ImportFromPem(ReadOnlySpan certPem) if (!Convert.TryFromBase64Chars(contents[fields.Base64Data], certBytes, out int bytesWritten) || bytesWritten != fields.DecodedDataLength) { - // The contents should have already been validated by the PEM reader. + Debug.Fail("The contents should have already been validated by the PEM reader."); throw new ArgumentException(SR.Cryptography_X509_NoPemCertificate, nameof(certPem)); } From 2a0d98cd625e4056079e7a0d9d00cbebf3bc8119 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 13:05:28 -0400 Subject: [PATCH 03/10] Combine PemTestData and TestData. --- .../tests/CollectionTests.cs | 12 +- .../tests/PemTestData.cs | 275 ------------------ ...Cryptography.X509Certificates.Tests.csproj | 1 - .../tests/TestData.cs | 266 +++++++++++++++++ .../tests/X509Certificate2PemTests.cs | 82 +++--- 5 files changed, 313 insertions(+), 323 deletions(-) delete mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/tests/PemTestData.cs diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index de0f5bc1bc8ad4..241156219830b3 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -1431,7 +1431,7 @@ public static void SerializedCertDisposeDoesNotRemoveKeyFile() public static void ImportFromPem_SingleCertificate_Success() { X509Certificate2Collection cc = new X509Certificate2Collection(); - cc.ImportFromPem(PemTestData.ECDsaCertificate); + cc.ImportFromPem(TestData.ECDsaCertificate); Assert.Single(cc); Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cc[0].Thumbprint); } @@ -1439,7 +1439,7 @@ public static void ImportFromPem_SingleCertificate_Success() [Fact] public static void ImportFromPem_SingleCertificate_IgnoresUnrelatedPems_Success() { - string pemAggregate = PemTestData.ECDsaPkcs8Key + PemTestData.ECDsaCertificate; + string pemAggregate = TestData.ECDsaPkcs8Key + TestData.ECDsaCertificate; X509Certificate2Collection cc = new X509Certificate2Collection(); cc.ImportFromPem(pemAggregate); Assert.Single(cc); @@ -1449,7 +1449,7 @@ public static void ImportFromPem_SingleCertificate_IgnoresUnrelatedPems_Success( [Fact] public static void ImportFromPem_MultiplePems_Success() { - string pemAggregate = PemTestData.RsaCertificate + PemTestData.ECDsaCertificate; + string pemAggregate = TestData.RsaCertificate + TestData.ECDsaCertificate; X509Certificate2Collection cc = new X509Certificate2Collection(); cc.ImportFromPem(pemAggregate); Assert.Equal(2, cc.Count); @@ -1461,15 +1461,15 @@ public static void ImportFromPem_MultiplePems_Success() public static void ImportFromPem_Exception_AllOrNothing() { X509Certificate2Collection cc = new X509Certificate2Collection(); - cc.ImportFromPem(PemTestData.DsaCertificate); + cc.ImportFromPem(TestData.DsaCertificate); StringBuilder builder = new StringBuilder(); - builder.AppendLine(PemTestData.RsaCertificate); + builder.AppendLine(TestData.RsaCertificate); builder.AppendLine(@" -----BEGIN CERTIFICATE----- MIII -----END CERTIFICATE-----"); - builder.AppendLine(PemTestData.ECDsaCertificate); + builder.AppendLine(TestData.ECDsaCertificate); Assert.ThrowsAny(() => cc.ImportFromPem(builder.ToString())); Assert.Single(cc); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PemTestData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PemTestData.cs deleted file mode 100644 index 0307583cf43afe..00000000000000 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PemTestData.cs +++ /dev/null @@ -1,275 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Security.Cryptography.X509Certificates.Tests -{ - internal static class PemTestData - { - public const string RsaCertificate = @" ------BEGIN CERTIFICATE----- -MIIDNjCCAh4CCQCu2+oEr9yAxDANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJV -UzERMA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkFsZXhhbmRyaWExEDAOBgNV -BAoMB0NvbnRvc28xFDASBgNVBAsMC0VuZ2luZWVyaW5nMB4XDTIwMDYwODE3MDk1 -M1oXDTIwMDcwODE3MDk1M1owXTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdp -bmlhMRMwEQYDVQQHDApBbGV4YW5kcmlhMRAwDgYDVQQKDAdDb250b3NvMRQwEgYD -VQQLDAtFbmdpbmVlcmluZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AL/yBbH8MoxLuApvUhWMMV+5h+v15xY9or+TxyAkwwtP0mLKMWPeHZm2S4OT8JCn -fJmz7J+7J4Nf3VB0HYDN+41uEEwNX3Caps30Kt17dcJm3/sG9uBB7eiQdbj2HNSS -nzxYK7d2mKnWwZlHg0I7/ZR5TMM8eCdJpESsHRpF5dPGgdWFgUzbdi73Yyk5/PFM -lauHPM0d4TWHoWnY0yh7Y08PMY/4MH7HoMVt+mbpV2d3DRC8WS0jhU+mbCqce4St -BWTCmR+ObuHFdMSVx7o88MWmdteZXCX8N6ohPwAl9W02pOa5Vq37xwuUZ1VvT9G2 -ndi+VRRgN51HCmG1qB1LhtsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIBtwgcLn -7LT+gfqqDsaZcMjpsHMu2TamR9inoRdxwoJKnG7dIe8tWL+nGxsN38DjjcZhb3y0 -Ilqi0e7LYqb0QyXAuS+Su2uJjypgvNd4stj+4Pl1EfU1rpzed4CA1O0pap5m0U++ -2YmWNNBmSZxcUi2ge09ZqrKm78a7Vtrpy8bNcixb+szrPSUFWh7WOXBCABusZ/OY -MnzZBXtQtQzDCtJb6IxevxAGod1XxInXQaB+nDnG4MD3v4MZQYgTI76+cAPxS6nx -pzJ/tfFzq7OAGBkrxcdmzqb1/caPWINKzwbDAEuNX2yeThP8eVrbRvGciy7LNzvR -EA0/67lEfPmRow== ------END CERTIFICATE-----"; - - public const string ECDsaCertificate = @" ------BEGIN CERTIFICATE----- -MIICDzCCAbWgAwIBAgIUeohSwWS4OT31fY5xvUxTEv07e8IwCgYIKoZIzj0EAwIw -XTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRMwEQYDVQQHDApBbGV4 -YW5kcmlhMRAwDgYDVQQKDAdDb250b3NvMRQwEgYDVQQLDAtEZXZlbG9wbWVudDAe -Fw0yMDA2MDgxOTE4NDVaFw0yMTA2MDgxOTE4NDVaMF0xCzAJBgNVBAYTAlVTMREw -DwYDVQQIDAhWaXJnaW5pYTETMBEGA1UEBwwKQWxleGFuZHJpYTEQMA4GA1UECgwH -Q29udG9zbzEUMBIGA1UECwwLRGV2ZWxvcG1lbnQwWTATBgcqhkjOPQIBBggqhkjO -PQMBBwNCAAQyB0wIKKfk5lmK4Z907qQnPsRRXh3TrU/VPMCTHxuBwoZqFBSE7gGm -JWLTnGwZ0MGMACP+N1HK4dU1S9VNoNw9o1MwUTAdBgNVHQ4EFgQUPzDLKQI9EfTa -rnMMO0/4p8rNwZgwHwYDVR0jBBgwFoAUPzDLKQI9EfTarnMMO0/4p8rNwZgwDwYD -VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiAwq18UD2DCufaMGTsz4JOQ -1vRqLI4hMLsIUQNyYgzbcgIhAJsB3qgv5WGyshlav98MPORcdCmYfkIvUCal0oPX -Wesb ------END CERTIFICATE-----"; - - public const string ECDsaPkcs8Key = @" ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIg/wHlL4nNyuLFbK -00Ga7N5LbP/2YCgEXqBiB3el8syhRANCAAQyB0wIKKfk5lmK4Z907qQnPsRRXh3T -rU/VPMCTHxuBwoZqFBSE7gGmJWLTnGwZ0MGMACP+N1HK4dU1S9VNoNw9 ------END PRIVATE KEY-----"; - - public const string ECDsaEncryptedPkcs8Key = @" ------BEGIN ENCRYPTED PRIVATE KEY----- -MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAh5RmzN7/AXZgICCAAw -DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIQHgahqqSQKcEgZCvEBMgW8a7IXmT+weI -0mlM4AcTELDkE+SEKpYC5qVF4ZDyrw4OmnVLkSPiu0GUwgJIopazWOQfetMdgC5Q -n5pYHoRm9bek0s6TK9eoaTIA+M2T0MMNM0fWXWcYaT5B2/4Uv+mMEYgIRncFe1c1 -FEixDW6ObZIXVBbxl+zK1KwtCpdewXE4HRX/qpBrPhB8z2s= ------END ENCRYPTED PRIVATE KEY-----"; - - public const string ECDsaECPrivateKey = @" ------BEGIN EC PRIVATE KEY----- -MHcCAQEEICIP8B5S+JzcrixWytNBmuzeS2z/9mAoBF6gYgd3pfLMoAoGCCqGSM49 -AwEHoUQDQgAEMgdMCCin5OZZiuGfdO6kJz7EUV4d061P1TzAkx8bgcKGahQUhO4B -piVi05xsGdDBjAAj/jdRyuHVNUvVTaDcPQ== ------END EC PRIVATE KEY-----"; - - public const string RsaPkcs1Key = @" ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAv/IFsfwyjEu4Cm9SFYwxX7mH6/XnFj2iv5PHICTDC0/SYsox -Y94dmbZLg5PwkKd8mbPsn7sng1/dUHQdgM37jW4QTA1fcJqmzfQq3Xt1wmbf+wb2 -4EHt6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9lHlMwzx4J0mkRKwdGkXl08aB1YWB -TNt2LvdjKTn88UyVq4c8zR3hNYehadjTKHtjTw8xj/gwfsegxW36ZulXZ3cNELxZ -LSOFT6ZsKpx7hK0FZMKZH45u4cV0xJXHujzwxaZ215lcJfw3qiE/ACX1bTak5rlW -rfvHC5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG2wIDAQABAoIBAEavE5XVr6+meqGt -GOdCdzQvGHS2W2D/VZ2DCAM4RnM1893ZY5LJStE+JlTP9/jtFJ9teKfhvc1NUiy8 -ddjnAcm1TF8VVZ4b9W1GizqAqn7qb3T7vZIb9UZ9XDy+tSM601Tfi0nGbLWuliCi -Cx4rBVjVyoTjEcQ2BD4du6HfN6FihzCu+DV6Dsm4Ar6orWRCYJ0v1doMYdGzOaoJ -SwcWCtAYkJ+krfYz37xlrFJfppMgYy67q040XBV7PuyjkGtt1GcNO98pw0puiCAs -VP+u+vnngNCjSqXlVSAZL1ISJylUxKkNRynABNBTfdGeRWCuhwc/M0vuEeX8A5Ce -NlDA1MECgYEA5iGjpQkyeXcsSP6zqE4LhA0L20YoCsnYnyout/Hn4C4AtUH3Yt2o -MvpRne7JxmGxmhSJkud+z6LfbaAKLU/s86wslOeclwlMkFz+CK75AFCDNecNgFCR -jENNBFIou329+/Rx7+fbGsgHfMwz2Cdv98hKCOmX3W3pZRCW+gVLMtECgYEA1YWG -q1v4M2QdB3f8hKiElicvlyNwVW7rHgbcsUiDPdGSLg/fREZeA3C4bnV+Mi8LlRDJ -+PoZyqML+xWhQDGWK+7r0iWuvFPsbCwk0BRTCYue0p7pCsUEjS0vVQPk0O1fLlOH -UdLtyyuYegsAtn5XSMN7+LeM0zyP+OWVfqWkEesCgYBSiYQYv+izedOPRpKG7Z7h -uJAlD89ytxwTUdy5qnBAjh9A4yzn75nQ1siI/Uiu9wDswyroXlC0BbVeqwSbZcwV -RQ4kRcF6xiIIsOGHmcHCpB27KmhEOiFJjiXEQ/dJ73pBMFXg9mY1/8H3t4FsqBBX -bSVoduc5yp7n2YBcoaNr0QKBgCPloPBqM94f9KluyKtc0X2U9PFJ6fbTAQA5Ux0S -/c2E0DiiPnzx/5hAeSFI64BwXFghTHNpSLDCnJ8H0eZC7+ZO8qKP50KOMW82NLIu -2I8ARCFQygkfelZpxE1crDlbzuaw8E0XUxcXKzlJZENKFk6LXuo/oZNZ2TKVFn8G -RgElAoGAMsD758wEk2bVTJFNISizHLHQ4yyCYgJSjmUqtP/GqK0qvo4Kz5TSm/2D -H7b+ao0a48b+i4EyuDTVT/CeJ3RoQzXvayc9WuipV/ewRBYRsEVzZwFO6D4aD+jI -a+7OpVqFLMtAySKTg0vPwcRF4OxQ/4YBE1e+ZGnx+vVTpkE3gjw= ------END RSA PRIVATE KEY-----"; - - public const string RsaPkcs1PublicKey = @" - -----BEGIN RSA PUBLIC KEY----- -MIIBCgKCAQEAv/IFsfwyjEu4Cm9SFYwxX7mH6/XnFj2iv5PHICTDC0/SYsoxY94d -mbZLg5PwkKd8mbPsn7sng1/dUHQdgM37jW4QTA1fcJqmzfQq3Xt1wmbf+wb24EHt -6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9lHlMwzx4J0mkRKwdGkXl08aB1YWBTNt2 -LvdjKTn88UyVq4c8zR3hNYehadjTKHtjTw8xj/gwfsegxW36ZulXZ3cNELxZLSOF -T6ZsKpx7hK0FZMKZH45u4cV0xJXHujzwxaZ215lcJfw3qiE/ACX1bTak5rlWrfvH -C5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG2wIDAQAB ------END RSA PUBLIC KEY-----"; - - public const string RsaPkcs8PublicKey = @" ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/IFsfwyjEu4Cm9SFYwx -X7mH6/XnFj2iv5PHICTDC0/SYsoxY94dmbZLg5PwkKd8mbPsn7sng1/dUHQdgM37 -jW4QTA1fcJqmzfQq3Xt1wmbf+wb24EHt6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9 -lHlMwzx4J0mkRKwdGkXl08aB1YWBTNt2LvdjKTn88UyVq4c8zR3hNYehadjTKHtj -Tw8xj/gwfsegxW36ZulXZ3cNELxZLSOFT6ZsKpx7hK0FZMKZH45u4cV0xJXHujzw -xaZ215lcJfw3qiE/ACX1bTak5rlWrfvHC5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG -2wIDAQAB ------END PUBLIC KEY-----"; - - public const string RsaPkcs8Key = @" ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/8gWx/DKMS7gK -b1IVjDFfuYfr9ecWPaK/k8cgJMMLT9JiyjFj3h2ZtkuDk/CQp3yZs+yfuyeDX91Q -dB2AzfuNbhBMDV9wmqbN9Crde3XCZt/7BvbgQe3okHW49hzUkp88WCu3dpip1sGZ -R4NCO/2UeUzDPHgnSaRErB0aReXTxoHVhYFM23Yu92MpOfzxTJWrhzzNHeE1h6Fp -2NMoe2NPDzGP+DB+x6DFbfpm6Vdndw0QvFktI4VPpmwqnHuErQVkwpkfjm7hxXTE -lce6PPDFpnbXmVwl/DeqIT8AJfVtNqTmuVat+8cLlGdVb0/Rtp3YvlUUYDedRwph -tagdS4bbAgMBAAECggEARq8TldWvr6Z6oa0Y50J3NC8YdLZbYP9VnYMIAzhGczXz -3dljkslK0T4mVM/3+O0Un214p+G9zU1SLLx12OcBybVMXxVVnhv1bUaLOoCqfupv -dPu9khv1Rn1cPL61IzrTVN+LScZsta6WIKILHisFWNXKhOMRxDYEPh27od83oWKH -MK74NXoOybgCvqitZEJgnS/V2gxh0bM5qglLBxYK0BiQn6St9jPfvGWsUl+mkyBj -LrurTjRcFXs+7KOQa23UZw073ynDSm6IICxU/676+eeA0KNKpeVVIBkvUhInKVTE -qQ1HKcAE0FN90Z5FYK6HBz8zS+4R5fwDkJ42UMDUwQKBgQDmIaOlCTJ5dyxI/rOo -TguEDQvbRigKydifKi638efgLgC1Qfdi3agy+lGd7snGYbGaFImS537Pot9toAot -T+zzrCyU55yXCUyQXP4IrvkAUIM15w2AUJGMQ00EUii7fb379HHv59sayAd8zDPY -J2/3yEoI6ZfdbellEJb6BUsy0QKBgQDVhYarW/gzZB0Hd/yEqISWJy+XI3BVbuse -BtyxSIM90ZIuD99ERl4DcLhudX4yLwuVEMn4+hnKowv7FaFAMZYr7uvSJa68U+xs -LCTQFFMJi57SnukKxQSNLS9VA+TQ7V8uU4dR0u3LK5h6CwC2fldIw3v4t4zTPI/4 -5ZV+paQR6wKBgFKJhBi/6LN5049GkobtnuG4kCUPz3K3HBNR3LmqcECOH0DjLOfv -mdDWyIj9SK73AOzDKuheULQFtV6rBJtlzBVFDiRFwXrGIgiw4YeZwcKkHbsqaEQ6 -IUmOJcRD90nvekEwVeD2ZjX/wfe3gWyoEFdtJWh25znKnufZgFyho2vRAoGAI+Wg -8Goz3h/0qW7Iq1zRfZT08Unp9tMBADlTHRL9zYTQOKI+fPH/mEB5IUjrgHBcWCFM -c2lIsMKcnwfR5kLv5k7yoo/nQo4xbzY0si7YjwBEIVDKCR96VmnETVysOVvO5rDw -TRdTFxcrOUlkQ0oWTote6j+hk1nZMpUWfwZGASUCgYAywPvnzASTZtVMkU0hKLMc -sdDjLIJiAlKOZSq0/8aorSq+jgrPlNKb/YMftv5qjRrjxv6LgTK4NNVP8J4ndGhD -Ne9rJz1a6KlX97BEFhGwRXNnAU7oPhoP6Mhr7s6lWoUsy0DJIpODS8/BxEXg7FD/ -hgETV75kafH69VOmQTeCPA== ------END PRIVATE KEY-----"; - - // password is `test`. - public const string RsaEncryptedPkcs8Key = @" ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIFGTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIozMG90LwJXECAggA -MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECK51Ahgtvr/LBIIEyO38HrNbUgAI95h6 -JolbqSkwpPeDfQsxmRlWsqtlin8K63U5gCZS/I1nkkkC13xXHB6WOQ0DrYIGv5pk -ceKiGYNki5UMxw7voXBw2F4pih07iBiATW0uKQnlNxdxbaxH4AEVyxaYTmfI6een -cPyVpgjk8BqZWIrzrcCNq0MfSkI7X1u2VRpni79xETMkdBDeNA5KqwwmeDs8EpRR -laWj2u/s+fpYkR0G1HgrJswMX38Ybp5GBu9i+JJUleDY/ByFRwaEmaMyArdhcI5V -Fp5Bl4vFFsrB0gXf8tApKN3Bmhou2gpO5VbRp3j1iHU27JCa8yBr9KOyrZ8UZrIz -bBcwjHTDOUOp1hwRVu1RUij+b33Bf4IPSobesGovIUgIe0iV+luOZGgitITb5Q4Q -Rup+q4v/M6E2JLry2zDz/FHOmtk83Og+lp9kczHjpGbj7OrGqOyT8YGqpLbmgD6q -vsuus8nCTEpAWG0rmI3RMsOR/nHdQkhgRb8U74vMIFhKfupUVAhLRvB9reKx44p0 -k5TUS+YYxh2LdZLquUeYcjMKFbRjSnzO/6N5vo8/Y/AV646bTSzrySmNaOUYArvl -A4l/GMZOFjODfjj0/p2AGE4q5kC9cKmPS99TfqD+l4IBncw9TD/tzi+c/+R+WSy9 -evT2cwQE26byo7703B7mn5Y1e0068gitPUhx5mudEloVN8Hg7N+opZpMWLrCAMEx -6kyXbWSdnrSLqv/LTSKXq2WoBk1tJ+/vfsOYdvXHU7oSje8ZoCj70OHsk3f9q8eH -b6R/+hvOJBg4UTtljXJ2BpF8yq34GPLuINYtBuMh/wo7iKQzz9AxdlfSn/EaR8Bz -MnFJlQo76Ie0LSuiHMqMunF9z6TweHMQI37IocBqpOMDRo+jz+Q0AQTe/H8PhQk0 -tGN68tPCpgkuY4apjWcNv/6dxm9/ZpR0ZvbA2DXSnxK4p1BQUEeUX1bTrb5DKL6a -KqRPokfoUbdyikGc80+TOk9SJ95if1+2XwT8gN+NLSagCRnJPWdZaOQqInYcaaZ+ -fXWPymGWEUjfIa9L71YMTOaVJ73oooC0mGFBKxMlGNamhRSqXUnX8OMJ5Zzflajw -HkgsooaGaL0CjHn7h1zFdctHyJTjRLamKe4f0drBcREz9U3sU5DQ31+hScqtJp+C -k/MZgiRRJudfi2tjNFDa28iP9U0wygPZ0RvD9IXBpYZH7KroYMtT2ysK0q16EJc3 -UqhwwEpCj6rPcVktEh/sYmeS6HL19VL03hwdxkKdmQ/7PjI8/vCy96AeIxgpBEJm -ziWCZffAXkbYwSNL2YIHronuvyt4sfjCctnoQUIBoCOQIwPF6yfNKbpL3ihPg55I -EsaecMRl/FDCbew/NqXY1ujA/ntpSMCcE4SkKKIGmzinlINw3BdfLU8bVzlPknCY -3W5ZH9Z5FV4SsEXmrRWFcAKb5kd1hvEl7XtOWpRvOtDbk9WrCko/xY2fgDXyYm25 -fl2Y9C69W24gloqGX1EOOCv1sOnUBaOoS0QnGaTPoo7aos+TNah6vB3qjylwxFFE -YTdGR9lY5wKHcJeFATIIcQyL+cziHNJQCwM839Q3uc0Sj6JvVm0sCHd/VUgzhfzX -16v5sI3D+15h+GTFjA== ------END ENCRYPTED PRIVATE KEY-----"; - - public const string Ed25519Certificate = @" ------BEGIN CERTIFICATE----- -MIICFzCCAcmgAwIBAgIUXX/A8D+9ewGf98c9FjYwAAhRIsswBQYDK2VwMIGAMQsw -CQYDVQQGEwJVUzEcMBoGA1UECAwTRGlzdGljdCBvZiBDb2x1bWJpYTETMBEGA1UE -BwwKV2FzaGluZ3RvbjEUMBIGA1UECgwLRGV2ZWxvcG1lbnQxFDASBgNVBAsMC0Rl -dmVsb3BtZW50MRIwEAYDVQQDDAlUZXN0IENlcnQwHhcNMjAwNDI4MTUwMjMwWhcN -MjEwNDI4MTUwMjMwWjCBgDELMAkGA1UEBhMCVVMxHDAaBgNVBAgME0Rpc3RpY3Qg -b2YgQ29sdW1iaWExEzARBgNVBAcMCldhc2hpbmd0b24xFDASBgNVBAoMC0RldmVs -b3BtZW50MRQwEgYDVQQLDAtEZXZlbG9wbWVudDESMBAGA1UEAwwJVGVzdCBDZXJ0 -MCowBQYDK2VwAyEAJ0WQ+SKMG8q3g9alCNzq0t3vjAeN295rq6IdaFj0NmSjUzBR -MB0GA1UdDgQWBBTUdW0AAtdSTqb1Oc6J2Pfvsk4bpjAfBgNVHSMEGDAWgBTUdW0A -AtdSTqb1Oc6J2Pfvsk4bpjAPBgNVHRMBAf8EBTADAQH/MAUGAytlcANBAOjstGTq -0fmzySS+DjSNfvkB9IXmaGd3XGIM8SWTKCnFi7L+mXhDBgd9wLO3H+EulBiuKtIo -mf0l9zwCygC+aA8= ------END CERTIFICATE-----"; - - - public const string OtherRsaPkcs1Key = @" ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA05yPUFo642M7gUYBYZgp9Itk/yPcrLcisUImPvQAsrrCeQDR -kKxo1rNApfEZAK5UffEL7kbYFRY7v0mhwWY4K2g17FJ5Bd7W54Sj5ziDzk5LKvvN -dxjZIIDi69z+AZYKcPWu4D6Znbi8isDeWv8mUTeFcA8Ine4YuvsAJ6b6IHGuz0Kr -qCZeE6319wjA6Bh14UziIPxOm1LLJ8Vlwq7dtngk034kUyliO9kWJg7aNatthn5U -q3nSUUCarhjfMFu0GhBy0TFRfsQmKNLOBMtrtx058aVbzeR3XO6pYhVsdF1cwhoP -SXPCYpgc9ZslH9u9RjQoP/0+vRuT3jBfHFKtvQIDAQABAoIBADufE93+3jKtBdoB -gGgf+Eo3cChW0Vk0bCjnS2FXXE7/QcXYDjOl8A/2B1P53yKK+7FUVhk3irA+SG03 -8MRN6auJPBAumHyn8YLfo1KFYNWix5j/wz84GA0JY2YzKLoHrT9waWozGRMQNscd -WkjnNMOTUhxlj/b3W65rA/soF7dF7Vohb4Ab251apJi/k/IBkv6gVUUH6adhPSxG -piEY4AZ0Vpngf14nI5gesStFLBhJqBtHbMeicdQbHJzg9OjNpQzNGCQzCx7EIteM -LJ/DZ+r6S9qm/m5qvgxALc0SMTi58RgIf0JwWpTrUuEEOfO0qgjKZoi8As8yFR27 -cU0n+MECgYEA7m5U81M+d/nV116Ln1+s9t0NbaR0Vzk68WsHok54lSUTItcsuCxo -CpCp7B0ARi8KzG3W9Tv+NPS64JZ7iYj/ST6w02IjgpbldpJXmPl4wV57uUOPsTq8 -hReeGEcXGN5wYTCo6jiWNmiLR4CNScmFjxcjwcXSGwac5+Sn/MttT5ECgYEA4zRR -JPaDqluVhGhu+uK5Jhb1tqeTlruSxorRuN2GAApaf354+SdfQffD73zqCpXDdtbq -uT7/Qu6tniR1bEFpLKSGeH/ayLYNfxTy9m9aC6ccxTmEhbDSntY/HHo50CtWJOWz -F5O4R6aeE5rqWfHiB+Ya9dDhdVPor5wUyPUOfW0CgYBMGw2kqaKb+zRFzZj1oz17 -gu3BXKgCG3N0Efza0v9sY+wqx9Iva4U+MhT7F/q5bFSfEkR1/NNUpfVssLv4F7Gc -+JTKtF2vVmkiIu4xFxhzaKxHY4hfQudf+DzvdOmrd30ZmMWiFbPk5BPpG+B9eATY -usMgG/vHwqGc54CzkV9v8QKBgHlveP9ckrr3AE8o0khd7b+h/eqGXqft0WE0ySsZ -m4lh/0StgFMK7CsCFkNmbGED8tkNvZ8NQLmxgDJKIkieHWyy8vxsua8VPtlxhPqa -QXKA+yuetmoOPESRFmJOIaBVyVEnRDWRyqjhMRQhdKhmU/0My9QetKJVGsThk2pl -MD1xAoGBANrkobUHP4sUIGenNfWld1As4tmKXRDTv2HHVKOCvIfF8XY5jMQBf96W -Tx7VNbqFa2IEKZW07Q3TmLgD4cplE+XxIKGRGIie3PECiQdO+EieEtPe+nFLDUrb -w4qGwpDllP58gBySGj3BQS8PG8d9fuOlCSfOQIW10dE8U9H0wwlE ------END RSA PRIVATE KEY-----"; - - public const string DsaCertificate = @" ------BEGIN CERTIFICATE----- -MIIDWTCCAxWgAwIBAgIUFRQGA90GHC74cNK/hNzQDi7XJFYwCwYJYIZIAWUDBAMC -MF0xCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhWaXJnaW5pYTETMBEGA1UEBwwKQWxl -eGFuZHJpYTEQMA4GA1UECgwHQ29udG9zbzEUMBIGA1UECwwLRGV2ZWxvcG1lbnQw -HhcNMjAwNjE5MTkyODIwWhcNMjAwNzE5MTkyODIwWjBdMQswCQYDVQQGEwJVUzER -MA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkFsZXhhbmRyaWExEDAOBgNVBAoM -B0NvbnRvc28xFDASBgNVBAsMC0RldmVsb3BtZW50MIIBtjCCASsGByqGSM44BAEw -ggEeAoGBAJyiyioeXx1O98gRCMEjlPKMpr79KrcDkoroghtuXO1U6Cx34pBRjOQm -QLDPqSOriEo5VuG6SJc/ppfZx9TrSrzqB26hKTUmiaOKmwpfIfzpi72wgsZeMOtU -7JQ+FThfGyS8VxGh6G0h7xw26B/9ALxRw25zO1cy9ZJs0EY3hsHzAhUA/4dpclsc -k8K+SkWBTcPfU+x7wTUCgYB4LP6UvrvIiiFPxhk7AEGMMr0MhcJ3hhsgKWukUqIY -sJKBM5MpKCnej5BHvnLXdKodIxygcKR4dJX7BRv69L+2RJk+UrYL1qBco5HpUslu -mA0e3gNdwRLoOoGD14dn1LD1LdESsyMgwfHHJ0RRkYwacgCVXsvHv/eAkA8qq136 -dwOBhAACgYAHltgzkK3zD8yGdcGY0YgvN5l3lna1voLmcK+XtmehjMVy7OSSFICN -KybLBOvO8paydhCb1J0klkLPAoAjgP2cEd+KueeRyJpx+jD1MsjIEXIn5jtjXdUH -d0JJmHWAyHdNzmhXrXC7JLnj4ri7xMAV3GZGDpAnYvvL0LiXzFyomqNTMFEwHQYD -VR0OBBYEFF1l4ZrF3ND05CjGd//ev0dJLCB7MB8GA1UdIwQYMBaAFF1l4ZrF3ND0 -5CjGd//ev0dJLCB7MA8GA1UdEwEB/wQFMAMBAf8wCwYJYIZIAWUDBAMCAzEAMC4C -FQD6plYf60MDCvMjf1yQ8SBaFX3YYwIVAKqRQklh2b0Qhv+US222hb8xySJV ------END CERTIFICATE-----"; - - public const string DsaPkcs8Key = @" ------BEGIN PRIVATE KEY----- -MIIBSwIBADCCASsGByqGSM44BAEwggEeAoGBAJyiyioeXx1O98gRCMEjlPKMpr79 -KrcDkoroghtuXO1U6Cx34pBRjOQmQLDPqSOriEo5VuG6SJc/ppfZx9TrSrzqB26h -KTUmiaOKmwpfIfzpi72wgsZeMOtU7JQ+FThfGyS8VxGh6G0h7xw26B/9ALxRw25z -O1cy9ZJs0EY3hsHzAhUA/4dpclsck8K+SkWBTcPfU+x7wTUCgYB4LP6UvrvIiiFP -xhk7AEGMMr0MhcJ3hhsgKWukUqIYsJKBM5MpKCnej5BHvnLXdKodIxygcKR4dJX7 -BRv69L+2RJk+UrYL1qBco5HpUslumA0e3gNdwRLoOoGD14dn1LD1LdESsyMgwfHH -J0RRkYwacgCVXsvHv/eAkA8qq136dwQXAhUA216Tqp4OvdUBNv8QLv8Z5QPopGQ= ------END PRIVATE KEY-----"; - - public const string DsaEncryptedPkcs8Key = @" ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIBoTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQI+PhdT1Kk/SkCAggA -MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECGV1ZmaiQtz2BIIBUA/6pNqTkXpkOLlI -22Lh0cm5+/foDRh3qTrAOSHHHV0Dz1xYvYMa9MFzONatLf55Rpb2ZPji3hXwUQfn -gOJeTBRTaMNz5LaKJiOIWj0qDckhgKt9cmgiBzVTvXO4pERp1uz5zcvaUOKj2TSv -ljxishj76MYQftIGMMkJQKf4OsHubCopuKUbzTPgJt0FuF4eT37+tiEMgbYrmA6p -REPE0vT1aY+LYdJLV/Dax/l4lMvYmQYOWs9TCLPlI5RZQxxte6zbcA13ESg/qLE3 -4Mx8xgXrPvCxp3h8KBKNMaJR1xzpr7UQOpkI9qja++3cJAl6O/0mdeqZct0V9Z8P -a3+wyUWo58z5sOPNdJHIMV6qw6m3w+IQoCJC7EbV0+Pyo5eSU5zbgm7YWZ9Yx6l8 -g1mCP4Q6Tqe6LjKfBsZAmYWSfKqoTKRjC3ocJMt53tIDpB5jFw== ------END ENCRYPTED PRIVATE KEY-----"; - } -} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj index 4f5cd6437886a3..a6b0a52c5a5088 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj @@ -27,7 +27,6 @@ - diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs index ff9363f7c8bf35..e4e3f345e8f9fa 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs @@ -1780,5 +1780,271 @@ internal static DSAParameters GetDSA1024Params() "B2F79D3A99ED75F7B871E6EAF2680D96D574A5F4C13BACE3B4B44DE1").HexToByteArray(); return p; } + + public const string RsaCertificate = @" +-----BEGIN CERTIFICATE----- +MIIDNjCCAh4CCQCu2+oEr9yAxDANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJV +UzERMA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkFsZXhhbmRyaWExEDAOBgNV +BAoMB0NvbnRvc28xFDASBgNVBAsMC0VuZ2luZWVyaW5nMB4XDTIwMDYwODE3MDk1 +M1oXDTIwMDcwODE3MDk1M1owXTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdp +bmlhMRMwEQYDVQQHDApBbGV4YW5kcmlhMRAwDgYDVQQKDAdDb250b3NvMRQwEgYD +VQQLDAtFbmdpbmVlcmluZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AL/yBbH8MoxLuApvUhWMMV+5h+v15xY9or+TxyAkwwtP0mLKMWPeHZm2S4OT8JCn +fJmz7J+7J4Nf3VB0HYDN+41uEEwNX3Caps30Kt17dcJm3/sG9uBB7eiQdbj2HNSS +nzxYK7d2mKnWwZlHg0I7/ZR5TMM8eCdJpESsHRpF5dPGgdWFgUzbdi73Yyk5/PFM +lauHPM0d4TWHoWnY0yh7Y08PMY/4MH7HoMVt+mbpV2d3DRC8WS0jhU+mbCqce4St +BWTCmR+ObuHFdMSVx7o88MWmdteZXCX8N6ohPwAl9W02pOa5Vq37xwuUZ1VvT9G2 +ndi+VRRgN51HCmG1qB1LhtsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAIBtwgcLn +7LT+gfqqDsaZcMjpsHMu2TamR9inoRdxwoJKnG7dIe8tWL+nGxsN38DjjcZhb3y0 +Ilqi0e7LYqb0QyXAuS+Su2uJjypgvNd4stj+4Pl1EfU1rpzed4CA1O0pap5m0U++ +2YmWNNBmSZxcUi2ge09ZqrKm78a7Vtrpy8bNcixb+szrPSUFWh7WOXBCABusZ/OY +MnzZBXtQtQzDCtJb6IxevxAGod1XxInXQaB+nDnG4MD3v4MZQYgTI76+cAPxS6nx +pzJ/tfFzq7OAGBkrxcdmzqb1/caPWINKzwbDAEuNX2yeThP8eVrbRvGciy7LNzvR +EA0/67lEfPmRow== +-----END CERTIFICATE-----"; + + public const string ECDsaCertificate = @" +-----BEGIN CERTIFICATE----- +MIICDzCCAbWgAwIBAgIUeohSwWS4OT31fY5xvUxTEv07e8IwCgYIKoZIzj0EAwIw +XTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRMwEQYDVQQHDApBbGV4 +YW5kcmlhMRAwDgYDVQQKDAdDb250b3NvMRQwEgYDVQQLDAtEZXZlbG9wbWVudDAe +Fw0yMDA2MDgxOTE4NDVaFw0yMTA2MDgxOTE4NDVaMF0xCzAJBgNVBAYTAlVTMREw +DwYDVQQIDAhWaXJnaW5pYTETMBEGA1UEBwwKQWxleGFuZHJpYTEQMA4GA1UECgwH +Q29udG9zbzEUMBIGA1UECwwLRGV2ZWxvcG1lbnQwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAAQyB0wIKKfk5lmK4Z907qQnPsRRXh3TrU/VPMCTHxuBwoZqFBSE7gGm +JWLTnGwZ0MGMACP+N1HK4dU1S9VNoNw9o1MwUTAdBgNVHQ4EFgQUPzDLKQI9EfTa +rnMMO0/4p8rNwZgwHwYDVR0jBBgwFoAUPzDLKQI9EfTarnMMO0/4p8rNwZgwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiAwq18UD2DCufaMGTsz4JOQ +1vRqLI4hMLsIUQNyYgzbcgIhAJsB3qgv5WGyshlav98MPORcdCmYfkIvUCal0oPX +Wesb +-----END CERTIFICATE-----"; + + public const string ECDsaPkcs8Key = @" +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIg/wHlL4nNyuLFbK +00Ga7N5LbP/2YCgEXqBiB3el8syhRANCAAQyB0wIKKfk5lmK4Z907qQnPsRRXh3T +rU/VPMCTHxuBwoZqFBSE7gGmJWLTnGwZ0MGMACP+N1HK4dU1S9VNoNw9 +-----END PRIVATE KEY-----"; + + public const string ECDsaEncryptedPkcs8Key = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAh5RmzN7/AXZgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIQHgahqqSQKcEgZCvEBMgW8a7IXmT+weI +0mlM4AcTELDkE+SEKpYC5qVF4ZDyrw4OmnVLkSPiu0GUwgJIopazWOQfetMdgC5Q +n5pYHoRm9bek0s6TK9eoaTIA+M2T0MMNM0fWXWcYaT5B2/4Uv+mMEYgIRncFe1c1 +FEixDW6ObZIXVBbxl+zK1KwtCpdewXE4HRX/qpBrPhB8z2s= +-----END ENCRYPTED PRIVATE KEY-----"; + + public const string ECDsaECPrivateKey = @" +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICIP8B5S+JzcrixWytNBmuzeS2z/9mAoBF6gYgd3pfLMoAoGCCqGSM49 +AwEHoUQDQgAEMgdMCCin5OZZiuGfdO6kJz7EUV4d061P1TzAkx8bgcKGahQUhO4B +piVi05xsGdDBjAAj/jdRyuHVNUvVTaDcPQ== +-----END EC PRIVATE KEY-----"; + + public const string RsaPkcs1Key = @" +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAv/IFsfwyjEu4Cm9SFYwxX7mH6/XnFj2iv5PHICTDC0/SYsox +Y94dmbZLg5PwkKd8mbPsn7sng1/dUHQdgM37jW4QTA1fcJqmzfQq3Xt1wmbf+wb2 +4EHt6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9lHlMwzx4J0mkRKwdGkXl08aB1YWB +TNt2LvdjKTn88UyVq4c8zR3hNYehadjTKHtjTw8xj/gwfsegxW36ZulXZ3cNELxZ +LSOFT6ZsKpx7hK0FZMKZH45u4cV0xJXHujzwxaZ215lcJfw3qiE/ACX1bTak5rlW +rfvHC5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG2wIDAQABAoIBAEavE5XVr6+meqGt +GOdCdzQvGHS2W2D/VZ2DCAM4RnM1893ZY5LJStE+JlTP9/jtFJ9teKfhvc1NUiy8 +ddjnAcm1TF8VVZ4b9W1GizqAqn7qb3T7vZIb9UZ9XDy+tSM601Tfi0nGbLWuliCi +Cx4rBVjVyoTjEcQ2BD4du6HfN6FihzCu+DV6Dsm4Ar6orWRCYJ0v1doMYdGzOaoJ +SwcWCtAYkJ+krfYz37xlrFJfppMgYy67q040XBV7PuyjkGtt1GcNO98pw0puiCAs +VP+u+vnngNCjSqXlVSAZL1ISJylUxKkNRynABNBTfdGeRWCuhwc/M0vuEeX8A5Ce +NlDA1MECgYEA5iGjpQkyeXcsSP6zqE4LhA0L20YoCsnYnyout/Hn4C4AtUH3Yt2o +MvpRne7JxmGxmhSJkud+z6LfbaAKLU/s86wslOeclwlMkFz+CK75AFCDNecNgFCR +jENNBFIou329+/Rx7+fbGsgHfMwz2Cdv98hKCOmX3W3pZRCW+gVLMtECgYEA1YWG +q1v4M2QdB3f8hKiElicvlyNwVW7rHgbcsUiDPdGSLg/fREZeA3C4bnV+Mi8LlRDJ ++PoZyqML+xWhQDGWK+7r0iWuvFPsbCwk0BRTCYue0p7pCsUEjS0vVQPk0O1fLlOH +UdLtyyuYegsAtn5XSMN7+LeM0zyP+OWVfqWkEesCgYBSiYQYv+izedOPRpKG7Z7h +uJAlD89ytxwTUdy5qnBAjh9A4yzn75nQ1siI/Uiu9wDswyroXlC0BbVeqwSbZcwV +RQ4kRcF6xiIIsOGHmcHCpB27KmhEOiFJjiXEQ/dJ73pBMFXg9mY1/8H3t4FsqBBX +bSVoduc5yp7n2YBcoaNr0QKBgCPloPBqM94f9KluyKtc0X2U9PFJ6fbTAQA5Ux0S +/c2E0DiiPnzx/5hAeSFI64BwXFghTHNpSLDCnJ8H0eZC7+ZO8qKP50KOMW82NLIu +2I8ARCFQygkfelZpxE1crDlbzuaw8E0XUxcXKzlJZENKFk6LXuo/oZNZ2TKVFn8G +RgElAoGAMsD758wEk2bVTJFNISizHLHQ4yyCYgJSjmUqtP/GqK0qvo4Kz5TSm/2D +H7b+ao0a48b+i4EyuDTVT/CeJ3RoQzXvayc9WuipV/ewRBYRsEVzZwFO6D4aD+jI +a+7OpVqFLMtAySKTg0vPwcRF4OxQ/4YBE1e+ZGnx+vVTpkE3gjw= +-----END RSA PRIVATE KEY-----"; + + public const string RsaPkcs1PublicKey = @" + -----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEAv/IFsfwyjEu4Cm9SFYwxX7mH6/XnFj2iv5PHICTDC0/SYsoxY94d +mbZLg5PwkKd8mbPsn7sng1/dUHQdgM37jW4QTA1fcJqmzfQq3Xt1wmbf+wb24EHt +6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9lHlMwzx4J0mkRKwdGkXl08aB1YWBTNt2 +LvdjKTn88UyVq4c8zR3hNYehadjTKHtjTw8xj/gwfsegxW36ZulXZ3cNELxZLSOF +T6ZsKpx7hK0FZMKZH45u4cV0xJXHujzwxaZ215lcJfw3qiE/ACX1bTak5rlWrfvH +C5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG2wIDAQAB +-----END RSA PUBLIC KEY-----"; + + public const string RsaPkcs8PublicKey = @" +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/IFsfwyjEu4Cm9SFYwx +X7mH6/XnFj2iv5PHICTDC0/SYsoxY94dmbZLg5PwkKd8mbPsn7sng1/dUHQdgM37 +jW4QTA1fcJqmzfQq3Xt1wmbf+wb24EHt6JB1uPYc1JKfPFgrt3aYqdbBmUeDQjv9 +lHlMwzx4J0mkRKwdGkXl08aB1YWBTNt2LvdjKTn88UyVq4c8zR3hNYehadjTKHtj +Tw8xj/gwfsegxW36ZulXZ3cNELxZLSOFT6ZsKpx7hK0FZMKZH45u4cV0xJXHujzw +xaZ215lcJfw3qiE/ACX1bTak5rlWrfvHC5RnVW9P0bad2L5VFGA3nUcKYbWoHUuG +2wIDAQAB +-----END PUBLIC KEY-----"; + + public const string RsaPkcs8Key = @" +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/8gWx/DKMS7gK +b1IVjDFfuYfr9ecWPaK/k8cgJMMLT9JiyjFj3h2ZtkuDk/CQp3yZs+yfuyeDX91Q +dB2AzfuNbhBMDV9wmqbN9Crde3XCZt/7BvbgQe3okHW49hzUkp88WCu3dpip1sGZ +R4NCO/2UeUzDPHgnSaRErB0aReXTxoHVhYFM23Yu92MpOfzxTJWrhzzNHeE1h6Fp +2NMoe2NPDzGP+DB+x6DFbfpm6Vdndw0QvFktI4VPpmwqnHuErQVkwpkfjm7hxXTE +lce6PPDFpnbXmVwl/DeqIT8AJfVtNqTmuVat+8cLlGdVb0/Rtp3YvlUUYDedRwph +tagdS4bbAgMBAAECggEARq8TldWvr6Z6oa0Y50J3NC8YdLZbYP9VnYMIAzhGczXz +3dljkslK0T4mVM/3+O0Un214p+G9zU1SLLx12OcBybVMXxVVnhv1bUaLOoCqfupv +dPu9khv1Rn1cPL61IzrTVN+LScZsta6WIKILHisFWNXKhOMRxDYEPh27od83oWKH +MK74NXoOybgCvqitZEJgnS/V2gxh0bM5qglLBxYK0BiQn6St9jPfvGWsUl+mkyBj +LrurTjRcFXs+7KOQa23UZw073ynDSm6IICxU/676+eeA0KNKpeVVIBkvUhInKVTE +qQ1HKcAE0FN90Z5FYK6HBz8zS+4R5fwDkJ42UMDUwQKBgQDmIaOlCTJ5dyxI/rOo +TguEDQvbRigKydifKi638efgLgC1Qfdi3agy+lGd7snGYbGaFImS537Pot9toAot +T+zzrCyU55yXCUyQXP4IrvkAUIM15w2AUJGMQ00EUii7fb379HHv59sayAd8zDPY +J2/3yEoI6ZfdbellEJb6BUsy0QKBgQDVhYarW/gzZB0Hd/yEqISWJy+XI3BVbuse +BtyxSIM90ZIuD99ERl4DcLhudX4yLwuVEMn4+hnKowv7FaFAMZYr7uvSJa68U+xs +LCTQFFMJi57SnukKxQSNLS9VA+TQ7V8uU4dR0u3LK5h6CwC2fldIw3v4t4zTPI/4 +5ZV+paQR6wKBgFKJhBi/6LN5049GkobtnuG4kCUPz3K3HBNR3LmqcECOH0DjLOfv +mdDWyIj9SK73AOzDKuheULQFtV6rBJtlzBVFDiRFwXrGIgiw4YeZwcKkHbsqaEQ6 +IUmOJcRD90nvekEwVeD2ZjX/wfe3gWyoEFdtJWh25znKnufZgFyho2vRAoGAI+Wg +8Goz3h/0qW7Iq1zRfZT08Unp9tMBADlTHRL9zYTQOKI+fPH/mEB5IUjrgHBcWCFM +c2lIsMKcnwfR5kLv5k7yoo/nQo4xbzY0si7YjwBEIVDKCR96VmnETVysOVvO5rDw +TRdTFxcrOUlkQ0oWTote6j+hk1nZMpUWfwZGASUCgYAywPvnzASTZtVMkU0hKLMc +sdDjLIJiAlKOZSq0/8aorSq+jgrPlNKb/YMftv5qjRrjxv6LgTK4NNVP8J4ndGhD +Ne9rJz1a6KlX97BEFhGwRXNnAU7oPhoP6Mhr7s6lWoUsy0DJIpODS8/BxEXg7FD/ +hgETV75kafH69VOmQTeCPA== +-----END PRIVATE KEY-----"; + + // password is `test`. + public const string RsaEncryptedPkcs8Key = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFGTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIozMG90LwJXECAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECK51Ahgtvr/LBIIEyO38HrNbUgAI95h6 +JolbqSkwpPeDfQsxmRlWsqtlin8K63U5gCZS/I1nkkkC13xXHB6WOQ0DrYIGv5pk +ceKiGYNki5UMxw7voXBw2F4pih07iBiATW0uKQnlNxdxbaxH4AEVyxaYTmfI6een +cPyVpgjk8BqZWIrzrcCNq0MfSkI7X1u2VRpni79xETMkdBDeNA5KqwwmeDs8EpRR +laWj2u/s+fpYkR0G1HgrJswMX38Ybp5GBu9i+JJUleDY/ByFRwaEmaMyArdhcI5V +Fp5Bl4vFFsrB0gXf8tApKN3Bmhou2gpO5VbRp3j1iHU27JCa8yBr9KOyrZ8UZrIz +bBcwjHTDOUOp1hwRVu1RUij+b33Bf4IPSobesGovIUgIe0iV+luOZGgitITb5Q4Q +Rup+q4v/M6E2JLry2zDz/FHOmtk83Og+lp9kczHjpGbj7OrGqOyT8YGqpLbmgD6q +vsuus8nCTEpAWG0rmI3RMsOR/nHdQkhgRb8U74vMIFhKfupUVAhLRvB9reKx44p0 +k5TUS+YYxh2LdZLquUeYcjMKFbRjSnzO/6N5vo8/Y/AV646bTSzrySmNaOUYArvl +A4l/GMZOFjODfjj0/p2AGE4q5kC9cKmPS99TfqD+l4IBncw9TD/tzi+c/+R+WSy9 +evT2cwQE26byo7703B7mn5Y1e0068gitPUhx5mudEloVN8Hg7N+opZpMWLrCAMEx +6kyXbWSdnrSLqv/LTSKXq2WoBk1tJ+/vfsOYdvXHU7oSje8ZoCj70OHsk3f9q8eH +b6R/+hvOJBg4UTtljXJ2BpF8yq34GPLuINYtBuMh/wo7iKQzz9AxdlfSn/EaR8Bz +MnFJlQo76Ie0LSuiHMqMunF9z6TweHMQI37IocBqpOMDRo+jz+Q0AQTe/H8PhQk0 +tGN68tPCpgkuY4apjWcNv/6dxm9/ZpR0ZvbA2DXSnxK4p1BQUEeUX1bTrb5DKL6a +KqRPokfoUbdyikGc80+TOk9SJ95if1+2XwT8gN+NLSagCRnJPWdZaOQqInYcaaZ+ +fXWPymGWEUjfIa9L71YMTOaVJ73oooC0mGFBKxMlGNamhRSqXUnX8OMJ5Zzflajw +HkgsooaGaL0CjHn7h1zFdctHyJTjRLamKe4f0drBcREz9U3sU5DQ31+hScqtJp+C +k/MZgiRRJudfi2tjNFDa28iP9U0wygPZ0RvD9IXBpYZH7KroYMtT2ysK0q16EJc3 +UqhwwEpCj6rPcVktEh/sYmeS6HL19VL03hwdxkKdmQ/7PjI8/vCy96AeIxgpBEJm +ziWCZffAXkbYwSNL2YIHronuvyt4sfjCctnoQUIBoCOQIwPF6yfNKbpL3ihPg55I +EsaecMRl/FDCbew/NqXY1ujA/ntpSMCcE4SkKKIGmzinlINw3BdfLU8bVzlPknCY +3W5ZH9Z5FV4SsEXmrRWFcAKb5kd1hvEl7XtOWpRvOtDbk9WrCko/xY2fgDXyYm25 +fl2Y9C69W24gloqGX1EOOCv1sOnUBaOoS0QnGaTPoo7aos+TNah6vB3qjylwxFFE +YTdGR9lY5wKHcJeFATIIcQyL+cziHNJQCwM839Q3uc0Sj6JvVm0sCHd/VUgzhfzX +16v5sI3D+15h+GTFjA== +-----END ENCRYPTED PRIVATE KEY-----"; + + public const string Ed25519Certificate = @" +-----BEGIN CERTIFICATE----- +MIICFzCCAcmgAwIBAgIUXX/A8D+9ewGf98c9FjYwAAhRIsswBQYDK2VwMIGAMQsw +CQYDVQQGEwJVUzEcMBoGA1UECAwTRGlzdGljdCBvZiBDb2x1bWJpYTETMBEGA1UE +BwwKV2FzaGluZ3RvbjEUMBIGA1UECgwLRGV2ZWxvcG1lbnQxFDASBgNVBAsMC0Rl +dmVsb3BtZW50MRIwEAYDVQQDDAlUZXN0IENlcnQwHhcNMjAwNDI4MTUwMjMwWhcN +MjEwNDI4MTUwMjMwWjCBgDELMAkGA1UEBhMCVVMxHDAaBgNVBAgME0Rpc3RpY3Qg +b2YgQ29sdW1iaWExEzARBgNVBAcMCldhc2hpbmd0b24xFDASBgNVBAoMC0RldmVs +b3BtZW50MRQwEgYDVQQLDAtEZXZlbG9wbWVudDESMBAGA1UEAwwJVGVzdCBDZXJ0 +MCowBQYDK2VwAyEAJ0WQ+SKMG8q3g9alCNzq0t3vjAeN295rq6IdaFj0NmSjUzBR +MB0GA1UdDgQWBBTUdW0AAtdSTqb1Oc6J2Pfvsk4bpjAfBgNVHSMEGDAWgBTUdW0A +AtdSTqb1Oc6J2Pfvsk4bpjAPBgNVHRMBAf8EBTADAQH/MAUGAytlcANBAOjstGTq +0fmzySS+DjSNfvkB9IXmaGd3XGIM8SWTKCnFi7L+mXhDBgd9wLO3H+EulBiuKtIo +mf0l9zwCygC+aA8= +-----END CERTIFICATE-----"; + + + public const string OtherRsaPkcs1Key = @" +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA05yPUFo642M7gUYBYZgp9Itk/yPcrLcisUImPvQAsrrCeQDR +kKxo1rNApfEZAK5UffEL7kbYFRY7v0mhwWY4K2g17FJ5Bd7W54Sj5ziDzk5LKvvN +dxjZIIDi69z+AZYKcPWu4D6Znbi8isDeWv8mUTeFcA8Ine4YuvsAJ6b6IHGuz0Kr +qCZeE6319wjA6Bh14UziIPxOm1LLJ8Vlwq7dtngk034kUyliO9kWJg7aNatthn5U +q3nSUUCarhjfMFu0GhBy0TFRfsQmKNLOBMtrtx058aVbzeR3XO6pYhVsdF1cwhoP +SXPCYpgc9ZslH9u9RjQoP/0+vRuT3jBfHFKtvQIDAQABAoIBADufE93+3jKtBdoB +gGgf+Eo3cChW0Vk0bCjnS2FXXE7/QcXYDjOl8A/2B1P53yKK+7FUVhk3irA+SG03 +8MRN6auJPBAumHyn8YLfo1KFYNWix5j/wz84GA0JY2YzKLoHrT9waWozGRMQNscd +WkjnNMOTUhxlj/b3W65rA/soF7dF7Vohb4Ab251apJi/k/IBkv6gVUUH6adhPSxG +piEY4AZ0Vpngf14nI5gesStFLBhJqBtHbMeicdQbHJzg9OjNpQzNGCQzCx7EIteM +LJ/DZ+r6S9qm/m5qvgxALc0SMTi58RgIf0JwWpTrUuEEOfO0qgjKZoi8As8yFR27 +cU0n+MECgYEA7m5U81M+d/nV116Ln1+s9t0NbaR0Vzk68WsHok54lSUTItcsuCxo +CpCp7B0ARi8KzG3W9Tv+NPS64JZ7iYj/ST6w02IjgpbldpJXmPl4wV57uUOPsTq8 +hReeGEcXGN5wYTCo6jiWNmiLR4CNScmFjxcjwcXSGwac5+Sn/MttT5ECgYEA4zRR +JPaDqluVhGhu+uK5Jhb1tqeTlruSxorRuN2GAApaf354+SdfQffD73zqCpXDdtbq +uT7/Qu6tniR1bEFpLKSGeH/ayLYNfxTy9m9aC6ccxTmEhbDSntY/HHo50CtWJOWz +F5O4R6aeE5rqWfHiB+Ya9dDhdVPor5wUyPUOfW0CgYBMGw2kqaKb+zRFzZj1oz17 +gu3BXKgCG3N0Efza0v9sY+wqx9Iva4U+MhT7F/q5bFSfEkR1/NNUpfVssLv4F7Gc ++JTKtF2vVmkiIu4xFxhzaKxHY4hfQudf+DzvdOmrd30ZmMWiFbPk5BPpG+B9eATY +usMgG/vHwqGc54CzkV9v8QKBgHlveP9ckrr3AE8o0khd7b+h/eqGXqft0WE0ySsZ +m4lh/0StgFMK7CsCFkNmbGED8tkNvZ8NQLmxgDJKIkieHWyy8vxsua8VPtlxhPqa +QXKA+yuetmoOPESRFmJOIaBVyVEnRDWRyqjhMRQhdKhmU/0My9QetKJVGsThk2pl +MD1xAoGBANrkobUHP4sUIGenNfWld1As4tmKXRDTv2HHVKOCvIfF8XY5jMQBf96W +Tx7VNbqFa2IEKZW07Q3TmLgD4cplE+XxIKGRGIie3PECiQdO+EieEtPe+nFLDUrb +w4qGwpDllP58gBySGj3BQS8PG8d9fuOlCSfOQIW10dE8U9H0wwlE +-----END RSA PRIVATE KEY-----"; + + public const string DsaCertificate = @" +-----BEGIN CERTIFICATE----- +MIIDWTCCAxWgAwIBAgIUFRQGA90GHC74cNK/hNzQDi7XJFYwCwYJYIZIAWUDBAMC +MF0xCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhWaXJnaW5pYTETMBEGA1UEBwwKQWxl +eGFuZHJpYTEQMA4GA1UECgwHQ29udG9zbzEUMBIGA1UECwwLRGV2ZWxvcG1lbnQw +HhcNMjAwNjE5MTkyODIwWhcNMjAwNzE5MTkyODIwWjBdMQswCQYDVQQGEwJVUzER +MA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkFsZXhhbmRyaWExEDAOBgNVBAoM +B0NvbnRvc28xFDASBgNVBAsMC0RldmVsb3BtZW50MIIBtjCCASsGByqGSM44BAEw +ggEeAoGBAJyiyioeXx1O98gRCMEjlPKMpr79KrcDkoroghtuXO1U6Cx34pBRjOQm +QLDPqSOriEo5VuG6SJc/ppfZx9TrSrzqB26hKTUmiaOKmwpfIfzpi72wgsZeMOtU +7JQ+FThfGyS8VxGh6G0h7xw26B/9ALxRw25zO1cy9ZJs0EY3hsHzAhUA/4dpclsc +k8K+SkWBTcPfU+x7wTUCgYB4LP6UvrvIiiFPxhk7AEGMMr0MhcJ3hhsgKWukUqIY +sJKBM5MpKCnej5BHvnLXdKodIxygcKR4dJX7BRv69L+2RJk+UrYL1qBco5HpUslu +mA0e3gNdwRLoOoGD14dn1LD1LdESsyMgwfHHJ0RRkYwacgCVXsvHv/eAkA8qq136 +dwOBhAACgYAHltgzkK3zD8yGdcGY0YgvN5l3lna1voLmcK+XtmehjMVy7OSSFICN +KybLBOvO8paydhCb1J0klkLPAoAjgP2cEd+KueeRyJpx+jD1MsjIEXIn5jtjXdUH +d0JJmHWAyHdNzmhXrXC7JLnj4ri7xMAV3GZGDpAnYvvL0LiXzFyomqNTMFEwHQYD +VR0OBBYEFF1l4ZrF3ND05CjGd//ev0dJLCB7MB8GA1UdIwQYMBaAFF1l4ZrF3ND0 +5CjGd//ev0dJLCB7MA8GA1UdEwEB/wQFMAMBAf8wCwYJYIZIAWUDBAMCAzEAMC4C +FQD6plYf60MDCvMjf1yQ8SBaFX3YYwIVAKqRQklh2b0Qhv+US222hb8xySJV +-----END CERTIFICATE-----"; + + public const string DsaPkcs8Key = @" +-----BEGIN PRIVATE KEY----- +MIIBSwIBADCCASsGByqGSM44BAEwggEeAoGBAJyiyioeXx1O98gRCMEjlPKMpr79 +KrcDkoroghtuXO1U6Cx34pBRjOQmQLDPqSOriEo5VuG6SJc/ppfZx9TrSrzqB26h +KTUmiaOKmwpfIfzpi72wgsZeMOtU7JQ+FThfGyS8VxGh6G0h7xw26B/9ALxRw25z +O1cy9ZJs0EY3hsHzAhUA/4dpclsck8K+SkWBTcPfU+x7wTUCgYB4LP6UvrvIiiFP +xhk7AEGMMr0MhcJ3hhsgKWukUqIYsJKBM5MpKCnej5BHvnLXdKodIxygcKR4dJX7 +BRv69L+2RJk+UrYL1qBco5HpUslumA0e3gNdwRLoOoGD14dn1LD1LdESsyMgwfHH +J0RRkYwacgCVXsvHv/eAkA8qq136dwQXAhUA216Tqp4OvdUBNv8QLv8Z5QPopGQ= +-----END PRIVATE KEY-----"; + + public const string DsaEncryptedPkcs8Key = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBoTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQI+PhdT1Kk/SkCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECGV1ZmaiQtz2BIIBUA/6pNqTkXpkOLlI +22Lh0cm5+/foDRh3qTrAOSHHHV0Dz1xYvYMa9MFzONatLf55Rpb2ZPji3hXwUQfn +gOJeTBRTaMNz5LaKJiOIWj0qDckhgKt9cmgiBzVTvXO4pERp1uz5zcvaUOKj2TSv +ljxishj76MYQftIGMMkJQKf4OsHubCopuKUbzTPgJt0FuF4eT37+tiEMgbYrmA6p +REPE0vT1aY+LYdJLV/Dax/l4lMvYmQYOWs9TCLPlI5RZQxxte6zbcA13ESg/qLE3 +4Mx8xgXrPvCxp3h8KBKNMaJR1xzpr7UQOpkI9qja++3cJAl6O/0mdeqZct0V9Z8P +a3+wyUWo58z5sOPNdJHIMV6qw6m3w+IQoCJC7EbV0+Pyo5eSU5zbgm7YWZ9Yx6l8 +g1mCP4Q6Tqe6LjKfBsZAmYWSfKqoTKRjC3ocJMt53tIDpB5jFw== +-----END ENCRYPTED PRIVATE KEY-----"; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index 81d76efeabdfea..a753fa9cc024fa 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -33,7 +33,7 @@ public static void CreateFromPem_ArgumentException_MalformedCertificate() public static void CreateFromPem_CryptographicException_InvalidKeyAlgorithm() { CryptographicException ce = Assert.Throws(() => - X509Certificate2.CreateFromPem(PemTestData.Ed25519Certificate, default)); + X509Certificate2.CreateFromPem(TestData.Ed25519Certificate, default)); Assert.Contains("'1.3.101.112'", ce.Message); } @@ -42,7 +42,7 @@ public static void CreateFromPem_CryptographicException_InvalidKeyAlgorithm() public static void CreateFromPem_ArgumentException_NoKey() { AssertExtensions.Throws("keyPem", () => - X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, default)); + X509Certificate2.CreateFromPem(TestData.RsaCertificate, default)); } [Fact] @@ -54,92 +54,92 @@ public static void CreateFromPem_ArgumentException_MalformedKey() -----END RSA PRIVATE KEY----- "; AssertExtensions.Throws("keyPem", () => - X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, CertContents)); + X509Certificate2.CreateFromPem(TestData.RsaCertificate, CertContents)); } [Fact] public static void CreateFromPem_Rsa_Pkcs1_Success() { - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, PemTestData.RsaPkcs1Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.RsaCertificate, TestData.RsaPkcs1Key)) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); } } [Fact] public static void CreateFromPem_Rsa_Pkcs8_Success() { - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, PemTestData.RsaPkcs8Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.RsaCertificate, TestData.RsaPkcs8Key)) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaPkcs8Key, cert.GetRSAPrivateKey); + AssertKeysMatch(TestData.RsaPkcs8Key, cert.GetRSAPrivateKey); } } [Fact] public static void CreateFromPem_Rsa_Aggregate_Pkcs8_Success() { - string pemAggregate = PemTestData.RsaCertificate + PemTestData.RsaPkcs8Key; + string pemAggregate = TestData.RsaCertificate + TestData.RsaPkcs8Key; using (X509Certificate2 cert = X509Certificate2.CreateFromPem(pemAggregate, pemAggregate)) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaPkcs8Key, cert.GetRSAPrivateKey); + AssertKeysMatch(TestData.RsaPkcs8Key, cert.GetRSAPrivateKey); } } [Fact] public static void CreateFromPem_Rsa_Aggregate_Pkcs1_Success() { - string pemAggregate = PemTestData.RsaCertificate + PemTestData.RsaPkcs1Key; + string pemAggregate = TestData.RsaCertificate + TestData.RsaPkcs1Key; using (X509Certificate2 cert = X509Certificate2.CreateFromPem(pemAggregate, pemAggregate)) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); } } [Fact] public static void CreateFromPem_Rsa_LoadsFirstCertificate_Success() { - string certAggregate = PemTestData.RsaCertificate + PemTestData.ECDsaCertificate; - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(certAggregate, PemTestData.RsaPkcs1Key)) + string certAggregate = TestData.RsaCertificate + TestData.ECDsaCertificate; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(certAggregate, TestData.RsaPkcs1Key)) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); } } [Fact] public static void CreateFromPem_Rsa_IgnoresNonMatchingAlgorithmKeys_Success() { - string keyAggregate = PemTestData.ECDsaECPrivateKey + PemTestData.RsaPkcs1Key; - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, keyAggregate)) + string keyAggregate = TestData.ECDsaECPrivateKey + TestData.RsaPkcs1Key; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.RsaCertificate, keyAggregate)) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); } } [Fact] public static void CreateFromPem_Rsa_IgnoresPkcs1PublicKey_Success() { - string keyAggregate = PemTestData.RsaPkcs1PublicKey + PemTestData.RsaPkcs1Key; - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, keyAggregate)) + string keyAggregate = TestData.RsaPkcs1PublicKey + TestData.RsaPkcs1Key; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.RsaCertificate, keyAggregate)) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); } } [Fact] public static void CreateFromPem_Rsa_IgnoresPkcs8PublicKey_Success() { - string keyAggregate = PemTestData.RsaPkcs8PublicKey + PemTestData.RsaPkcs1Key; - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, keyAggregate)) + string keyAggregate = TestData.RsaPkcs8PublicKey + TestData.RsaPkcs1Key; + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.RsaCertificate, keyAggregate)) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); } } @@ -147,21 +147,21 @@ public static void CreateFromPem_Rsa_IgnoresPkcs8PublicKey_Success() public static void CreateFromPem_Rsa_KeyMismatch_Fail() { AssertExtensions.Throws("privateKey", () => - X509Certificate2.CreateFromPem(PemTestData.RsaCertificate, PemTestData.OtherRsaPkcs1Key)); + X509Certificate2.CreateFromPem(TestData.RsaCertificate, TestData.OtherRsaPkcs1Key)); } [Fact] public static void CreateFromEncryptedPem_Rsa_Success() { X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPem( - PemTestData.RsaCertificate, - PemTestData.RsaEncryptedPkcs8Key, + TestData.RsaCertificate, + TestData.RsaEncryptedPkcs8Key, "test"); using (cert) { Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); - AssertKeysMatch(PemTestData.RsaEncryptedPkcs8Key, cert.GetRSAPrivateKey, "test"); + AssertKeysMatch(TestData.RsaEncryptedPkcs8Key, cert.GetRSAPrivateKey, "test"); } } @@ -169,7 +169,7 @@ public static void CreateFromEncryptedPem_Rsa_Success() public static void CreateFromEncryptedPem_Rsa_InvalidPassword_Fail() { CryptographicException ce = Assert.Throws(() => - X509Certificate2.CreateFromEncryptedPem(PemTestData.RsaCertificate, PemTestData.RsaEncryptedPkcs8Key, "florp")); + X509Certificate2.CreateFromEncryptedPem(TestData.RsaCertificate, TestData.RsaEncryptedPkcs8Key, "florp")); Assert.Contains("password may be incorrect", ce.Message); } @@ -178,26 +178,26 @@ public static void CreateFromEncryptedPem_Rsa_InvalidPassword_Fail() public static void CreateFromEncryptedPem_Rsa_IgnoresUnencryptedPem_Fail() { AssertExtensions.Throws("keyPem", () => - X509Certificate2.CreateFromEncryptedPem(PemTestData.RsaCertificate, PemTestData.RsaPkcs8Key, "test")); + X509Certificate2.CreateFromEncryptedPem(TestData.RsaCertificate, TestData.RsaPkcs8Key, "test")); } [Fact] public static void CreateFromPem_ECDsa_ECPrivateKey_Success() { - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.ECDsaCertificate, PemTestData.ECDsaECPrivateKey)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.ECDsaCertificate, TestData.ECDsaECPrivateKey)) { Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cert.Thumbprint); - AssertKeysMatch(PemTestData.ECDsaECPrivateKey, cert.GetECDsaPrivateKey); + AssertKeysMatch(TestData.ECDsaECPrivateKey, cert.GetECDsaPrivateKey); } } [Fact] public static void CreateFromPem_ECDsa_Pkcs8_Success() { - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.ECDsaCertificate, PemTestData.ECDsaPkcs8Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.ECDsaCertificate, TestData.ECDsaPkcs8Key)) { Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cert.Thumbprint); - AssertKeysMatch(PemTestData.ECDsaPkcs8Key, cert.GetECDsaPrivateKey); + AssertKeysMatch(TestData.ECDsaPkcs8Key, cert.GetECDsaPrivateKey); } } @@ -205,24 +205,24 @@ public static void CreateFromPem_ECDsa_Pkcs8_Success() public static void CreateFromPem_ECDsa_EncryptedPkcs8_Success() { X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPem( - PemTestData.ECDsaCertificate, - PemTestData.ECDsaEncryptedPkcs8Key, + TestData.ECDsaCertificate, + TestData.ECDsaEncryptedPkcs8Key, "test"); using (cert) { Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cert.Thumbprint); - AssertKeysMatch(PemTestData.ECDsaEncryptedPkcs8Key, cert.GetECDsaPrivateKey, "test"); + AssertKeysMatch(TestData.ECDsaEncryptedPkcs8Key, cert.GetECDsaPrivateKey, "test"); } } [Fact] public static void CreateFromPem_Dsa_Pkcs8_Success() { - using (X509Certificate2 cert = X509Certificate2.CreateFromPem(PemTestData.DsaCertificate, PemTestData.DsaPkcs8Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.DsaCertificate, TestData.DsaPkcs8Key)) { Assert.Equal("35052C549E4E7805E4EA204C2BE7F4BC19B88EC8", cert.Thumbprint); - AssertKeysMatch(PemTestData.DsaPkcs8Key, cert.GetDSAPrivateKey); + AssertKeysMatch(TestData.DsaPkcs8Key, cert.GetDSAPrivateKey); } } @@ -230,14 +230,14 @@ public static void CreateFromPem_Dsa_Pkcs8_Success() public static void CreateFromPem_Dsa_EncryptedPkcs8_Success() { X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPem( - PemTestData.DsaCertificate, - PemTestData.DsaEncryptedPkcs8Key, + TestData.DsaCertificate, + TestData.DsaEncryptedPkcs8Key, "test"); using (cert) { Assert.Equal("35052C549E4E7805E4EA204C2BE7F4BC19B88EC8", cert.Thumbprint); - AssertKeysMatch(PemTestData.DsaEncryptedPkcs8Key, cert.GetDSAPrivateKey, "test"); + AssertKeysMatch(TestData.DsaEncryptedPkcs8Key, cert.GetDSAPrivateKey, "test"); } } From cb194b820a7b11e652e5fa02ad83233e093819f6 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 13:19:04 -0400 Subject: [PATCH 04/10] Throw CryptographicException instead of ArgumentException. --- .../X509Certificates/X509Certificate2.cs | 36 +++++++++---------- .../X509Certificate2Collection.cs | 2 +- .../tests/X509Certificate2PemTests.cs | 18 +++++----- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index 2fbdf9f76d6f6d..036b4cb57fa0ac 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -658,13 +658,12 @@ public bool Verify() /// The contents of the file path in contains /// a key that does not match the public key in the certificate. /// + /// -or- + /// The certificate uses an unknown public key algorithm. /// /// /// is . /// - /// - /// The certificate uses an unknown public key algorithm. - /// /// /// /// See for additional documentation about @@ -711,7 +710,7 @@ public static X509Certificate2 CreateFromPemFile(string certPemFilePath, string? /// /// The password for the encrypted PEM. /// A new certificate with the private key. - /// + /// /// /// The contents of the file path in do not contain /// a PEM-encoded certificate, or it is malformed. @@ -726,15 +725,14 @@ public static X509Certificate2 CreateFromPemFile(string certPemFilePath, string? /// The contents of the file path in contains /// a key that does not match the public key in the certificate. /// - /// - /// - /// is . - /// - /// + /// -or- /// The certificate uses an unknown public key algorithm. /// -or- /// The password specified for the private key is incorrect. /// + /// + /// is . + /// /// /// /// See for additional documentation about @@ -772,15 +770,14 @@ public static X509Certificate2 CreateFromEncryptedPemFile(string certPemFilePath /// The text of the PEM-encoded X509 certificate. /// The text of the PEM-encoded private key. /// A new certificate with the private key. - /// + /// /// The contents of do not contain a PEM-encoded certificate, or it is malformed. /// -or- /// The contents of do not contain a PEM-encoded private key, or it is malformed. /// -or- /// The contents of contains a key that does not match the public key in the certificate. - /// - /// - /// The certificate uses an unknown public key algorithm. + /// -or- + /// The certificate uses an unknown public key algorithm. /// /// /// @@ -843,7 +840,7 @@ public static X509Certificate2 CreateFromPem(ReadOnlySpan certPem, ReadOnl /// The text of the password protected PEM-encoded private key. /// The password for the encrypted PEM. /// A new certificate with the private key. - /// + /// /// The contents of do not contain a PEM-encoded certificate, or it is malformed. /// -or- /// @@ -852,8 +849,7 @@ public static X509Certificate2 CreateFromPem(ReadOnlySpan certPem, ReadOnl /// /// -or- /// The contents of contains a key that does not match the public key in the certificate. - /// - /// + /// -or- /// The certificate uses an unknown public key algorithm. /// -or- /// The password specified for the private key is incorrect. @@ -923,14 +919,14 @@ private static X509Certificate2 ExtractCertificateFromPem(ReadOnlySpan cer || bytesWritten != fields.DecodedDataLength) { Debug.Fail("The contents should have already been validated by the PEM reader."); - throw new ArgumentException(SR.Cryptography_X509_NoPemCertificate, nameof(certPem)); + throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } return new X509Certificate2(certBytes); } } - throw new ArgumentException(SR.Cryptography_X509_NoPemCertificate, nameof(certPem)); + throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } private static TAlg ExtractKeyFromPem(ReadOnlySpan keyPem, string[] labels, Func factory) where TAlg : AsymmetricAlgorithm @@ -950,7 +946,7 @@ private static TAlg ExtractKeyFromPem(ReadOnlySpan keyPem, string[] } } - throw new ArgumentException(SR.Cryptography_X509_NoOrMismatchedPemKey, nameof(keyPem)); + throw new CryptographicException(SR.Cryptography_X509_NoOrMismatchedPemKey); } private static TAlg ExtractKeyFromEncryptedPem( @@ -970,7 +966,7 @@ private static TAlg ExtractKeyFromEncryptedPem( } } - throw new ArgumentException(SR.Cryptography_X509_NoOrMismatchedPemKey, nameof(keyPem)); + throw new CryptographicException(SR.Cryptography_X509_NoOrMismatchedPemKey); } private static X509Extension? CreateCustomExtensionIfAny(Oid oid) => diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs index f2fb9300beea04..1382c379e3a5c3 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs @@ -303,7 +303,7 @@ public void ImportFromPem(ReadOnlySpan certPem) || bytesWritten != fields.DecodedDataLength) { Debug.Fail("The contents should have already been validated by the PEM reader."); - throw new ArgumentException(SR.Cryptography_X509_NoPemCertificate, nameof(certPem)); + throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } Import(certBytes); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index a753fa9cc024fa..32db202612a785 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -11,21 +11,21 @@ namespace System.Security.Cryptography.X509Certificates.Tests public static class X509Certificate2PemTests { [Fact] - public static void CreateFromPem_ArgumentException_NoCertificate() + public static void CreateFromPem_CryptographicException_NoCertificate() { - AssertExtensions.Throws("certPem", () => + Assert.Throws(() => X509Certificate2.CreateFromPem(default, default)); } [Fact] - public static void CreateFromPem_ArgumentException_MalformedCertificate() + public static void CreateFromPem_CryptographicException_MalformedCertificate() { const string CertContents = @" -----BEGIN CERTIFICATE----- MII -----END CERTIFICATE----- "; - AssertExtensions.Throws("certPem", () => + Assert.Throws(() => X509Certificate2.CreateFromPem(CertContents, default)); } @@ -39,21 +39,21 @@ public static void CreateFromPem_CryptographicException_InvalidKeyAlgorithm() } [Fact] - public static void CreateFromPem_ArgumentException_NoKey() + public static void CreateFromPem_CryptographicException_NoKey() { - AssertExtensions.Throws("keyPem", () => + Assert.Throws(() => X509Certificate2.CreateFromPem(TestData.RsaCertificate, default)); } [Fact] - public static void CreateFromPem_ArgumentException_MalformedKey() + public static void CreateFromPem_CryptographicException_MalformedKey() { const string CertContents = @" -----BEGIN RSA PRIVATE KEY----- MII -----END RSA PRIVATE KEY----- "; - AssertExtensions.Throws("keyPem", () => + Assert.Throws(() => X509Certificate2.CreateFromPem(TestData.RsaCertificate, CertContents)); } @@ -177,7 +177,7 @@ public static void CreateFromEncryptedPem_Rsa_InvalidPassword_Fail() [Fact] public static void CreateFromEncryptedPem_Rsa_IgnoresUnencryptedPem_Fail() { - AssertExtensions.Throws("keyPem", () => + Assert.Throws(() => X509Certificate2.CreateFromEncryptedPem(TestData.RsaCertificate, TestData.RsaPkcs8Key, "test")); } From 4ee355ea89f0179548a7f2be652b7d5d70df549b Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 14:41:53 -0400 Subject: [PATCH 05/10] Do not permit non-X509 content. --- .../X509Certificates/X509Certificate2.cs | 14 ++++ .../X509Certificate2Collection.cs | 14 ++++ .../tests/Cert.cs | 7 ++ .../tests/CollectionTests.cs | 83 ++++++++++++++----- .../tests/X509Certificate2PemTests.cs | 29 +++++++ 5 files changed, 124 insertions(+), 23 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index 036b4cb57fa0ac..d9ceeeb684a721 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -6,9 +6,11 @@ using Internal.Cryptography.Pal; using System; using System.Diagnostics; +using System.Formats.Asn1; using System.IO; using System.Runtime.Serialization; using System.Security; +using System.Security.Cryptography.X509Certificates.Asn1; using System.Text; namespace System.Security.Cryptography.X509Certificates @@ -922,6 +924,18 @@ private static X509Certificate2 ExtractCertificateFromPem(ReadOnlySpan cer throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } + try + { + // Check that the contents are actually an X509 DER encoded + // certificate, not something else that the constructor will + // will otherwise be able to figure out. + CertificateAsn.Decode(certBytes, AsnEncodingRules.DER); + } + catch (CryptographicException) + { + throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); + } + return new X509Certificate2(certBytes); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs index 1382c379e3a5c3..ce00b573a7f86e 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs @@ -6,6 +6,8 @@ using Internal.Cryptography.Pal; using Microsoft.Win32.SafeHandles; using System.Diagnostics; +using System.Formats.Asn1; +using System.Security.Cryptography.X509Certificates.Asn1; namespace System.Security.Cryptography.X509Certificates { @@ -306,6 +308,18 @@ public void ImportFromPem(ReadOnlySpan certPem) throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } + try + { + // Check that the contents are actually an X509 DER encoded + // certificate, not something else that the constructor will + // will otherwise be able to figure out. + CertificateAsn.Decode(certBytes, AsnEncodingRules.DER); + } + catch (CryptographicException) + { + throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); + } + Import(certBytes); added++; } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/Cert.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/Cert.cs index c2c6572d4d8d53..a52a0319ccb533 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/Cert.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/Cert.cs @@ -56,6 +56,13 @@ public static ImportedCollection Import(string fileName, string password, X509Ke collection.Import(fileName, password, keyStorageFlags); return new ImportedCollection(collection); } + + public static ImportedCollection ImportFromPem(ReadOnlySpan certPem) + { + X509Certificate2Collection collection = new X509Certificate2Collection(); + collection.ImportFromPem(certPem); + return new ImportedCollection(collection); + } } // diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index 241156219830b3..c232895651fd20 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -1430,50 +1430,87 @@ public static void SerializedCertDisposeDoesNotRemoveKeyFile() [Fact] public static void ImportFromPem_SingleCertificate_Success() { - X509Certificate2Collection cc = new X509Certificate2Collection(); - cc.ImportFromPem(TestData.ECDsaCertificate); - Assert.Single(cc); - Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cc[0].Thumbprint); + using(ImportedCollection ic = Cert.ImportFromPem(TestData.ECDsaCertificate)) + { + Assert.Single(ic.Collection); + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", ic.Collection[0].Thumbprint); + } } [Fact] public static void ImportFromPem_SingleCertificate_IgnoresUnrelatedPems_Success() { string pemAggregate = TestData.ECDsaPkcs8Key + TestData.ECDsaCertificate; - X509Certificate2Collection cc = new X509Certificate2Collection(); - cc.ImportFromPem(pemAggregate); - Assert.Single(cc); - Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cc[0].Thumbprint); + + using(ImportedCollection ic = Cert.ImportFromPem(pemAggregate)) + { + Assert.Single(ic.Collection); + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", ic.Collection[0].Thumbprint); + } } [Fact] public static void ImportFromPem_MultiplePems_Success() { string pemAggregate = TestData.RsaCertificate + TestData.ECDsaCertificate; - X509Certificate2Collection cc = new X509Certificate2Collection(); - cc.ImportFromPem(pemAggregate); - Assert.Equal(2, cc.Count); - Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cc[0].Thumbprint); - Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", cc[1].Thumbprint); + + using(ImportedCollection ic = Cert.ImportFromPem(pemAggregate)) + { + Assert.Equal(2, ic.Collection.Count); + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", ic.Collection[0].Thumbprint); + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", ic.Collection[1].Thumbprint); + } } [Fact] public static void ImportFromPem_Exception_AllOrNothing() + { + using(ImportedCollection ic = Cert.ImportFromPem(TestData.DsaCertificate)) + { + StringBuilder builder = new StringBuilder(); + builder.AppendLine(TestData.RsaCertificate); + builder.AppendLine(@" + -----BEGIN CERTIFICATE----- + MIII + -----END CERTIFICATE-----"); + builder.AppendLine(TestData.ECDsaCertificate); + + Assert.ThrowsAny(() => ic.Collection.ImportFromPem(builder.ToString())); + Assert.Single(ic.Collection); + Assert.Equal("35052C549E4E7805E4EA204C2BE7F4BC19B88EC8", ic.Collection[0].Thumbprint); + } + } + + [Fact] + public static void ImportFromPem_NonCertificateContent_Pkcs12_Fails() + { + X509Certificate2Collection cc = new X509Certificate2Collection(); + + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.RsaCertificate, TestData.RsaPkcs1Key)) + { + string content = Convert.ToBase64String(cert.Export(X509ContentType.Pkcs12)); + string certContents = $@" +-----BEGIN CERTIFICATE----- +{content} +-----END CERTIFICATE----- +"; + Assert.Throws(() => cc.ImportFromPem(certContents)); + } + } + + [Fact] + public static void ImportFromPem_NonCertificateContent_Pkcs7_Fails() { X509Certificate2Collection cc = new X509Certificate2Collection(); - cc.ImportFromPem(TestData.DsaCertificate); - StringBuilder builder = new StringBuilder(); - builder.AppendLine(TestData.RsaCertificate); - builder.AppendLine(@" + string content = Convert.ToBase64String(TestData.Pkcs7ChainDerBytes); + string certContents = $@" -----BEGIN CERTIFICATE----- -MIII ------END CERTIFICATE-----"); - builder.AppendLine(TestData.ECDsaCertificate); +{content} +-----END CERTIFICATE----- +"; - Assert.ThrowsAny(() => cc.ImportFromPem(builder.ToString())); - Assert.Single(cc); - Assert.Equal("35052C549E4E7805E4EA204C2BE7F4BC19B88EC8", cc[0].Thumbprint); + Assert.Throws(() => cc.ImportFromPem(certContents)); } private static void TestExportSingleCert_SecureStringPassword(X509ContentType ct) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index 32db202612a785..a5be36d92d9117 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -57,6 +57,35 @@ public static void CreateFromPem_CryptographicException_MalformedKey() X509Certificate2.CreateFromPem(TestData.RsaCertificate, CertContents)); } + [Fact] + public static void CreateFromPem_CryptographicException_CertIsPfx() + { + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.RsaCertificate, TestData.RsaPkcs1Key)) + { + string content = Convert.ToBase64String(cert.Export(X509ContentType.Pkcs12)); + string certContents = $@" +-----BEGIN CERTIFICATE----- +{content} +-----END CERTIFICATE----- +"; + Assert.Throws(() => + X509Certificate2.CreateFromPem(certContents, TestData.RsaPkcs1Key)); + } + } + + [Fact] + public static void CreateFromPem_CryptographicException_CertIsPkcs7() + { + string content = Convert.ToBase64String(TestData.Pkcs7ChainDerBytes); + string certContents = $@" +-----BEGIN CERTIFICATE----- +{content} +-----END CERTIFICATE----- +"; + Assert.Throws(() => + X509Certificate2.CreateFromPem(certContents, TestData.RsaPkcs1Key)); + } + [Fact] public static void CreateFromPem_Rsa_Pkcs1_Success() { From 2fcab92a3305c190b612fcc610d6f70a728fcc51 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 14:53:58 -0400 Subject: [PATCH 06/10] Less brittle key comparison. --- .../tests/X509Certificate2PemTests.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index a5be36d92d9117..6973936ad5a547 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -294,9 +294,25 @@ private static void AssertKeysMatch(string keyPem, Func keyLoader, string alg.ImportFromEncryptedPem(keyPem, password); } - ReadOnlySpan pemPkcs8 = alg.ExportPkcs8PrivateKey(); - ReadOnlySpan keyPkcs8 = key.ExportPkcs8PrivateKey(); - Assert.True(pemPkcs8.SequenceEqual(keyPkcs8)); + byte[] data = alg.ExportPkcs8PrivateKey(); + + switch ((alg, key)) + { + case (RSA rsa, RSA rsaPem): + byte[] rsaSignature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + Assert.True(rsaPem.VerifyData(data, rsaSignature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); + break; + case (ECDsa ecdsa, ECDsa ecdsaPem): + byte[] ecdsaSignature = ecdsa.SignData(data, HashAlgorithmName.SHA256); + Assert.True(ecdsaPem.VerifyData(data, ecdsaSignature, HashAlgorithmName.SHA256)); + break; + case (DSA dsa, DSA dsaPem): + byte[] dsaSignature = dsa.SignData(data, HashAlgorithmName.SHA256); + Assert.True(dsaPem.VerifyData(data, dsaSignature, HashAlgorithmName.SHA256)); + break; + default: + throw new CryptographicException("Unknown key algorithm"); + } } } } From 07546eca7ec09bbacf5b33eb141fc6f90ce74c29 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 15:29:58 -0400 Subject: [PATCH 07/10] Test file-based PEM loading. --- .../tests/Cert.cs | 7 ++ .../tests/CollectionTests.cs | 23 ++++ ...Cryptography.X509Certificates.Tests.csproj | 1 + .../tests/TempFileHolder.cs | 36 ++++++ .../tests/X509Certificate2PemTests.cs | 110 ++++++++++++++++++ 5 files changed, 177 insertions(+) create mode 100644 src/libraries/System.Security.Cryptography.X509Certificates/tests/TempFileHolder.cs diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/Cert.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/Cert.cs index a52a0319ccb533..b4c535bccf3ddb 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/Cert.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/Cert.cs @@ -63,6 +63,13 @@ public static ImportedCollection ImportFromPem(ReadOnlySpan certPem) collection.ImportFromPem(certPem); return new ImportedCollection(collection); } + + public static ImportedCollection ImportFromPemFile(string certPemFilePath) + { + X509Certificate2Collection collection = new X509Certificate2Collection(); + collection.ImportFromPemFile(certPemFilePath); + return new ImportedCollection(collection); + } } // diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs index c232895651fd20..ffb8241b0d31f0 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs @@ -1462,6 +1462,29 @@ public static void ImportFromPem_MultiplePems_Success() } } + [Fact] + public static void ImportFromPemFile_MultiplePems_Success() + { + string pemAggregate = TestData.RsaCertificate + TestData.ECDsaCertificate; + + using (TempFileHolder aggregatePemFile = new TempFileHolder(pemAggregate)) + using(ImportedCollection ic = Cert.ImportFromPemFile(aggregatePemFile.FilePath)) + { + Assert.Equal(2, ic.Collection.Count); + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", ic.Collection[0].Thumbprint); + Assert.Equal("E844FA74BC8DCE46EF4F8605EA00008F161AB56F", ic.Collection[1].Thumbprint); + } + } + + [Fact] + public static void ImportFromPemFile_Null_Throws() + { + X509Certificate2Collection cc = new X509Certificate2Collection(); + + AssertExtensions.Throws("certPemFilePath", () => + cc.ImportFromPemFile(null)); + } + [Fact] public static void ImportFromPem_Exception_AllOrNothing() { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj index a6b0a52c5a5088..c5cd7e39fe0082 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj @@ -34,6 +34,7 @@ + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TempFileHolder.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TempFileHolder.cs new file mode 100644 index 00000000000000..a32d89b8d38651 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TempFileHolder.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + internal sealed class TempFileHolder : IDisposable + { + public string FilePath { get; } + + public TempFileHolder(ReadOnlySpan content) + { + FilePath = Path.GetTempFileName(); + + using (StreamWriter writer = new StreamWriter(FilePath, append: false)) + { + writer.Write(content); + } + } + + public void Dispose() + { + try + { + File.Delete(FilePath); + } + catch + { + // Best effort + } + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index 6973936ad5a547..093a860e087cc7 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -270,6 +270,116 @@ public static void CreateFromPem_Dsa_EncryptedPkcs8_Success() } } + [Fact] + public static void CreateFromPemFile_NoKeyFile_Rsa_Success() + { + string pemAggregate = TestData.RsaCertificate + TestData.RsaPkcs1Key; + + using (TempFileHolder certAndKey = new TempFileHolder(pemAggregate)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPemFile(certAndKey.FilePath)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPemFile_SameFile_Rsa_Success() + { + using (TempFileHolder aggregatePem = new TempFileHolder(TestData.RsaCertificate + TestData.RsaPkcs1Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPemFile(aggregatePem.FilePath, aggregatePem.FilePath)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPemFile_WithKeyFile_Rsa_Success() + { + using (TempFileHolder certPem = new TempFileHolder(TestData.RsaCertificate)) + using (TempFileHolder keyPem = new TempFileHolder(TestData.RsaPkcs1Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPemFile(certPem.FilePath, keyPem.FilePath)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPemFile_PrefersKeyFromKeyFile_Success() + { + using (TempFileHolder certPem = new TempFileHolder(TestData.RsaCertificate + TestData.OtherRsaPkcs1Key)) + using (TempFileHolder keyPem = new TempFileHolder(TestData.RsaPkcs1Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPemFile(certPem.FilePath, keyPem.FilePath)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(TestData.RsaPkcs1Key, cert.GetRSAPrivateKey); + } + } + + [Fact] + public static void CreateFromPemFile_Null_Throws() + { + AssertExtensions.Throws("certPemFilePath", () => + X509Certificate2.CreateFromPemFile(null)); + } + + [Fact] + public static void CreateFromEncryptedPemFile_NoKeyFile_Rsa_Success() + { + string pemAggregate = TestData.RsaCertificate + TestData.RsaEncryptedPkcs8Key; + + using (TempFileHolder certAndKey = new TempFileHolder(pemAggregate)) + using (X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPemFile(certAndKey.FilePath, "test")) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(TestData.RsaEncryptedPkcs8Key, cert.GetRSAPrivateKey, "test"); + } + } + + [Fact] + public static void CreateFromEncryptedPemFile_SameFile_Rsa_Success() + { + using (TempFileHolder aggregatePem = new TempFileHolder(TestData.RsaCertificate + TestData.RsaEncryptedPkcs8Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPemFile(aggregatePem.FilePath, "test", aggregatePem.FilePath)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(TestData.RsaEncryptedPkcs8Key, cert.GetRSAPrivateKey, "test"); + } + } + + [Fact] + public static void CreateFromEncryptedPemFile_WithKeyFile_Rsa_Success() + { + using (TempFileHolder certPem = new TempFileHolder(TestData.RsaCertificate)) + using (TempFileHolder keyPem = new TempFileHolder(TestData.RsaEncryptedPkcs8Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPemFile(certPem.FilePath, "test", keyPem.FilePath)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(TestData.RsaEncryptedPkcs8Key, cert.GetRSAPrivateKey, "test"); + } + } + + [Fact] + public static void CreateFromEncryptedPemFile_PrefersKeyFromKeyFile_Success() + { + using (TempFileHolder certPem = new TempFileHolder(TestData.RsaCertificate + TestData.OtherRsaPkcs1Key)) + using (TempFileHolder keyPem = new TempFileHolder(TestData.RsaEncryptedPkcs8Key)) + using (X509Certificate2 cert = X509Certificate2.CreateFromEncryptedPemFile(certPem.FilePath, "test", keyPem.FilePath)) + { + Assert.Equal("A33348E44A047A121F44E810E888899781E1FF19", cert.Thumbprint); + AssertKeysMatch(TestData.RsaEncryptedPkcs8Key, cert.GetRSAPrivateKey, "test"); + } + } + + [Fact] + public static void CreateFromEncryptedPemFile_Null_Throws() + { + AssertExtensions.Throws("certPemFilePath", () => + X509Certificate2.CreateFromEncryptedPemFile(null, default)); + } + private static void AssertKeysMatch(string keyPem, Func keyLoader, string password = null) where T : AsymmetricAlgorithm { AsymmetricAlgorithm key = keyLoader(); From d2f9f90b2016d07af764df3111197e04aff58dde Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 16:50:30 -0400 Subject: [PATCH 08/10] Use SHA1 for DSA to support macOS. --- .../tests/X509Certificate2PemTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index 093a860e087cc7..c0db76e6f549d5 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -417,8 +417,8 @@ private static void AssertKeysMatch(string keyPem, Func keyLoader, string Assert.True(ecdsaPem.VerifyData(data, ecdsaSignature, HashAlgorithmName.SHA256)); break; case (DSA dsa, DSA dsaPem): - byte[] dsaSignature = dsa.SignData(data, HashAlgorithmName.SHA256); - Assert.True(dsaPem.VerifyData(data, dsaSignature, HashAlgorithmName.SHA256)); + byte[] dsaSignature = dsa.SignData(data, HashAlgorithmName.SHA1); + Assert.True(dsaPem.VerifyData(data, dsaSignature, HashAlgorithmName.SHA1)); break; default: throw new CryptographicException("Unknown key algorithm"); From ae8f60bebe18a877afc163c89fa230069ab89d9b Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 21:19:45 -0400 Subject: [PATCH 09/10] Use CryptographicException for key mismatches. --- .../src/Resources/Strings.resx | 2 +- .../X509Certificates/X509Certificate2.cs | 82 +++++++++---------- .../tests/TestData.cs | 32 ++++++++ .../tests/X509Certificate2PemTests.cs | 13 ++- 4 files changed, 84 insertions(+), 45 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx index 8b67e79c56ac9f..3c1833771980e9 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx @@ -296,7 +296,7 @@ The certificate contents do not contain a PEM with a CERTIFICATE label, or the content is malformed. - The key contents do not contain a PEM, the content is malformed, or the key does not match the certificate algorithm. + The key contents do not contain a PEM, the content is malformed, or the key does not match the certificate. Enumeration has not started. Call MoveNext. diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index d9ceeeb684a721..14d8790ba09efe 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -811,26 +811,13 @@ public static X509Certificate2 CreateFromPem(ReadOnlySpan certPem, ReadOnl { string keyAlgorithm = certificate.GetKeyAlgorithm(); - switch (keyAlgorithm) + return keyAlgorithm switch { - case Oids.Rsa: - using (RSA key = ExtractKeyFromPem(keyPem, s_RsaPublicKeyPrivateKeyLabels, RSA.Create)) - { - return certificate.CopyWithPrivateKey(key); - } - case Oids.EcPublicKey: - using (ECDsa key = ExtractKeyFromPem(keyPem, s_EcPublicKeyPrivateKeyLabels, ECDsa.Create)) - { - return certificate.CopyWithPrivateKey(key); - } - case Oids.Dsa: - using (DSA key = ExtractKeyFromPem(keyPem, s_DsaPublicKeyPrivateKeyLabels, DSA.Create)) - { - return certificate.CopyWithPrivateKey(key); - } - default: - throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, keyAlgorithm)); - } + Oids.Rsa => ExtractKeyFromPem(keyPem, s_RsaPublicKeyPrivateKeyLabels, RSA.Create, certificate.CopyWithPrivateKey), + Oids.Dsa => ExtractKeyFromPem(keyPem, s_DsaPublicKeyPrivateKeyLabels, DSA.Create, certificate.CopyWithPrivateKey), + Oids.EcPublicKey => ExtractKeyFromPem(keyPem, s_EcPublicKeyPrivateKeyLabels, ECDsa.Create, certificate.CopyWithPrivateKey), + _ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, keyAlgorithm)), + }; } } @@ -883,26 +870,13 @@ public static X509Certificate2 CreateFromEncryptedPem(ReadOnlySpan certPem { string keyAlgorithm = certificate.GetKeyAlgorithm(); - switch (keyAlgorithm) + return keyAlgorithm switch { - case Oids.Rsa: - using (RSA key = ExtractKeyFromEncryptedPem(keyPem, password, RSA.Create)) - { - return certificate.CopyWithPrivateKey(key); - } - case Oids.EcPublicKey: - using (ECDsa key = ExtractKeyFromEncryptedPem(keyPem, password, ECDsa.Create)) - { - return certificate.CopyWithPrivateKey(key); - } - case Oids.Dsa: - using (DSA key = ExtractKeyFromEncryptedPem(keyPem, password, DSA.Create)) - { - return certificate.CopyWithPrivateKey(key); - } - default: - throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, keyAlgorithm)); - } + Oids.Rsa => ExtractKeyFromEncryptedPem(keyPem, password, RSA.Create, certificate.CopyWithPrivateKey), + Oids.Dsa => ExtractKeyFromEncryptedPem(keyPem, password, DSA.Create, certificate.CopyWithPrivateKey), + Oids.EcPublicKey => ExtractKeyFromEncryptedPem(keyPem, password, ECDsa.Create, certificate.CopyWithPrivateKey), + _ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownKeyAlgorithm, keyAlgorithm)), + }; } } @@ -943,7 +917,11 @@ private static X509Certificate2 ExtractCertificateFromPem(ReadOnlySpan cer throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } - private static TAlg ExtractKeyFromPem(ReadOnlySpan keyPem, string[] labels, Func factory) where TAlg : AsymmetricAlgorithm + private static X509Certificate2 ExtractKeyFromPem( + ReadOnlySpan keyPem, + string[] labels, + Func factory, + Func import) where TAlg : AsymmetricAlgorithm { foreach ((ReadOnlySpan contents, PemFields fields) in new PemEnumerator(keyPem)) { @@ -955,7 +933,15 @@ private static TAlg ExtractKeyFromPem(ReadOnlySpan keyPem, string[] { TAlg key = factory(); key.ImportFromPem(contents[fields.Location]); - return key; + + try + { + return import(key); + } + catch (ArgumentException ae) + { + throw new CryptographicException(SR.Cryptography_X509_NoOrMismatchedPemKey, ae); + } } } } @@ -963,10 +949,11 @@ private static TAlg ExtractKeyFromPem(ReadOnlySpan keyPem, string[] throw new CryptographicException(SR.Cryptography_X509_NoOrMismatchedPemKey); } - private static TAlg ExtractKeyFromEncryptedPem( + private static X509Certificate2 ExtractKeyFromEncryptedPem( ReadOnlySpan keyPem, ReadOnlySpan password, - Func factory) where TAlg : AsymmetricAlgorithm + Func factory, + Func import) where TAlg : AsymmetricAlgorithm { foreach ((ReadOnlySpan contents, PemFields fields) in new PemEnumerator(keyPem)) { @@ -976,7 +963,16 @@ private static TAlg ExtractKeyFromEncryptedPem( { TAlg key = factory(); key.ImportFromEncryptedPem(contents[fields.Location], password); - return key; + + try + { + return import(key); + } + catch (ArgumentException ae) + { + throw new CryptographicException(SR.Cryptography_X509_NoOrMismatchedPemKey, ae); + } + } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs index e4e3f345e8f9fa..506eb4ad4736a7 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs @@ -2001,6 +2001,38 @@ internal static DSAParameters GetDSA1024Params() w4qGwpDllP58gBySGj3BQS8PG8d9fuOlCSfOQIW10dE8U9H0wwlE -----END RSA PRIVATE KEY-----"; + public const string OtherRsaPkcs8EncryptedKey = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFGTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQICs3NqD8hVe0CAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECBLUM63bM9vSBIIEyAk8lJcgxlkclPR0 +9BlUXGQA/ll6zCQa1DDiXdrLxTKuuedwOv0YF3X/iiZlhpey0E8zjKo13HQt7P+w +rpYVvA+qHH+4k0oc/ZC4ZD/ml4AIKCpQGs0AX8gQXJ9z8heFbCGAIYe2O8sx+Teh +V9YNbhzXiZ521+pPpMgzlrfeDFVimjCEw9ESNuHl1nJaHLzeC+S6TL5uXNJCy3BF +XwNYH53GfSIWwWcxiDKdHu0uI0pTUgCUKRgm4qWxqQvbO+dd+pBvqa/nrd5Yk8Sa +vSUmsSHBHiMnYoEs2guD3BD6YIzgzGQ3dR9LWCJNCh7BavnYoGFPCre79RiA+Wdy +8Sxd+irf12JESZdQPsUSAiMBHtZ3gpsvO1bA+2xwSqE0EcltHdrDyUoLzUAx+2yU +0uvJxRlchYsNLAkDcezOWA1W4uisNBQCXxklSLIVRk7zKvAtZ5vLm0fvybCYoHyH +xPTPs3k16xBF5lsquGF/XHcxfKjB2s8oMDRU/7ZnOSjCOyZV/Ey07ZkjhU5GbyxI +ninu5h7q2K79GWOm3B4LaEzl3IYFCFifJqod1UyI08fv3l2x5GdGlEL9Hdvl1nAW +Ty4JnhvvJR4VdGGOczJNDv5/ZDirN4wpW5Q5QUnbedIZjiTugxHC81Q1JxMXfFPs +iDLCVXAU4oZwfkQYJYbwvR/5Zu973zFRIjsp2cJgdN8XsS5mFUD69cnGnZaHEMJy +o+Mki2VMF6QiimVS4RezJFQcdglrY4Uy16J4indJFGIjJez78M1+Wdr0iDk5akOr +pln+Dj5yim3IG5+hfY5Q/jkbuPMSUkfsLhGAHrxKCLcJSj6+FYV4/Mk7lpC7WPND +xkVTYae0XDOAKGzXZ2fvS7ORk5ZJDn0RKoHEv31QXpIP4fyzM54x0tknnxlnI++J +erLOgEnl0r9F0uM4Lw/JqYRhfjrroMUgGxDWSHwGtHkanhjQNAKTY4zLp830HZY/ +yii7+Tt7q29/P4R0cHUcnqdz77re/zYZK7ru63UNZwnE6hybG4c9vneCVOT6bmxG +Acxu2L6nVpUhxhhDQ/K1d0JgFNRg15rQjA/LN5MTfIDimPZAdGhDPey0NLLl2BVG +jc9JRT7KopOh2eypWcXecNCHqoDAKcHdAi71OIZRKEQS8BGVk/LY55iwdgKqkqLF ++iuWtEfgDD9aekOBKLdTQqn8PvvHc3+QOFIoDzQ7iJLh7Uca6MUqK0uwBieDnK7B +8b9NLs2Uz1Rr4UqK+r+06QUQ/kOldgeLguouOt9fz+ugZNxfvDGXSbkzMXQbsSJ4 +EFs7DHD/76GXsgN6p95LeCdR1jGgFV/HlD+pdNUW5A+P3hs02UqvkWzeC4ZOBnLX +/oLjja5XWuxuYkyk0yE4UP0CzJMnrVwzKeqesrWKSP42miwSxPzSY4AY8pZnYY4Y +iOGN5vueX9hkfRmsrrud6rujPuS49ho+UkJuygmR9ElGktvzw1NbouhoaVsFyrRV +cEL42Y0WZVuUrNcm3X8ng5HkT2L5D9Rys3WV0GaaxximLzGzuhIsfG3cCiLq+ZT5 +XOmtog9dZs8lAIM58M/a/7q7dP0+FGxQ41/JslOjbWY5zypViy3aZssrkVF+7xZ/ +oWZazn8MBIuVRT4TcA== +-----END ENCRYPTED PRIVATE KEY-----"; + public const string DsaCertificate = @" -----BEGIN CERTIFICATE----- MIIDWTCCAxWgAwIBAgIUFRQGA90GHC74cNK/hNzQDi7XJFYwCwYJYIZIAWUDBAMC diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs index c0db76e6f549d5..34803447ff4049 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509Certificate2PemTests.cs @@ -175,8 +175,10 @@ public static void CreateFromPem_Rsa_IgnoresPkcs8PublicKey_Success() [Fact] public static void CreateFromPem_Rsa_KeyMismatch_Fail() { - AssertExtensions.Throws("privateKey", () => + CryptographicException ce = AssertExtensions.Throws(() => X509Certificate2.CreateFromPem(TestData.RsaCertificate, TestData.OtherRsaPkcs1Key)); + + Assert.IsType(ce.InnerException); } [Fact] @@ -194,6 +196,15 @@ public static void CreateFromEncryptedPem_Rsa_Success() } } + [Fact] + public static void CreateFromEncryptedPem_Rsa_KeyMismatch_Fail() + { + CryptographicException ce = AssertExtensions.Throws(() => + X509Certificate2.CreateFromEncryptedPem(TestData.RsaCertificate, TestData.OtherRsaPkcs8EncryptedKey, "test")); + + Assert.IsType(ce.InnerException); + } + [Fact] public static void CreateFromEncryptedPem_Rsa_InvalidPassword_Fail() { From e99eb2a6b4e2567f9053f9d5babf4cc4f1d7f79a Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Jun 2020 21:22:11 -0400 Subject: [PATCH 10/10] Correct exception type in XML docs. --- .../Security/Cryptography/X509Certificates/X509Certificate2.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index 14d8790ba09efe..ef2bdce23f1123 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -645,7 +645,7 @@ public bool Verify() /// the private key. /// /// A new certificate with the private key. - /// + /// /// /// The contents of the file path in do not contain /// a PEM-encoded certificate, or it is malformed.