diff --git a/.config/CredScanSuppressions.json b/.config/CredScanSuppressions.json index 31bd3c0d81060..d8861add608c5 100644 --- a/.config/CredScanSuppressions.json +++ b/.config/CredScanSuppressions.json @@ -17,6 +17,7 @@ "/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs", "/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs", "/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs", + "/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs", "/src/libraries/System.Data.Common/tests/System/Data/Common/DbConnectionStringBuilderTest.cs", "/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs", "/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/constants.cs", diff --git a/src/libraries/Common/src/Internal/Cryptography/PemKeyImportHelpers.cs b/src/libraries/Common/src/Internal/Cryptography/PemKeyImportHelpers.cs new file mode 100644 index 0000000000000..4a6c1ea13ced4 --- /dev/null +++ b/src/libraries/Common/src/Internal/Cryptography/PemKeyImportHelpers.cs @@ -0,0 +1,170 @@ +// 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. + +#nullable enable +using System; +using System.Diagnostics; +using System.Security.Cryptography; + +namespace Internal.Cryptography +{ + internal static class PemKeyImportHelpers + { + public delegate void ImportKeyAction(ReadOnlySpan source, out int bytesRead); + public delegate ImportKeyAction? FindImportActionFunc(ReadOnlySpan label); + public delegate void ImportEncryptedKeyAction( + ReadOnlySpan password, + ReadOnlySpan source, + out int bytesRead); + + public static void ImportEncryptedPem( + ReadOnlySpan input, + ReadOnlySpan password, + ImportEncryptedKeyAction importAction) + { + bool foundEncryptedPem = false; + PemFields foundFields = default; + ReadOnlySpan foundSlice = default; + + ReadOnlySpan pem = input; + while (PemEncoding.TryFind(pem, out PemFields fields)) + { + ReadOnlySpan label = pem[fields.Label]; + + if (label.SequenceEqual(PemLabels.EncryptedPkcs8PrivateKey)) + { + if (foundEncryptedPem) + { + throw new ArgumentException(SR.Argument_PemImport_AmbiguousPem, nameof(input)); + } + + foundEncryptedPem = true; + foundFields = fields; + foundSlice = pem; + } + + Index offset = fields.Location.End; + pem = pem[offset..]; + } + + if (!foundEncryptedPem) + { + throw new ArgumentException(SR.Argument_PemImport_NoPemFound, nameof(input)); + } + + ReadOnlySpan base64Contents = foundSlice[foundFields.Base64Data]; + int base64size = foundFields.DecodedDataLength; + byte[] decodeBuffer = CryptoPool.Rent(base64size); + int bytesWritten = 0; + + try + { + if (!Convert.TryFromBase64Chars(base64Contents, decodeBuffer, out bytesWritten)) + { + // Couldn't decode base64. We shouldn't get here since the + // contents are pre-validated. + Debug.Fail("Base64 decoding failed on already validated contents."); + throw new ArgumentException(); + } + + Debug.Assert(bytesWritten == base64size); + Span decodedBase64 = decodeBuffer.AsSpan(0, bytesWritten); + + // Don't need to check the bytesRead here. We're already operating + // on an input which is already a parsed subset of the input. + importAction(password, decodedBase64, out _); + } + finally + { + CryptoPool.Return(decodeBuffer, clearSize: bytesWritten); + } + } + + public static void ImportPem(ReadOnlySpan input, FindImportActionFunc callback) + { + ImportKeyAction? importAction = null; + PemFields foundFields = default; + ReadOnlySpan foundSlice = default; + bool containsEncryptedPem = false; + + ReadOnlySpan pem = input; + while (PemEncoding.TryFind(pem, out PemFields fields)) + { + ReadOnlySpan label = pem[fields.Label]; + ImportKeyAction? action = callback(label); + + // Caller knows how to handle this PEM by label. + if (action != null) + { + // There was a previous PEM that could have been handled, + // which means this is ambiguous and contains multiple + // importable keys. Or, this contained an encrypted PEM. + // For purposes of encrypted PKCS8 with another actionable + // PEM, we will throw a duplicate exception. + if (importAction != null || containsEncryptedPem) + { + throw new ArgumentException(SR.Argument_PemImport_AmbiguousPem, nameof(input)); + } + + importAction = action; + foundFields = fields; + foundSlice = pem; + } + else if (label.SequenceEqual(PemLabels.EncryptedPkcs8PrivateKey)) + { + if (importAction != null || containsEncryptedPem) + { + throw new ArgumentException(SR.Argument_PemImport_AmbiguousPem, nameof(input)); + } + + containsEncryptedPem = true; + } + + Index offset = fields.Location.End; + pem = pem[offset..]; + } + + // The only PEM found that could potentially be used is encrypted PKCS8, + // but we won't try to import it with a null or blank password, so + // throw. + if (containsEncryptedPem) + { + throw new ArgumentException(SR.Argument_PemImport_EncryptedPem, nameof(input)); + } + + // We went through the PEM and found nothing that could be handled. + if (importAction is null) + { + throw new ArgumentException(SR.Argument_PemImport_NoPemFound, nameof(input)); + } + + ReadOnlySpan base64Contents = foundSlice[foundFields.Base64Data]; + int base64size = foundFields.DecodedDataLength; + byte[] decodeBuffer = CryptoPool.Rent(base64size); + int bytesWritten = 0; + + try + { + if (!Convert.TryFromBase64Chars(base64Contents, decodeBuffer, out bytesWritten)) + { + // Couldn't decode base64. We shouldn't get here since the + // contents are pre-validated. + Debug.Fail("Base64 decoding failed on already validated contents."); + throw new ArgumentException(); + } + + Debug.Assert(bytesWritten == base64size); + Span decodedBase64 = decodeBuffer.AsSpan(0, bytesWritten); + + // Don't need to check the bytesRead here. We're already operating + // on an input which is already a parsed subset of the input. + importAction(decodedBase64, out _); + } + finally + { + CryptoPool.Return(decodeBuffer, clearSize: bytesWritten); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs b/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs new file mode 100644 index 0000000000000..5adf4ab5d83d9 --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/PemLabels.cs @@ -0,0 +1,16 @@ +// 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 +{ + internal static class PemLabels + { + internal const string Pkcs8PrivateKey = "PRIVATE KEY"; + internal const string EncryptedPkcs8PrivateKey = "ENCRYPTED PRIVATE KEY"; + internal const string SpkiPublicKey = "PUBLIC KEY"; + internal const string RsaPublicKey = "RSA PUBLIC KEY"; + internal const string RsaPrivateKey = "RSA PRIVATE KEY"; + internal const string EcPrivateKey = "EC PRIVATE KEY"; + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs new file mode 100644 index 0000000000000..6029383756cac --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs @@ -0,0 +1,379 @@ +// 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.Text; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Dsa.Tests +{ + public static class DSAKeyPemTests + { + private const string AmbiguousExceptionMarker = "multiple keys"; + private const string EncryptedExceptionMarker = "encrypted key"; + private const string NoPemExceptionMarker = "No supported key"; + + [Fact] + public static void ImportFromPem_NoPem() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = "pem? what pem? there is no pem here."; + ArgumentException ae = AssertExtensions.Throws("input", () => dsa.ImportFromPem(pem)); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8UnEncrypted_Simple() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY-----"; + dsa.ImportFromPem(pem); + DSAParameters dsaParameters = dsa.ExportParameters(true); + + DSAImportExport.AssertKeyEquals(DSATestData.Dsa512Parameters, dsaParameters); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8UnEncrypted_IgnoresUnrelatedAlgorithm() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49 +AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS +NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END EC PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY-----"; + dsa.ImportFromPem(pem); + DSAParameters dsaParameters = dsa.ExportParameters(true); + + DSAImportExport.AssertKeyEquals(DSATestData.Dsa512Parameters, dsaParameters); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8_UnrelatedPrecedingPem() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN CERTIFICATE----- +MII= +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY-----"; + dsa.ImportFromPem(pem); + DSAParameters dsaParameters = dsa.ExportParameters(true); + + DSAImportExport.AssertKeyEquals(DSATestData.Dsa512Parameters, dsaParameters); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8_PrecedingMalformedPem() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN CERTIFICATE----- +$$$ BAD PEM +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY-----"; + dsa.ImportFromPem(pem); + DSAParameters dsaParameters = dsa.ExportParameters(true); + + DSAImportExport.AssertKeyEquals(DSATestData.Dsa512Parameters, dsaParameters); + } + } + + [Fact] + public static void ImportFromPem_SubjectPublicKeyInfo_Simple() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN PUBLIC KEY----- +MIHxMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWNIHRn +QNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV1eFk +MKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6fve7 +7OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorA0QAAkEAwwDg5n2HfmztOf7q +qsHywr1WjmoyRnIn4Stq5FqNlHhUGkgKyAA4qshjgn1uOYQGGiWQXBi9JJmoOWY8 +PKRWBQ== +-----END PUBLIC KEY-----"; + dsa.ImportFromPem(pem); + DSAParameters dsaParameters = dsa.ExportParameters(false); + + DSAImportExport.AssertKeyEquals(DSATestData.Dsa512Parameters.ToPublic(), dsaParameters); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8_AmbiguousKey_Pkcs8() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => dsa.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8_AmbiguousKey_Spki() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN PUBLIC KEY----- +MIHxMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWNIHRn +QNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV1eFk +MKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6fve7 +7OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorA0QAAkEAwwDg5n2HfmztOf7q +qsHywr1WjmoyRnIn4Stq5FqNlHhUGkgKyAA4qshjgn1uOYQGGiWQXBi9JJmoOWY8 +PKRWBQ== +-----END PUBLIC KEY----- +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => dsa.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8_AmbiguousKey_EncryptedPkcs8() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBIDBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIkM/kCKe6rYsCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECBOccveL65bDBIHQiCcCqwxJs93g1+16 +7Gx1D5lL4/nZ94fRa+Hl4nGEX4gmjuxH6pOHKyywwflAyXNTfVhOCP9zBedwENx9 +MGHbpaaShD6iJfoGMRX0frr0mMCtuOOZkkjBF9pSpkhaH0TDSq1PrVLxcM0/S4Vs ++//2uPrP8U+CTW9W7CXCZw698BAuevZRuD0koT2Bn9ErhTiuVZZMcOjtLmN2oXHG +dVYwfovccu8ktEAwk5XAOo0r+5CCw2lDDw/hbDeO87BToC5Cc5nu3F5LxAUj8Flc +v8pi3w== +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => dsa.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_EncryptedPrivateKeyFails() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBIDBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIkM/kCKe6rYsCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECBOccveL65bDBIHQiCcCqwxJs93g1+16 +7Gx1D5lL4/nZ94fRa+Hl4nGEX4gmjuxH6pOHKyywwflAyXNTfVhOCP9zBedwENx9 +MGHbpaaShD6iJfoGMRX0frr0mMCtuOOZkkjBF9pSpkhaH0TDSq1PrVLxcM0/S4Vs ++//2uPrP8U+CTW9W7CXCZw698BAuevZRuD0koT2Bn9ErhTiuVZZMcOjtLmN2oXHG +dVYwfovccu8ktEAwk5XAOo0r+5CCw2lDDw/hbDeO87BToC5Cc5nu3F5LxAUj8Flc +v8pi3w== +-----END ENCRYPTED PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => dsa.ImportFromPem(pem)); + Assert.Contains(EncryptedExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_SpkiAlgorithmMismatch_Throws() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +The below key is for an RSA SPKI +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQx +m5NTLEHDwUd7idstLzPXuah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQ== +-----END PUBLIC KEY-----"; + Assert.Throws(() => dsa.ImportFromPem(pem)); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8_Encrypted_Char_Simple() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBIDBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIkM/kCKe6rYsCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECBOccveL65bDBIHQiCcCqwxJs93g1+16 +7Gx1D5lL4/nZ94fRa+Hl4nGEX4gmjuxH6pOHKyywwflAyXNTfVhOCP9zBedwENx9 +MGHbpaaShD6iJfoGMRX0frr0mMCtuOOZkkjBF9pSpkhaH0TDSq1PrVLxcM0/S4Vs ++//2uPrP8U+CTW9W7CXCZw698BAuevZRuD0koT2Bn9ErhTiuVZZMcOjtLmN2oXHG +dVYwfovccu8ktEAwk5XAOo0r+5CCw2lDDw/hbDeO87BToC5Cc5nu3F5LxAUj8Flc +v8pi3w== +-----END ENCRYPTED PRIVATE KEY-----"; + dsa.ImportFromEncryptedPem(pem, "test"); + DSAParameters dsaParameters = dsa.ExportParameters(true); + + DSAImportExport.AssertKeyEquals(DSATestData.Dsa512Parameters, dsaParameters); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8_Encrypted_Byte_Simple() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBLDBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIfcoipdEY/C4CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBC9heEphj00fB89aP6chSOjBIHQ +HF2RLrIw6654q2hjUdCG4PhhYNXlck0zD0mOuaVQHmnKIKArk/1DSpgSrYnKw6aE +2eujwNdySLLEwUj5l+X/IXwhOnPIZDJqUN7oMagUYJX28gnQmXyDvrt3r16utbpd +ho0YNYGUDSgOs6RxBpw1rJUCnAlHNU09peCjEP+aZSrhsxlejN/GpVS4e0JTmMeo +xTL6VO9mx52x6h5WDAQAisMVeMkBoxQUWLANXiw1zSfVbsmB7mDknsRcvD3tcgMs +7YLD7LQMiPAIjDlOP8XP/w== +-----END ENCRYPTED PRIVATE KEY-----"; + byte[] passwordBytes = Encoding.UTF8.GetBytes("test"); + dsa.ImportFromEncryptedPem(pem, passwordBytes); + DSAParameters dsaParameters = dsa.ExportParameters(true); + + DSAImportExport.AssertKeyEquals(DSATestData.Dsa512Parameters, dsaParameters); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8_Encrypted_AmbiguousPem() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBLDBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIfcoipdEY/C4CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBC9heEphj00fB89aP6chSOjBIHQ +HF2RLrIw6654q2hjUdCG4PhhYNXlck0zD0mOuaVQHmnKIKArk/1DSpgSrYnKw6aE +2eujwNdySLLEwUj5l+X/IXwhOnPIZDJqUN7oMagUYJX28gnQmXyDvrt3r16utbpd +ho0YNYGUDSgOs6RxBpw1rJUCnAlHNU09peCjEP+aZSrhsxlejN/GpVS4e0JTmMeo +xTL6VO9mx52x6h5WDAQAisMVeMkBoxQUWLANXiw1zSfVbsmB7mDknsRcvD3tcgMs +7YLD7LQMiPAIjDlOP8XP/w== +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBIDBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIkM/kCKe6rYsCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECBOccveL65bDBIHQiCcCqwxJs93g1+16 +7Gx1D5lL4/nZ94fRa+Hl4nGEX4gmjuxH6pOHKyywwflAyXNTfVhOCP9zBedwENx9 +MGHbpaaShD6iJfoGMRX0frr0mMCtuOOZkkjBF9pSpkhaH0TDSq1PrVLxcM0/S4Vs ++//2uPrP8U+CTW9W7CXCZw698BAuevZRuD0koT2Bn9ErhTiuVZZMcOjtLmN2oXHG +dVYwfovccu8ktEAwk5XAOo0r+5CCw2lDDw/hbDeO87BToC5Cc5nu3F5LxAUj8Flc +v8pi3w== +-----END ENCRYPTED PRIVATE KEY-----"; + byte[] passwordBytes = Encoding.UTF8.GetBytes("test"); + ArgumentException ae = AssertExtensions.Throws("input", () => + dsa.ImportFromEncryptedPem(pem, passwordBytes)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8_Byte_NoPem() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = ""; + byte[] passwordBytes = Encoding.UTF8.GetBytes("test"); + ArgumentException ae = AssertExtensions.Throws("input", () => + dsa.ImportFromEncryptedPem(pem, passwordBytes)); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8_Char_NoPem() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = ""; + ArgumentException ae = AssertExtensions.Throws("input", () => + dsa.ImportFromEncryptedPem(pem, "")); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8_NoEncryptedPem() + { + using (DSA dsa = DSAFactory.Create()) + { + string pem = @" +-----BEGIN PRIVATE KEY----- +MIHGAgEAMIGoBgcqhkjOOAQBMIGcAkEA1qi38cr3ppZNB2Y/xpHSL2q81Vw3rvWN +IHRnQNgv4U4UY2NifZGSUULc3uOEvgoeBO1b9fRxSG9NmG1CoufflQIVAPq19iXV +1eFkMKHvYw6+M4l8wiT5AkAIRMSQ5S71jgWQLGNtZNHV6yxggqDU87/RzgeOh7Q6 +fve77OGaTv4qbZwinTYAg86p9yHzmwW6+XBS3vxnpYorBBYCFC49eoTIW2Z4Xh9v +55aYKyKwy5i8 +-----END PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => + dsa.ImportFromEncryptedPem(pem, "")); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + private static DSAParameters ToPublic(this DSAParameters dsaParams) + { + dsaParams.X = null; + return dsaParams; + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs new file mode 100644 index 0000000000000..b07c29c6b4544 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs @@ -0,0 +1,438 @@ +// 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.Text; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Tests +{ + public abstract class ECKeyPemTests where TAlg : AsymmetricAlgorithm + { + private const string AmbiguousExceptionMarker = "multiple keys"; + private const string EncryptedExceptionMarker = "encrypted key"; + private const string NoPemExceptionMarker = "No supported key"; + + protected abstract TAlg CreateKey(); + protected abstract ECParameters ExportParameters(TAlg key, bool includePrivateParameters); + + [Fact] + public void ImportFromPem_NoPem() + { + using (TAlg key = CreateKey()) + { + ArgumentException ae = AssertExtensions.Throws("input", () => key.ImportFromPem("")); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromPem_ECPrivateKey_Simple() + { + using (TAlg key = CreateKey()) + { + key.ImportFromPem(@" +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49 +AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS +NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END EC PRIVATE KEY-----"); + ECParameters ecParameters = ExportParameters(key, true); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.AssertEqual(expected, ecParameters); + } + } + + [Fact] + public void ImportFromPem_ECPrivateKey_IgnoresUnrelatedAlgorithm() + { + using (TAlg key = CreateKey()) + { + key.ImportFromPem(@" +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49 +AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS +NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END EC PRIVATE KEY-----"); + ECParameters ecParameters = ExportParameters(key, true); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.AssertEqual(expected, ecParameters); + } + } + + [Fact] + public void ImportFromPem_Pkcs8_Simple() + { + using (TAlg key = CreateKey()) + { + key.ImportFromPem(@" +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcKEsLbFoRe1W/2jP +whpHKz8E19aFG/Y0ny19WzRSs4qhRANCAASBAezkdGSm6tcM9ppuK9PYhpGjJi0i +y6T3Y16v8maAqNihK6YdWZI19n2ctNWPF4PTykPnjwpauqYkB5k2wMOp +-----END PRIVATE KEY-----"); + ECParameters ecParameters = ExportParameters(key, true); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.AssertEqual(expected, ecParameters); + } + } + + [Fact] + public void ImportFromPem_Pkcs8_IgnoresUnrelatedAlgorithm() + { + using (TAlg key = CreateKey()) + { + key.ImportFromPem(@" +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcKEsLbFoRe1W/2jP +whpHKz8E19aFG/Y0ny19WzRSs4qhRANCAASBAezkdGSm6tcM9ppuK9PYhpGjJi0i +y6T3Y16v8maAqNihK6YdWZI19n2ctNWPF4PTykPnjwpauqYkB5k2wMOp +-----END PRIVATE KEY-----"); + ECParameters ecParameters = ExportParameters(key, true); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.AssertEqual(expected, ecParameters); + } + } + + [Fact] + public void ImportFromPem_Spki_Simple() + { + using (TAlg key = CreateKey()) + { + key.ImportFromPem(@" +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgQHs5HRkpurXDPaabivT2IaRoyYt +Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END PUBLIC KEY-----"); + ECParameters ecParameters = ExportParameters(key, false); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.ComparePublicKey(expected.Q, ecParameters.Q, isEqual: true); + } + } + + [Fact] + public void ImportFromPem_Spki_PrecedingUnrelatedPemIsIgnored() + { + using (TAlg key = CreateKey()) + { + key.ImportFromPem(@" +-----BEGIN CERTIFICATE----- +MIICTzCCAgmgAwIBAgIJAMQtYhFJ0+5jMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHUmVkbW9uZDEY +MBYGA1UECgwPTWljcm9zb2Z0IENvcnAuMSAwHgYDVQQLDBcuTkVUIEZyYW1ld29y +ayAoQ29yZUZ4KTEgMB4GA1UEAwwXUlNBIDM4NC1iaXQgQ2VydGlmaWNhdGUwHhcN +MTYwMzAyMTY1OTA0WhcNMTYwNDAxMTY1OTA0WjCBkjELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1JlZG1vbmQxGDAWBgNVBAoMD01p +Y3Jvc29mdCBDb3JwLjEgMB4GA1UECwwXLk5FVCBGcmFtZXdvcmsgKENvcmVGeCkx +IDAeBgNVBAMMF1JTQSAzODQtYml0IENlcnRpZmljYXRlMEwwDQYJKoZIhvcNAQEB +BQADOwAwOAIxANrMIthuZxV1Ay4x8gbc/BksZeLVEInlES0JbyiCr9tbeM22Vy/S +9h2zkEciMuPZ9QIDAQABo1AwTjAdBgNVHQ4EFgQU5FG2Fmi86hJOCf4KnjaxOGWV +dRUwHwYDVR0jBBgwFoAU5FG2Fmi86hJOCf4KnjaxOGWVdRUwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQUFAAMxAEzDg/u8TlApCnE8qxhcbTXk2MbX+2n5PCn+MVrW +wggvPj3b2WMXsVWiPr4S1Y/nBA== +-----END CERTIFICATE----- +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgQHs5HRkpurXDPaabivT2IaRoyYt +Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END PUBLIC KEY-----"); + ECParameters ecParameters = ExportParameters(key, false); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.ComparePublicKey(expected.Q, ecParameters.Q, isEqual: true); + } + } + + [Fact] + public void ImportFromPem_Spki_IgnoresUnrelatedAlgorithms() + { + using (TAlg key = CreateKey()) + { + key.ImportFromPem(@" +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY----- +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgQHs5HRkpurXDPaabivT2IaRoyYt +Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END PUBLIC KEY-----"); + ECParameters ecParameters = ExportParameters(key, false); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.ComparePublicKey(expected.Q, ecParameters.Q, isEqual: true); + } + } + + [Fact] + public void ImportFromPem_Spki_PrecedingMalformedPem() + { + using (TAlg key = CreateKey()) + { + key.ImportFromPem(@" +-----BEGIN CERTIFICATE----- +$$ I AM NOT A PEM +-----END CERTIFICATE----- +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgQHs5HRkpurXDPaabivT2IaRoyYt +Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END PUBLIC KEY-----"); + ECParameters ecParameters = ExportParameters(key, false); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.ComparePublicKey(expected.Q, ecParameters.Q, isEqual: true); + } + } + + [Fact] + public void ImportFromPem_Spki_AmbiguousKey_Spki() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgQHs5HRkpurXDPaabivT2IaRoyYt +Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END PUBLIC KEY----- +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgQHs5HRkpurXDPaabivT2IaRoyYt +Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END PUBLIC KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => key.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromPem_Spki_AmbiguousKey_EncryptedPkcs8() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAjVvm4KTLb0JgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuHgfok8Ytl0EgZDkDSJ9vt8UvSesdyV+ +Evt9yfvEjiP/6yITq59drw1Kcgp6buOCVCY7LZ06aD6WpogiqGDYMuzfvqg5hNFp +opSAJ/pvHONL5kyAJLeNyG9c/mR2qyrP2L9gL0Z5fB9NyPejKTLi0PXMGQWdDTH8 +Qh0fqdrNovgFLubbJFMQN/MwwIAfIuf0Mn0WFYYeQiBJ3kg= +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgQHs5HRkpurXDPaabivT2IaRoyYt +Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END PUBLIC KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => key.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromPem_Spki_AmbiguousKey_EncryptedPkcs8_Pkcs8First() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgQHs5HRkpurXDPaabivT2IaRoyYt +Isuk92Ner/JmgKjYoSumHVmSNfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END PUBLIC KEY----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAjVvm4KTLb0JgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuHgfok8Ytl0EgZDkDSJ9vt8UvSesdyV+ +Evt9yfvEjiP/6yITq59drw1Kcgp6buOCVCY7LZ06aD6WpogiqGDYMuzfvqg5hNFp +opSAJ/pvHONL5kyAJLeNyG9c/mR2qyrP2L9gL0Z5fB9NyPejKTLi0PXMGQWdDTH8 +Qh0fqdrNovgFLubbJFMQN/MwwIAfIuf0Mn0WFYYeQiBJ3kg= +-----END ENCRYPTED PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => key.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromPem_EncryptedPrivateKeyFails() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAjVvm4KTLb0JgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuHgfok8Ytl0EgZDkDSJ9vt8UvSesdyV+ +Evt9yfvEjiP/6yITq59drw1Kcgp6buOCVCY7LZ06aD6WpogiqGDYMuzfvqg5hNFp +opSAJ/pvHONL5kyAJLeNyG9c/mR2qyrP2L9gL0Z5fB9NyPejKTLi0PXMGQWdDTH8 +Qh0fqdrNovgFLubbJFMQN/MwwIAfIuf0Mn0WFYYeQiBJ3kg= +-----END ENCRYPTED PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => key.ImportFromPem(pem)); + Assert.Contains(EncryptedExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromPem_MultipleEncryptedPrivateKeyAreAmbiguous() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAjVvm4KTLb0JgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuHgfok8Ytl0EgZDkDSJ9vt8UvSesdyV+ +Evt9yfvEjiP/6yITq59drw1Kcgp6buOCVCY7LZ06aD6WpogiqGDYMuzfvqg5hNFp +opSAJ/pvHONL5kyAJLeNyG9c/mR2qyrP2L9gL0Z5fB9NyPejKTLi0PXMGQWdDTH8 +Qh0fqdrNovgFLubbJFMQN/MwwIAfIuf0Mn0WFYYeQiBJ3kg= +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAjVvm4KTLb0JgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuHgfok8Ytl0EgZDkDSJ9vt8UvSesdyV+ +Evt9yfvEjiP/6yITq59drw1Kcgp6buOCVCY7LZ06aD6WpogiqGDYMuzfvqg5hNFp +opSAJ/pvHONL5kyAJLeNyG9c/mR2qyrP2L9gL0Z5fB9NyPejKTLi0PXMGQWdDTH8 +Qh0fqdrNovgFLubbJFMQN/MwwIAfIuf0Mn0WFYYeQiBJ3kg= +-----END ENCRYPTED PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => key.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromEncryptedPem_Pkcs8_Char_Simple() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAjVvm4KTLb0JgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuHgfok8Ytl0EgZDkDSJ9vt8UvSesdyV+ +Evt9yfvEjiP/6yITq59drw1Kcgp6buOCVCY7LZ06aD6WpogiqGDYMuzfvqg5hNFp +opSAJ/pvHONL5kyAJLeNyG9c/mR2qyrP2L9gL0Z5fB9NyPejKTLi0PXMGQWdDTH8 +Qh0fqdrNovgFLubbJFMQN/MwwIAfIuf0Mn0WFYYeQiBJ3kg= +-----END ENCRYPTED PRIVATE KEY-----"; + key.ImportFromEncryptedPem(pem, "test"); + ECParameters ecParameters = ExportParameters(key, true); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.AssertEqual(expected, ecParameters); + } + } + + [Fact] + public void ImportFromEncryptedPem_Pkcs8_Byte_Simple() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAgf9krO2ZiPvAICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEEv4Re1ATH9lHzx+13GoZU0EgZAV +iE/+pIb/4quf+Y524bXUKTGYXzdSUE8Dp1qdZFcwDiCYCTtpL+065fGhmf1KZS2c +/OMt/tWvtMSj17+dJvShsu/NYJXF5fsfpSJbd3e50Y3AisW0Ob7mmF54KBfg6Y+4 +aATwwQdUIKVzUZsQctsHPjbriQKKn7GKSyUOikBUNQ+TozojX8/g7JAsl+T9jGM= +-----END ENCRYPTED PRIVATE KEY-----"; + byte[] passwordBytes = Encoding.UTF8.GetBytes("test"); + key.ImportFromEncryptedPem(pem, passwordBytes); + ECParameters ecParameters = ExportParameters(key, true); + ECParameters expected = EccTestData.GetNistP256ReferenceKey(); + EccTestBase.AssertEqual(expected, ecParameters); + } + } + + [Fact] + public void ImportFromEncryptedPem_AmbiguousPem_Byte() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAgf9krO2ZiPvAICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEEv4Re1ATH9lHzx+13GoZU0EgZAV +iE/+pIb/4quf+Y524bXUKTGYXzdSUE8Dp1qdZFcwDiCYCTtpL+065fGhmf1KZS2c +/OMt/tWvtMSj17+dJvShsu/NYJXF5fsfpSJbd3e50Y3AisW0Ob7mmF54KBfg6Y+4 +aATwwQdUIKVzUZsQctsHPjbriQKKn7GKSyUOikBUNQ+TozojX8/g7JAsl+T9jGM= +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAjVvm4KTLb0JgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuHgfok8Ytl0EgZDkDSJ9vt8UvSesdyV+ +Evt9yfvEjiP/6yITq59drw1Kcgp6buOCVCY7LZ06aD6WpogiqGDYMuzfvqg5hNFp +opSAJ/pvHONL5kyAJLeNyG9c/mR2qyrP2L9gL0Z5fB9NyPejKTLi0PXMGQWdDTH8 +Qh0fqdrNovgFLubbJFMQN/MwwIAfIuf0Mn0WFYYeQiBJ3kg= +-----END ENCRYPTED PRIVATE KEY-----"; + byte[] passwordBytes = Encoding.UTF8.GetBytes("test"); + + ArgumentException ae = AssertExtensions.Throws("input", () => + key.ImportFromEncryptedPem(pem, passwordBytes)); + + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromEncryptedPem_AmbiguousPem_Char() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAgf9krO2ZiPvAICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEEv4Re1ATH9lHzx+13GoZU0EgZAV +iE/+pIb/4quf+Y524bXUKTGYXzdSUE8Dp1qdZFcwDiCYCTtpL+065fGhmf1KZS2c +/OMt/tWvtMSj17+dJvShsu/NYJXF5fsfpSJbd3e50Y3AisW0Ob7mmF54KBfg6Y+4 +aATwwQdUIKVzUZsQctsHPjbriQKKn7GKSyUOikBUNQ+TozojX8/g7JAsl+T9jGM= +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHgMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAjVvm4KTLb0JgICCAAw +DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIuHgfok8Ytl0EgZDkDSJ9vt8UvSesdyV+ +Evt9yfvEjiP/6yITq59drw1Kcgp6buOCVCY7LZ06aD6WpogiqGDYMuzfvqg5hNFp +opSAJ/pvHONL5kyAJLeNyG9c/mR2qyrP2L9gL0Z5fB9NyPejKTLi0PXMGQWdDTH8 +Qh0fqdrNovgFLubbJFMQN/MwwIAfIuf0Mn0WFYYeQiBJ3kg= +-----END ENCRYPTED PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => + key.ImportFromEncryptedPem(pem, "")); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromEncryptedPem_UnencryptedPem_ThrowsNoPem() + { + using (TAlg key = CreateKey()) + { + string pem = @" +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgcKEsLbFoRe1W/2jP +whpHKz8E19aFG/Y0ny19WzRSs4qhRANCAASBAezkdGSm6tcM9ppuK9PYhpGjJi0i +y6T3Y16v8maAqNihK6YdWZI19n2ctNWPF4PTykPnjwpauqYkB5k2wMOp +-----END PRIVATE KEY-----"; + byte[] passwordBytes = Array.Empty(); + ArgumentException ae = AssertExtensions.Throws("input", () => + key.ImportFromEncryptedPem(pem, passwordBytes)); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + [Fact] + public void ImportFromEncryptedPem_NoPem() + { + using(TAlg key = CreateKey()) + { + ArgumentException ae = AssertExtensions.Throws("input", () => + key.ImportFromEncryptedPem("", "")); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs new file mode 100644 index 0000000000000..2e6985a220f8c --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanKeyPemTests.cs @@ -0,0 +1,15 @@ +// 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.Security.Cryptography.Tests; + +namespace System.Security.Cryptography.EcDsa.Tests +{ + public sealed class ECDiffieHellmanKeyPemTests : ECKeyPemTests + { + protected override ECDiffieHellman CreateKey() => ECDiffieHellman.Create(); + protected override ECParameters ExportParameters(ECDiffieHellman key, bool includePrivateParameters) => + key.ExportParameters(includePrivateParameters); + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs new file mode 100644 index 0000000000000..8358edca90acd --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyPemTests.cs @@ -0,0 +1,15 @@ +// 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.Security.Cryptography.Tests; + +namespace System.Security.Cryptography.EcDsa.Tests +{ + public sealed class ECDsaKeyPemTests : ECKeyPemTests + { + protected override ECDsa CreateKey() => ECDsa.Create(); + protected override ECParameters ExportParameters(ECDsa key, bool includePrivateParameters) => + key.ExportParameters(includePrivateParameters); + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs new file mode 100644 index 0000000000000..37c90271b66a7 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs @@ -0,0 +1,530 @@ +// 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.Text; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Rsa.Tests +{ + public static class RSAKeyPemTests + { + private const string AmbiguousExceptionMarker = "multiple keys"; + private const string EncryptedExceptionMarker = "encrypted key"; + private const string NoPemExceptionMarker = "No supported key"; + + [Fact] + public static void ImportFromPem_NoPem() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @"these aren't the PEMs you're looking for"; + ArgumentException ae = AssertExtensions.Throws("input", () => rsa.ImportFromPem(pem)); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_RSAPrivateKey_Simple() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY-----"; + + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(true); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters, rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8UnEncrypted_Simple() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN PRIVATE KEY----- +MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAtz9Z9e6L1V4kt/8C +mtFqhUPJbSU+VDGbk1MsQcPBR3uJ2y0vM9e5qHRYSOBqjmg7UERRHhvKNiUn4Xz0 +KzgGFQIDAQABAkEAr+byNi+cr17FpJH4MCEiPXaKnmkH4c4U52EJtL9yg2gijBrp +Ykat3c2nWb0EGGi5aWgXxQHoi7z97/ACD4X3KQIhAPNyex6GdiBVlNPHOgInTU8a +mARKKVHIXM0SxvxXrRl7AiEAwLI66OpSqftDTv1KUfNe6+hyoh23ggzUSYiWuVT0 +Ya8CHwiO/cUU9RIt8A2B84gf2ZfuV2nPMaSuZpTPFC/K5UsCIQCsJMzx1JuilQAN +acPiMCuFTnRSFYAhozpmsqoLyTREqwIhAMLJlZTGjEB2N+sEazH5ToEczQzKqp7t +9juGNbOPhoEL +-----END PRIVATE KEY-----"; + + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(true); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters, rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8UnEncrypted_UnrelatedAlgorithmIsIgnored() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49 +AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS +NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END EC PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAtz9Z9e6L1V4kt/8C +mtFqhUPJbSU+VDGbk1MsQcPBR3uJ2y0vM9e5qHRYSOBqjmg7UERRHhvKNiUn4Xz0 +KzgGFQIDAQABAkEAr+byNi+cr17FpJH4MCEiPXaKnmkH4c4U52EJtL9yg2gijBrp +Ykat3c2nWb0EGGi5aWgXxQHoi7z97/ACD4X3KQIhAPNyex6GdiBVlNPHOgInTU8a +mARKKVHIXM0SxvxXrRl7AiEAwLI66OpSqftDTv1KUfNe6+hyoh23ggzUSYiWuVT0 +Ya8CHwiO/cUU9RIt8A2B84gf2ZfuV2nPMaSuZpTPFC/K5UsCIQCsJMzx1JuilQAN +acPiMCuFTnRSFYAhozpmsqoLyTREqwIhAMLJlZTGjEB2N+sEazH5ToEczQzKqp7t +9juGNbOPhoEL +-----END PRIVATE KEY-----"; + + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(true); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters, rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_SubjectPublicKeyInfo_Simple() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQx +m5NTLEHDwUd7idstLzPXuah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQ== +-----END PUBLIC KEY-----"; + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(false); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters.ToPublic(), rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_SubjectPublicKeyInfo_IgnoresUnrelatedAlgorithm() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49 +AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS +NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END EC PRIVATE KEY----- +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQx +m5NTLEHDwUd7idstLzPXuah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQ== +-----END PUBLIC KEY-----"; + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(false); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters.ToPublic(), rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_RSAPublicKey_Simple() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN RSA PUBLIC KEY----- +MEgCQQC3P1n17ovVXiS3/wKa0WqFQ8ltJT5UMZuTUyxBw8FHe4nbLS8z17modFhI +4GqOaDtQRFEeG8o2JSfhfPQrOAYVAgMBAAE= +-----END RSA PUBLIC KEY-----"; + + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(false); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters.ToPublic(), rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_RSAPrivateKey_PrecedingUnrelatedPem() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN CERTIFICATE----- +MIICTzCCAgmgAwIBAgIJAMQtYhFJ0+5jMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHUmVkbW9uZDEY +MBYGA1UECgwPTWljcm9zb2Z0IENvcnAuMSAwHgYDVQQLDBcuTkVUIEZyYW1ld29y +ayAoQ29yZUZ4KTEgMB4GA1UEAwwXUlNBIDM4NC1iaXQgQ2VydGlmaWNhdGUwHhcN +MTYwMzAyMTY1OTA0WhcNMTYwNDAxMTY1OTA0WjCBkjELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1JlZG1vbmQxGDAWBgNVBAoMD01p +Y3Jvc29mdCBDb3JwLjEgMB4GA1UECwwXLk5FVCBGcmFtZXdvcmsgKENvcmVGeCkx +IDAeBgNVBAMMF1JTQSAzODQtYml0IENlcnRpZmljYXRlMEwwDQYJKoZIhvcNAQEB +BQADOwAwOAIxANrMIthuZxV1Ay4x8gbc/BksZeLVEInlES0JbyiCr9tbeM22Vy/S +9h2zkEciMuPZ9QIDAQABo1AwTjAdBgNVHQ4EFgQU5FG2Fmi86hJOCf4KnjaxOGWV +dRUwHwYDVR0jBBgwFoAU5FG2Fmi86hJOCf4KnjaxOGWVdRUwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQUFAAMxAEzDg/u8TlApCnE8qxhcbTXk2MbX+2n5PCn+MVrW +wggvPj3b2WMXsVWiPr4S1Y/nBA== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY-----"; + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(true); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters, rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_RSAPrivateKey_PrecedingMalformedPem() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN CERTIFICATE----- +$$ I AM NOT A PEM +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY-----"; + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(true); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters, rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_RSAPrivateKey_IgnoresOtherAlgorithms() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49 +AwEHoUQDQgAEgQHs5HRkpurXDPaabivT2IaRoyYtIsuk92Ner/JmgKjYoSumHVmS +NfZ9nLTVjxeD08pD548KWrqmJAeZNsDDqQ== +-----END EC PRIVATE KEY----- +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY-----"; + rsa.ImportFromPem(pem); + RSAParameters rsaParameters = rsa.ExportParameters(true); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters, rsaParameters); + } + } + + [Fact] + public static void ImportFromPem_RSAPrivateKey_AmbiguousKey_RSAPrivateKey() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN RSA PRIVATE KEY----- +MII= +-----END RSA PRIVATE KEY----- +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => rsa.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_RSAPrivateKey_AmbiguousKey_SubjectPublicKeyInfo() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN PUBLIC KEY----- +MII= +-----END PUBLIC KEY----- +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => rsa.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_RSAPrivateKey_AmbiguousKey_RSAPublicKey() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN RSA PUBLIC KEY----- +MII= +-----END RSA PUBLIC KEY----- +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => rsa.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_RSAPrivateKey_AmbiguousKey_EncryptedPkcs8() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MII= +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALc/WfXui9VeJLf/AprRaoVDyW0lPlQxm5NTLEHDwUd7idstLzPX +uah0WEjgao5oO1BEUR4byjYlJ+F89Cs4BhUCAwEAAQJBAK/m8jYvnK9exaSR+DAh +Ij12ip5pB+HOFOdhCbS/coNoIowa6WJGrd3Np1m9BBhouWloF8UB6Iu8/e/wAg+F +9ykCIQDzcnsehnYgVZTTxzoCJ01PGpgESilRyFzNEsb8V60ZewIhAMCyOujqUqn7 +Q079SlHzXuvocqIdt4IM1EmIlrlU9GGvAh8Ijv3FFPUSLfANgfOIH9mX7ldpzzGk +rmaUzxQvyuVLAiEArCTM8dSbopUADWnD4jArhU50UhWAIaM6ZrKqC8k0RKsCIQDC +yZWUxoxAdjfrBGsx+U6BHM0Myqqe7fY7hjWzj4aBCw== +-----END RSA PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => rsa.ImportFromPem(pem)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_EncryptedPrivateKeyFails() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBsTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIioaQaFwlfasCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECJLGzSuIgnSkBIIBYHofFpp5AsrkNc9w +s0uebkLBgMXbmhu+t6XQYXhnZXguT4KF4g49vIE3XwtZkXzEeSrNRIWZcPH1UWp2 +qbv2d+ub3wBpMdFDzv5Zty6e6gACWwyMRy/oX8gZqWDfDnQwm7BV21yLANEFnRuT +K3c9EmQ9IAT2MLLRUeijyg6KUL0dZ5VmXbtQdDoovuhzU20HjSyQLXNbX8NzUhWy +VMuNHs8NhiIgOuFKMoqlN42LBA1+iOA4MGR5XDXXmGyKPLCs0USbD9Dm4/Q1h7Fs +x2yC94Mej7kgAusuNZk9GafsIQbM7jZT1PLxIKyMXAxIpS9sIYbegxK774npiy8/ +LiBC1SQXJ3sJdAeUE0QPJEci937f8SteWUmF5mUqznb/0nYjvSZh/GcZ4GWEAO8j +RkMxT/C7OZVMOlb3HV3fJj7kDmOMqfc6aKEQjLdWtuYRB8CgaudldIpK4jP2+0b5 +pBORBb0= +-----END ENCRYPTED PRIVATE KEY-----"; + ArgumentException ae = AssertExtensions.Throws("input", () => rsa.ImportFromPem(pem)); + Assert.Contains(EncryptedExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromPem_Pkcs8AlgorithmMismatch_Throws() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +The below PEM is a 1024-bit DSA key. +-----BEGIN PRIVATE KEY----- +MIIBSgIBADCCASsGByqGSM44BAEwggEeAoGBAL5KGXEaazCA+k1pMcCBc/+bodFh +0P4U2QDLyDtnmytusGPaHcFp69pVdJZWMBycwJdaFQkraQNmqQsjAmBHtpqMeJpE +VLgjzve83oMAw5aysmaQC4Wy35vnBZnshvdzgbPRHZD2dWmFvWxToqBnxh74rb/H +Nkpt8JrirFOdNuyvAhUA9+LZ6XHLZZKeFhDxYl+a9lYabdsCgYACRi+pc9joLRah +A9ushrXVItFyOsq45hOB9hT37nyTEmane/YAjmoR28XyDYdF/Ql97iSVm3cY3OYT +eDr38gQ/Hk0CgW3/RFrNWdbIpfMifs80vqCUNqDggcQixEmDVZ0gwq4+wz8EVyYG +42+vM7ajN4O2VGvCA99Vl6zv69hOpAQWAhQtFFLZyKAUOQwUQh4hNw+oBgPhFw== +-----END PRIVATE KEY-----"; + Assert.Throws(() => rsa.ImportFromPem(pem)); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8Encrypted_Char_Simple() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBsTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIcvgI1lw9LqYCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECFDpLREQXt5pBIIBYOKuM5ljAvCViDL+ +nTFq7A/fI9rqdL20TMdf0wy7s43oXmsw5gCStoNEaoVToFCQWYYBRU99mK8YNFA8 +1ZJT53SDS7buJ0zX9oDltf2ByXRPI4mn2Il2HZvN2hi9ir1w8M3XoSFSurN9tC8r +IOiGkVfK9Ll54knONewNiCNefFZFctRfVMbac5SwHokCkBMHukl0oPrpVuBE8kRo +p7XtjM8ILtzLVz0iLqKXiNIf6kRdouCBmCn8VIQgIvPPIHD8vheMXWjN7g69P5n4 +1YI4c/acljcofmq1BBPTwvxaETrg2NHW0XMIgAxoaVP8lIIGlNk1glWTYpuMd69L +AWvBUt33Sozc+dF0l7NGLAWL2tqkkpyDQuKn6UgYz/vxkFeQAVfSuaJVR+fUlHg0 +N4lD7/hJq7b+yYPhlN3Fvvt8M9MtRg1TLAve67CA2v4TITHB06M/ELe3y42bZuLW +CA7ffFk= +-----END ENCRYPTED PRIVATE KEY-----"; + rsa.ImportFromEncryptedPem(pem, "test"); + RSAParameters rsaParameters = rsa.ExportParameters(true); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters, rsaParameters); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8Encrypted_Byte_Simple() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBvTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIciLWmWb33X0CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBBVEmHhJdbi+HKzPttNjXm4BIIB +YFejknurbot2VDXwc671A0mfA0cw/u7K44gsYXcZwAARC8j6f3lSzB0tN2kMEx/L +TB+kpMBbfAoIPKoEc9Y4w9m3NXkQYrLRONh9AFiAnOjULHwkstQfN2ofFlolDfbH +hAE6ga6aQJTQ8rDKTL4QkCg+s+qWlicPqs5ikSQfUz2Qiy8FKe7zZlJ0OWpT+zk7 +EYRrUSKQcEAjfNS7anlMps2ZXRc1LkLJNHZSl6h2BuFPfIKEV9REpy3Y7sH7vNZZ +PWPa9/xM4CX/c/ommy6LqvZikUuUGc56/Hbz65SwG3voivIhOTmM28LiA6z0YXmY +E+nr7hyinl51raM1RSHojJB22oOW+GwV7GgWYIjUgIEMDOhN10FcGNfTeC65PCXx +5QSEe7EKVF0aHXBYB5SzMGVuxR/BqydDa26jlhVzO3LNvy9FYuqLKUslCrBCmPrt +raZNyk8KAsLs+FJq9T2tda0= +-----END ENCRYPTED PRIVATE KEY-----"; + rsa.ImportFromEncryptedPem(pem, Encoding.UTF8.GetBytes("test")); + RSAParameters rsaParameters = rsa.ExportParameters(true); + + ImportExport.AssertKeyEquals(TestData.DiminishedDPParameters, rsaParameters); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8Encrypted_AmbiguousPem() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBvTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIciLWmWb33X0CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBBVEmHhJdbi+HKzPttNjXm4BIIB +YFejknurbot2VDXwc671A0mfA0cw/u7K44gsYXcZwAARC8j6f3lSzB0tN2kMEx/L +TB+kpMBbfAoIPKoEc9Y4w9m3NXkQYrLRONh9AFiAnOjULHwkstQfN2ofFlolDfbH +hAE6ga6aQJTQ8rDKTL4QkCg+s+qWlicPqs5ikSQfUz2Qiy8FKe7zZlJ0OWpT+zk7 +EYRrUSKQcEAjfNS7anlMps2ZXRc1LkLJNHZSl6h2BuFPfIKEV9REpy3Y7sH7vNZZ +PWPa9/xM4CX/c/ommy6LqvZikUuUGc56/Hbz65SwG3voivIhOTmM28LiA6z0YXmY +E+nr7hyinl51raM1RSHojJB22oOW+GwV7GgWYIjUgIEMDOhN10FcGNfTeC65PCXx +5QSEe7EKVF0aHXBYB5SzMGVuxR/BqydDa26jlhVzO3LNvy9FYuqLKUslCrBCmPrt +raZNyk8KAsLs+FJq9T2tda0= +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBsTBLBgkqhkiG9w0BBQ0wPjApBgkqhkiG9w0BBQwwHAQIcvgI1lw9LqYCAggA +MAwGCCqGSIb3DQIJBQAwEQYFKw4DAgcECFDpLREQXt5pBIIBYOKuM5ljAvCViDL+ +nTFq7A/fI9rqdL20TMdf0wy7s43oXmsw5gCStoNEaoVToFCQWYYBRU99mK8YNFA8 +1ZJT53SDS7buJ0zX9oDltf2ByXRPI4mn2Il2HZvN2hi9ir1w8M3XoSFSurN9tC8r +IOiGkVfK9Ll54knONewNiCNefFZFctRfVMbac5SwHokCkBMHukl0oPrpVuBE8kRo +p7XtjM8ILtzLVz0iLqKXiNIf6kRdouCBmCn8VIQgIvPPIHD8vheMXWjN7g69P5n4 +1YI4c/acljcofmq1BBPTwvxaETrg2NHW0XMIgAxoaVP8lIIGlNk1glWTYpuMd69L +AWvBUt33Sozc+dF0l7NGLAWL2tqkkpyDQuKn6UgYz/vxkFeQAVfSuaJVR+fUlHg0 +N4lD7/hJq7b+yYPhlN3Fvvt8M9MtRg1TLAve67CA2v4TITHB06M/ELe3y42bZuLW +CA7ffFk= +-----END ENCRYPTED PRIVATE KEY-----"; + byte[] passwordBytes = Encoding.UTF8.GetBytes("test"); + ArgumentException ae = AssertExtensions.Throws("input", () => + rsa.ImportFromEncryptedPem(pem, passwordBytes)); + Assert.Contains(AmbiguousExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8Encrypted_Byte_NoPem() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = "these aren't the PEMs we're looking for."; + byte[] passwordBytes = Encoding.UTF8.GetBytes("test"); + ArgumentException ae = AssertExtensions.Throws("input", () => + rsa.ImportFromEncryptedPem(pem, passwordBytes)); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromEncryptedPem_NoEncryptedPem() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = @" +-----BEGIN PRIVATE KEY----- +MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAtz9Z9e6L1V4kt/8C +mtFqhUPJbSU+VDGbk1MsQcPBR3uJ2y0vM9e5qHRYSOBqjmg7UERRHhvKNiUn4Xz0 +KzgGFQIDAQABAkEAr+byNi+cr17FpJH4MCEiPXaKnmkH4c4U52EJtL9yg2gijBrp +Ykat3c2nWb0EGGi5aWgXxQHoi7z97/ACD4X3KQIhAPNyex6GdiBVlNPHOgInTU8a +mARKKVHIXM0SxvxXrRl7AiEAwLI66OpSqftDTv1KUfNe6+hyoh23ggzUSYiWuVT0 +Ya8CHwiO/cUU9RIt8A2B84gf2ZfuV2nPMaSuZpTPFC/K5UsCIQCsJMzx1JuilQAN +acPiMCuFTnRSFYAhozpmsqoLyTREqwIhAMLJlZTGjEB2N+sEazH5ToEczQzKqp7t +9juGNbOPhoEL +-----END PRIVATE KEY-----"; + byte[] passwordBytes = Encoding.UTF8.GetBytes("test"); + ArgumentException ae = AssertExtensions.Throws("input", () => + rsa.ImportFromEncryptedPem(pem, passwordBytes)); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + [Fact] + public static void ImportFromEncryptedPem_Pkcs8Encrypted_Char_NoPem() + { + using (RSA rsa = RSAFactory.Create()) + { + string pem = "go about your business"; + string password = "test"; + ArgumentException ae = AssertExtensions.Throws("input", () => + rsa.ImportFromEncryptedPem(pem, password)); + Assert.Contains(NoPemExceptionMarker, ae.Message); + } + } + + private static RSAParameters ToPublic(this RSAParameters rsaParams) + { + return new RSAParameters + { + Exponent = rsaParams.Exponent, + Modulus = rsaParams.Modulus + }; + } + } +} diff --git a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs index fe1cafe80e816..5ea714ab6d8c1 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs @@ -135,6 +135,9 @@ public override void FromXmlString(string xmlString) { } protected virtual byte[] HashData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan passwordBytes) { } + public override void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan password) { } + public override void ImportFromPem(System.ReadOnlySpan input) { } public abstract void ImportParameters(System.Security.Cryptography.DSAParameters parameters); public override void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } @@ -283,6 +286,9 @@ public virtual void GenerateKey(System.Security.Cryptography.ECCurve curve) { } public virtual void ImportECPrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan passwordBytes) { } + public override void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan password) { } + public override void ImportFromPem(System.ReadOnlySpan input) { } public virtual void ImportParameters(System.Security.Cryptography.ECParameters parameters) { } public override void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } @@ -324,6 +330,9 @@ public virtual void GenerateKey(System.Security.Cryptography.ECCurve curve) { } public virtual void ImportECPrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan passwordBytes) { } + public override void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan password) { } + public override void ImportFromPem(System.ReadOnlySpan input) { } public virtual void ImportParameters(System.Security.Cryptography.ECParameters parameters) { } public override void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } @@ -573,6 +582,9 @@ public override void FromXmlString(string xmlString) { } protected virtual byte[] HashData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; } public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan passwordBytes) { } + public override void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan password) { } + public override void ImportFromPem(System.ReadOnlySpan input) { } public abstract void ImportParameters(System.Security.Cryptography.RSAParameters parameters); public override void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public virtual void ImportRSAPrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx index 878856832fbab..d162b5c57ddc1 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx @@ -81,6 +81,15 @@ Buffer cannot be null. + + No supported key formats were found. Check that the input represents the contents of a PEM-encoded key file, not the path to such a file. + + + The input contains multiple keys, but only one key can be imported. + + + An encrypted key was found, but no password was provided. Use ImportFromEncryptedPem to import this key. + Error occurred during a cryptographic operation. @@ -320,7 +329,7 @@ Method not supported. - + Method not supported. Derived class must override. diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 680eedda4281d..b0a49ecf2030e 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -98,6 +98,9 @@ Internal\Cryptography\HashProvider.cs + + Common\Internal\Cryptography\PemKeyImportHelpers.cs + Internal\Cryptography\UniversalCryptoTransform.cs @@ -134,6 +137,9 @@ Common\System\Security\Cryptography\PasswordBasedEncryption.cs + + Common\System\Security\Cryptography\PemLabels.cs + Common\System\Security\Cryptography\Pkcs12Kdf.cs @@ -731,4 +737,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs index 69b260cc71550..71c22caff3915 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs @@ -1125,5 +1125,197 @@ public int GetMaxSignatureSize(DSASignatureFormat signatureFormat) throw new ArgumentOutOfRangeException(nameof(signatureFormat)); } } + + /// + /// Imports an RFC 7468 PEM-encoded key, replacing the keys for this object. + /// + /// The PEM text of the key to import. + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains an encrypted PEM-encoded key. + /// + /// + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is raised to prevent importing a key when + /// the key is ambiguous. + /// + /// + /// This method supports the following PEM labels: + /// + /// PUBLIC KEY + /// PRIVATE KEY + /// + /// + /// + public override void ImportFromPem(ReadOnlySpan input) + { + PemKeyImportHelpers.ImportPem(input, label => { + if (label.SequenceEqual(PemLabels.Pkcs8PrivateKey)) + { + return ImportPkcs8PrivateKey; + } + else if (label.SequenceEqual(PemLabels.SpkiPublicKey)) + { + return ImportSubjectPublicKeyInfo; + } + else + { + return null; + } + }); + } + + /// + /// Imports an encrypted RFC 7468 PEM-encoded private key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The password to use for decrypting the key material. + /// + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// + /// + /// The password is incorrect. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// indicate the key is for an algorithm other than the algorithm + /// represented by this instance. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// represent the key in a format that is not supported. + /// + /// + /// -or- + /// + /// + /// The algorithm-specific key import failed. + /// + /// + /// + /// + /// When the base-64 decoded contents of indicate an algorithm that uses PBKDF1 + /// (Password-Based Key Derivation Function 1) or PBKDF2 (Password-Based Key Derivation Function 2), + /// the password is converted to bytes via the UTF-8 encoding. + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is thrown to prevent importing a key when + /// the key is ambiguous. + /// + /// This method supports the ENCRYPTED PRIVATE KEY PEM label. + /// + public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan password) + { + PemKeyImportHelpers.ImportEncryptedPem(input, password, ImportEncryptedPkcs8PrivateKey); + } + + /// + /// Imports an encrypted RFC 7468 PEM-encoded private key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The bytes to use as a password when decrypting the key material. + /// + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// + /// + /// The password is incorrect. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// indicate the key is for an algorithm other than the algorithm + /// represented by this instance. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// represent the key in a format that is not supported. + /// + /// + /// -or- + /// + /// + /// The algorithm-specific key import failed. + /// + /// + /// + /// + /// The password bytes are passed directly into the Key Derivation Function (KDF) + /// used by the algorithm indicated by pbeParameters. This enables compatibility + /// with other systems which use a text encoding other than UTF-8 when processing + /// passwords with PBKDF2 (Password-Based Key Derivation Function 2). + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is thrown to prevent importing a key when + /// the key is ambiguous. + /// + /// This method supports the ENCRYPTED PRIVATE KEY PEM label. + /// + public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan passwordBytes) + { + PemKeyImportHelpers.ImportEncryptedPem(input, passwordBytes, ImportEncryptedPkcs8PrivateKey); + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs index 6109ae344db20..fd7c034f7be31 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellman.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 System.Security.Cryptography.Asn1; namespace System.Security.Cryptography @@ -433,5 +434,202 @@ public virtual unsafe bool TryExportECPrivateKey(Span destination, out int } } } + + /// + /// Imports an RFC 7468 PEM-encoded key, replacing the keys for this object. + /// + /// The PEM text of the key to import. + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains an encrypted PEM-encoded key. + /// + /// + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is raised to prevent importing a key when + /// the key is ambiguous. + /// + /// + /// This method supports the following PEM labels: + /// + /// PUBLIC KEY + /// PRIVATE KEY + /// EC PRIVATE KEY + /// + /// + /// + public override void ImportFromPem(ReadOnlySpan input) + { + PemKeyImportHelpers.ImportPem(input, label => { + if (label.SequenceEqual(PemLabels.Pkcs8PrivateKey)) + { + return ImportPkcs8PrivateKey; + } + else if (label.SequenceEqual(PemLabels.SpkiPublicKey)) + { + return ImportSubjectPublicKeyInfo; + } + else if (label.SequenceEqual(PemLabels.EcPrivateKey)) + { + return ImportECPrivateKey; + } + else + { + return null; + } + }); + } + + /// + /// Imports an encrypted RFC 7468 PEM-encoded private key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The password to use for decrypting the key material. + /// + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// + /// + /// The password is incorrect. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// indicate the key is for an algorithm other than the algorithm + /// represented by this instance. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// represent the key in a format that is not supported. + /// + /// + /// -or- + /// + /// + /// The algorithm-specific key import failed. + /// + /// + /// + /// + /// When the base-64 decoded contents of indicate an algorithm that uses PBKDF1 + /// (Password-Based Key Derivation Function 1) or PBKDF2 (Password-Based Key Derivation Function 2), + /// the password is converted to bytes via the UTF-8 encoding. + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is thrown to prevent importing a key when + /// the key is ambiguous. + /// + /// This method supports the ENCRYPTED PRIVATE KEY PEM label. + /// + public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan password) + { + PemKeyImportHelpers.ImportEncryptedPem(input, password, ImportEncryptedPkcs8PrivateKey); + } + + /// + /// Imports an encrypted RFC 7468 PEM-encoded private key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The bytes to use as a password when decrypting the key material. + /// + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// + /// + /// The password is incorrect. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// indicate the key is for an algorithm other than the algorithm + /// represented by this instance. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// represent the key in a format that is not supported. + /// + /// + /// -or- + /// + /// + /// The algorithm-specific key import failed. + /// + /// + /// + /// + /// The password bytes are passed directly into the Key Derivation Function (KDF) + /// used by the algorithm indicated by pbeParameters. This enables compatibility + /// with other systems which use a text encoding other than UTF-8 when processing + /// passwords with PBKDF2 (Password-Based Key Derivation Function 2). + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is thrown to prevent importing a key when + /// the key is ambiguous. + /// + /// This method supports the ENCRYPTED PRIVATE KEY PEM label. + /// + public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan passwordBytes) + { + PemKeyImportHelpers.ImportEncryptedPem(input, passwordBytes, ImportEncryptedPkcs8PrivateKey); + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs index 01d1109e6edd3..59b52bd0eb36d 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsa.cs @@ -1298,5 +1298,202 @@ public int GetMaxSignatureSize(DSASignatureFormat signatureFormat) throw new ArgumentOutOfRangeException(nameof(signatureFormat)); } } + + /// + /// Imports an RFC 7468 PEM-encoded key, replacing the keys for this object. + /// + /// The PEM text of the key to import. + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains an encrypted PEM-encoded key. + /// + /// + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is raised to prevent importing a key when + /// the key is ambiguous. + /// + /// + /// This method supports the following PEM labels: + /// + /// PUBLIC KEY + /// PRIVATE KEY + /// EC PRIVATE KEY + /// + /// + /// + public override void ImportFromPem(ReadOnlySpan input) + { + PemKeyImportHelpers.ImportPem(input, label => { + if (label.SequenceEqual(PemLabels.Pkcs8PrivateKey)) + { + return ImportPkcs8PrivateKey; + } + else if (label.SequenceEqual(PemLabels.SpkiPublicKey)) + { + return ImportSubjectPublicKeyInfo; + } + else if (label.SequenceEqual(PemLabels.EcPrivateKey)) + { + return ImportECPrivateKey; + } + else + { + return null; + } + }); + } + + /// + /// Imports an encrypted RFC 7468 PEM-encoded private key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The password to use for decrypting the key material. + /// + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// + /// + /// The password is incorrect. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// indicate the key is for an algorithm other than the algorithm + /// represented by this instance. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// represent the key in a format that is not supported. + /// + /// + /// -or- + /// + /// + /// The algorithm-specific key import failed. + /// + /// + /// + /// + /// When the base-64 decoded contents of indicate an algorithm that uses PBKDF1 + /// (Password-Based Key Derivation Function 1) or PBKDF2 (Password-Based Key Derivation Function 2), + /// the password is converted to bytes via the UTF-8 encoding. + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is thrown to prevent importing a key when + /// the key is ambiguous. + /// + /// This method supports the ENCRYPTED PRIVATE KEY PEM label. + /// + public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan password) + { + PemKeyImportHelpers.ImportEncryptedPem(input, password, ImportEncryptedPkcs8PrivateKey); + } + + /// + /// Imports an encrypted RFC 7468 PEM-encoded private key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The bytes to use as a password when decrypting the key material. + /// + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// + /// + /// The password is incorrect. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// indicate the key is for an algorithm other than the algorithm + /// represented by this instance. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// represent the key in a format that is not supported. + /// + /// + /// -or- + /// + /// + /// The algorithm-specific key import failed. + /// + /// + /// + /// + /// The password bytes are passed directly into the Key Derivation Function (KDF) + /// used by the algorithm indicated by pbeParameters. This enables compatibility + /// with other systems which use a text encoding other than UTF-8 when processing + /// passwords with PBKDF2 (Password-Based Key Derivation Function 2). + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is thrown to prevent importing a key when + /// the key is ambiguous. + /// + /// This method supports the ENCRYPTED PRIVATE KEY PEM label. + /// + public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan passwordBytes) + { + PemKeyImportHelpers.ImportEncryptedPem(input, passwordBytes, ImportEncryptedPkcs8PrivateKey); + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs index 0d7a6c6bd41b7..56b25ff778825 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSA.cs @@ -625,6 +625,208 @@ public override unsafe void ImportEncryptedPkcs8PrivateKey( bytesRead = localRead; } + /// + /// Imports an RFC 7468 PEM-encoded key, replacing the keys for this object. + /// + /// The PEM text of the key to import. + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains an encrypted PEM-encoded key. + /// + /// + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is raised to prevent importing a key when + /// the key is ambiguous. + /// + /// + /// This method supports the following PEM labels: + /// + /// PUBLIC KEY + /// PRIVATE KEY + /// RSA PRIVATE KEY + /// RSA PUBLIC KEY + /// + /// + /// + public override void ImportFromPem(ReadOnlySpan input) + { + PemKeyImportHelpers.ImportPem(input, label => { + if (label.SequenceEqual(PemLabels.RsaPrivateKey)) + { + return ImportRSAPrivateKey; + } + else if (label.SequenceEqual(PemLabels.Pkcs8PrivateKey)) + { + return ImportPkcs8PrivateKey; + } + else if (label.SequenceEqual(PemLabels.RsaPublicKey)) + { + return ImportRSAPublicKey; + } + else if (label.SequenceEqual(PemLabels.SpkiPublicKey)) + { + return ImportSubjectPublicKeyInfo; + } + else + { + return null; + } + }); + } + + /// + /// Imports an encrypted RFC 7468 PEM-encoded private key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The password to use for decrypting the key material. + /// + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// + /// + /// The password is incorrect. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// indicate the key is for an algorithm other than the algorithm + /// represented by this instance. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// represent the key in a format that is not supported. + /// + /// + /// -or- + /// + /// + /// The algorithm-specific key import failed. + /// + /// + /// + /// + /// When the base-64 decoded contents of indicate an algorithm that uses PBKDF1 + /// (Password-Based Key Derivation Function 1) or PBKDF2 (Password-Based Key Derivation Function 2), + /// the password is converted to bytes via the UTF-8 encoding. + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is thrown to prevent importing a key when + /// the key is ambiguous. + /// + /// This method supports the ENCRYPTED PRIVATE KEY PEM label. + /// + public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan password) + { + PemKeyImportHelpers.ImportEncryptedPem(input, password, ImportEncryptedPkcs8PrivateKey); + } + + /// + /// Imports an encrypted RFC 7468 PEM-encoded private key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The bytes to use as a password when decrypting the key material. + /// + /// + /// + /// does not contain a PEM-encoded key with a recognized label. + /// + /// + /// -or- + /// + /// + /// contains multiple PEM-encoded keys with a recognized label. + /// + /// + /// + /// + /// The password is incorrect. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// do not represent an ASN.1-BER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// indicate the key is for an algorithm other than the algorithm + /// represented by this instance. + /// + /// + /// -or- + /// + /// + /// The base-64 decoded contents of the PEM text from + /// represent the key in a format that is not supported. + /// + /// + /// -or- + /// + /// + /// The algorithm-specific key import failed. + /// + /// + /// + /// + /// The password bytes are passed directly into the Key Derivation Function (KDF) + /// used by the algorithm indicated by pbeParameters. This enables compatibility + /// with other systems which use a text encoding other than UTF-8 when processing + /// passwords with PBKDF2 (Password-Based Key Derivation Function 2). + /// + /// + /// Unsupported or malformed PEM-encoded objects will be ignored. If multiple supported PEM labels + /// are found, an exception is thrown to prevent importing a key when + /// the key is ambiguous. + /// + /// This method supports the ENCRYPTED PRIVATE KEY PEM label. + /// + public override void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan passwordBytes) + { + PemKeyImportHelpers.ImportEncryptedPem(input, passwordBytes, ImportEncryptedPkcs8PrivateKey); + } + private static void ClearPrivateParameters(in RSAParameters rsaParameters) { CryptographicOperations.ZeroMemory(rsaParameters.D); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index 0f71a474092d7..8b648f3cc436c 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -141,15 +141,24 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanFactory.cs @@ -177,9 +186,15 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyPemTests.cs + @@ -283,4 +298,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index c0b950a15c25f..a5d72223d4701 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -117,6 +117,9 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSASignatureFormatter.cs @@ -138,15 +141,24 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaTests.netcoreapp.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaSignatureFormatTests.cs @@ -162,6 +174,9 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSASignatureFormatter.cs diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs b/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs index 63dc763b5ef09..9d02ae8643bdb 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Csp/tests/ShimHelpers.cs @@ -51,6 +51,9 @@ public static void VerifyAllBaseMembersOverloaded(Type shimType) // DecryptValue and EncryptValue throw PNSE in base class, so they don't need to be checked. "DecryptValue", "EncryptValue", + // PEM Import/Export defers to Import methods. + "ImportFromPem", + "ImportFromEncryptedPem", // Key Import/Export defers to ImportParameters/ExportParameters (covered by *KeyFileTests) "ImportRSAPrivateKey", "ImportRSAPublicKey", diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj b/src/libraries/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj index df8ab9e89365c..887d4a3e1f205 100644 --- a/src/libraries/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj @@ -75,6 +75,9 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyGeneration.cs @@ -117,6 +120,9 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\SignVerify.netcoreapp.cs diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj index 0084e99045416..f766dd7a378c3 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj @@ -140,18 +140,30 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDiffieHellmanKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDsa\ECDsaTests.netcoreapp.cs @@ -161,9 +173,12 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyPemTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\SignVerify.netcoreapp.cs - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index 3530fac13129f..92787b5a35ff6 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -28,6 +28,9 @@ protected virtual void Dispose(bool disposing) { } public virtual void FromXmlString(string xmlString) { } public virtual void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } public virtual void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public virtual void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan passwordBytes) { } + public virtual void ImportFromEncryptedPem(System.ReadOnlySpan input, System.ReadOnlySpan password) { } + public virtual void ImportFromPem(System.ReadOnlySpan input) { } public virtual void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public virtual void ImportSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } public virtual string ToXmlString(bool includePrivateParameters) { throw null; } diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/AsymmetricAlgorithm.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/AsymmetricAlgorithm.cs index 0b2941def5b32..6c811c578d1a5 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/AsymmetricAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/AsymmetricAlgorithm.cs @@ -164,6 +164,57 @@ public virtual bool TryExportPkcs8PrivateKey(Span destination, out int byt public virtual bool TryExportSubjectPublicKeyInfo(Span destination, out int bytesWritten) => throw new NotImplementedException(SR.NotSupported_SubclassOverride); + /// + /// When overridden in a derived class, imports an encrypted RFC 7468 + /// PEM-encoded key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The password to use for decrypting the key material. + /// + /// + /// A derived type has not overridden this member. + /// + /// + /// Because each algorithm may have algorithm-specific PEM labels, the + /// default behavior will throw . + /// + public virtual void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan password) => + throw new NotImplementedException(SR.NotSupported_SubclassOverride); + + /// + /// When overridden in a derived class, imports an encrypted RFC 7468 + /// PEM-encoded key, replacing the keys for this object. + /// + /// The PEM text of the encrypted key to import. + /// + /// The bytes to use as a password when decrypting the key material. + /// + /// + /// A derived type has not overridden this member. + /// + /// + /// Because each algorithm may have algorithm-specific PEM labels, the + /// default behavior will throw . + /// + public virtual void ImportFromEncryptedPem(ReadOnlySpan input, ReadOnlySpan passwordBytes) => + throw new NotImplementedException(SR.NotSupported_SubclassOverride); + + /// + /// When overridden in a derived class, imports an RFC 7468 textually + /// encoded key, replacing the keys for this object. + /// + /// The text of the PEM key to import. + /// + /// A derived type has not overridden this member. + /// + /// + /// Because each algorithm may have algorithm-specific PEM labels, the + /// default behavior will throw . + /// + public virtual void ImportFromPem(ReadOnlySpan input) => + throw new NotImplementedException(SR.NotSupported_SubclassOverride); + private delegate bool TryExportPbe( ReadOnlySpan password, PbeParameters pbeParameters,