diff --git a/Sources/_CryptoExtras/AES/AES_CBC.swift b/Sources/_CryptoExtras/AES/AES_CBC.swift index 3363ddbd..2bebccca 100644 --- a/Sources/_CryptoExtras/AES/AES_CBC.swift +++ b/Sources/_CryptoExtras/AES/AES_CBC.swift @@ -134,7 +134,7 @@ extension AES { } if !noPadding { - try plaintext.trimPadding() + try plaintext.trimCBCPadding() } return plaintext } @@ -193,7 +193,7 @@ extension AES._CBC { } extension Data { - fileprivate mutating func trimPadding() throws { + mutating func trimCBCPadding() throws { guard let paddingBytes = self.last else { // Degenerate case, empty string. This is forbidden: // we must always pad. diff --git a/Sources/_CryptoExtras/RSA/RSA.swift b/Sources/_CryptoExtras/RSA/RSA.swift index a7d5652c..3877f64c 100644 --- a/Sources/_CryptoExtras/RSA/RSA.swift +++ b/Sources/_CryptoExtras/RSA/RSA.swift @@ -179,6 +179,10 @@ extension _RSA.Signing { throw CryptoKitError.incorrectParameterSize } } + + public init(encryptedPEMRepresentation: String, encryptionPassword: String) throws { + self.backing = try BackingPrivateKey(encryptedPEMRepresentation: encryptedPEMRepresentation, encryptionPassword: encryptionPassword) + } /// Construct an RSA private key from a DER representation. /// diff --git a/Sources/_CryptoExtras/RSA/RSA_boring.swift b/Sources/_CryptoExtras/RSA/RSA_boring.swift index 1baf9b40..9fb0306a 100644 --- a/Sources/_CryptoExtras/RSA/RSA_boring.swift +++ b/Sources/_CryptoExtras/RSA/RSA_boring.swift @@ -63,7 +63,7 @@ internal struct BoringSSLRSAPublicKey: Sendable { } func getKeyPrimitives() throws -> (n: Data, e: Data) { - try self.backing.getKeyPrimitives() + self.backing.getKeyPrimitives() } } diff --git a/Sources/_CryptoExtras/RSA/RSA_security.swift b/Sources/_CryptoExtras/RSA/RSA_security.swift index 114012de..3d86285c 100644 --- a/Sources/_CryptoExtras/RSA/RSA_security.swift +++ b/Sources/_CryptoExtras/RSA/RSA_security.swift @@ -97,7 +97,24 @@ internal struct SecurityRSAPrivateKey: @unchecked Sendable { default: throw _CryptoRSAError.invalidPEMDocument } - + } + + init(encryptedPEMRepresentation: String, encryptionPassword: String) throws { + let document = try EncryptedPEMDocument(pemEncoded: encryptedPEMRepresentation) + let pem = try document.decrypt(withPassword: encryptionPassword) + + switch pem.discriminator { + case _RSA.PKCS1KeyType: + // This is what is expected by Security.framework + self = try .init(derRepresentation: pem.derBytes) + case _RSA.PKCS8KeyType: + guard let pkcs8Bytes = pem.derBytes.pkcs8RSAKeyBytes else { + throw _CryptoRSAError.invalidPEMDocument + } + self = try .init(derRepresentation: pkcs8Bytes) + default: + throw _CryptoRSAError.invalidPEMDocument + } } init(derRepresentation: Bytes) throws { @@ -457,6 +474,16 @@ extension Int { } } +extension [UInt8] { + var pkcs8RSAKeyBytes: [UInt8]? { + let bytes = Data(self).pkcs8RSAKeyBytes + guard let bytes else { + return nil + } + return [UInt8](bytes) + } +} + extension UInt { // Bytes needed to store a given integer in 7 bit bytes. fileprivate var neededBytes: Int { diff --git a/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+EncryptionScheme.swift b/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+EncryptionScheme.swift new file mode 100644 index 00000000..f3e260d8 --- /dev/null +++ b/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+EncryptionScheme.swift @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2021 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import SwiftASN1 + +extension EncryptedPEMDocument { + struct EncryptionScheme: DERParseable { + static var defaultIdentifier: SwiftASN1.ASN1Identifier { .sequence } + + let encryptionAlgorithm: ASN1ObjectIdentifier + let encryptionAlgorithmParameters: ASN1OctetString + + init(encryptionAlgorithm: ASN1ObjectIdentifier, encryptionAlgorithmParameters: ASN1OctetString) { + self.encryptionAlgorithm = encryptionAlgorithm + self.encryptionAlgorithmParameters = encryptionAlgorithmParameters + } + + init(derEncoded node: ASN1Node) throws { + self = try DER.sequence(node, identifier: .sequence) { nodes in + let encryptionAlgorithm = try ASN1ObjectIdentifier(derEncoded: &nodes) + let encryptionAlgorithmParameters = try ASN1OctetString(derEncoded: &nodes) + + return .init(encryptionAlgorithm: encryptionAlgorithm, encryptionAlgorithmParameters: encryptionAlgorithmParameters) + } + } + } +} + diff --git a/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+KeyDerivationFunction.swift b/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+KeyDerivationFunction.swift new file mode 100644 index 00000000..54341cf9 --- /dev/null +++ b/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+KeyDerivationFunction.swift @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2021 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import SwiftASN1 + +extension EncryptedPEMDocument { + struct KeyDerivationFunction: DERParseable { + static var defaultIdentifier: ASN1Identifier { .sequence } + + let algorithm: ASN1ObjectIdentifier + let parameters: ASN1Any + + init(algorithm: ASN1ObjectIdentifier, parameters: ASN1Any) { + self.algorithm = algorithm + self.parameters = parameters + } + + init(derEncoded node: ASN1Node) throws { + self = try DER.sequence(node, identifier: .sequence) { nodes in + let algorithm = try ASN1ObjectIdentifier(derEncoded: &nodes) + let parameters = try ASN1Any(derEncoded: &nodes) + + return .init(algorithm: algorithm, parameters: parameters) + } + } + } +} + +extension EncryptedPEMDocument.KeyDerivationFunction { + // PBKDF2-params ::= SEQUENCE { + // salt CHOICE { + // specified OCTET STRING, + // otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} + // }, + // iterationCount INTEGER (1..MAX), + // keyLength INTEGER (1..MAX) OPTIONAL, + // prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 + // } + struct PBKDF2Parameters: DERParseable { + static var defaultIdentifier: ASN1Identifier { .sequence } + + let salt: ASN1OctetString + let iterationCount: any ASN1IntegerRepresentable + let hashFunction: HashFunction + + init(salt: ASN1OctetString, iterationCount: any ASN1IntegerRepresentable, hashFunction: HashFunction) { + self.salt = salt + self.iterationCount = iterationCount + self.hashFunction = hashFunction + } + + init(derEncoded node: ASN1Node) throws { + self = try DER.sequence(node, identifier: .sequence) { nodes in + let salt = try ASN1OctetString(derEncoded: &nodes) + let iterationCount = try Int(derEncoded: &nodes) + let hashFunction = try HashFunction(derEncoded: &nodes) + + return .init(salt: salt, iterationCount: iterationCount, hashFunction: hashFunction) + } + } + } +} + +extension EncryptedPEMDocument.KeyDerivationFunction.PBKDF2Parameters { + struct HashFunction: DERParseable { + static var defaultIdentifier: ASN1Identifier { .sequence } + + let objectIdentifer: ASN1ObjectIdentifier + let null: ASN1Null + + init(objectIdentifer: ASN1ObjectIdentifier, null: ASN1Null) { + self.objectIdentifer = objectIdentifer + self.null = null + } + + init(derEncoded node: ASN1Node) throws { + self = try DER.sequence(node, identifier: .sequence) { nodes in + let objectIdentifer = try ASN1ObjectIdentifier(derEncoded: &nodes) + let null = try ASN1Null(derEncoded: &nodes) + + return .init(objectIdentifer: objectIdentifer, null: null) + } + } + } +} + +extension KDF.Insecure.PBKDF2.HashFunction { + static func from(objectIdentifier: ASN1ObjectIdentifier) -> Self? { + switch objectIdentifier.oidComponents { + case [2, 16, 840, 1, 101, 3, 4, 2, 1], + [1, 2, 840, 113549, 2, 9]: // hmacWithSHA256 + .sha256 + case [2, 16, 840, 1, 101, 3, 4, 2, 2]: + .sha384 + case [2, 16, 840, 1, 101, 3, 4, 2, 3]: + .sha512 + default: nil + } + } +} diff --git a/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+PBES2Parameters.swift b/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+PBES2Parameters.swift new file mode 100644 index 00000000..a2c1064a --- /dev/null +++ b/Sources/_CryptoExtras/Util/ASN1/EncryptedPEMDocument+PBES2Parameters.swift @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2021 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import SwiftASN1 + +extension EncryptedPEMDocument { + struct PBES2Parameters: DERParseable { + static var defaultIdentifier: ASN1Identifier { .sequence } + + let keyDerivationFunction: KeyDerivationFunction + let encryptionScheme: EncryptionScheme + + init(keyDerivationFunction: KeyDerivationFunction, encryptionScheme: EncryptionScheme) { + self.keyDerivationFunction = keyDerivationFunction + self.encryptionScheme = encryptionScheme + } + + init(derEncoded node: ASN1Node) throws { + self = try DER.sequence(node, identifier: .sequence) { nodes in + let keyDerivationFunction = try KeyDerivationFunction(derEncoded: &nodes) + let encryptionScheme = try EncryptionScheme(derEncoded: &nodes) + + return .init(keyDerivationFunction: keyDerivationFunction, encryptionScheme: encryptionScheme) + } + } + } +} diff --git a/Sources/_CryptoExtras/Util/EncryptedPEMDocument.swift b/Sources/_CryptoExtras/Util/EncryptedPEMDocument.swift new file mode 100644 index 00000000..ec52ad1a --- /dev/null +++ b/Sources/_CryptoExtras/Util/EncryptedPEMDocument.swift @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2021 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Crypto +import SwiftASN1 +import Foundation +@_implementationOnly import CCryptoBoringSSL + +// EncryptedPrivateKeyInfo ::= SEQUENCE { +// encryptionAlgorithm EncryptionAlgorithmIdentifier, +// encryptedData EncryptedData +// } +// +// EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier +// +// EncryptedData ::= OCTET STRING +struct EncryptedPEMDocument: PEMParseable { + let algorithmIdentifier: RFC5480AlgorithmIdentifier + let encryptedData: ASN1OctetString + + init(algorithmIdentifier: RFC5480AlgorithmIdentifier, encryptedData: ASN1OctetString) { + self.algorithmIdentifier = algorithmIdentifier + self.encryptedData = encryptedData + } + + static var defaultPEMDiscriminator: String { + "ENCRYPTED PRIVATE KEY" + } + + init(derEncoded node: ASN1Node) throws { + self = try DER.sequence(node, identifier: .sequence) { nodes in + let algorithmIdentifier = try RFC5480AlgorithmIdentifier(derEncoded: &nodes) + let encryptedData = try ASN1OctetString(derEncoded: &nodes) + + return .init(algorithmIdentifier: algorithmIdentifier, encryptedData: encryptedData) + } + } + + func decrypt(withPassword password: String) throws -> PEMDocument { + let algorithm = self.algorithmIdentifier.algorithm + + guard let parameters = self.algorithmIdentifier.parameters else { + throw _CryptoRSAError.invalidPEMDocument + } + + switch algorithm { + case .pkcs5PBES2: + let pbes2Params = try PBES2Parameters(asn1Any: parameters) + let pbkdf2Params = try KeyDerivationFunction.PBKDF2Parameters(asn1Any: pbes2Params.keyDerivationFunction.parameters) + + let hashFunction = pbkdf2Params.hashFunction + + let derivedKey = try KDF.Insecure.PBKDF2.deriveKey( + from: [UInt8](password.utf8), + salt: pbkdf2Params.salt.bytes, + using: .from(objectIdentifier: hashFunction.objectIdentifer)!, + outputByteCount: pbes2Params.encryptionScheme.encryptionAlgorithm.encryptionAlgorithmKeyLength, + unsafeUncheckedRounds: pbkdf2Params.iterationCount as! Int + ) + + let decryption: Data? = switch pbes2Params.encryptionScheme.encryptionAlgorithm { + case .aes128_CBC, .aes192_CBC, .aes256_CBC: + try AES._CBC.decrypt( + encryptedData.bytes, + using: derivedKey, + iv: .init(ivBytes: pbes2Params.encryptionScheme.encryptionAlgorithmParameters.bytes) + ) + case .des_EDE3_CBC: + try TripleDES.CBC.decrypt( + encryptedData.bytes, + using: derivedKey, + iv: pbes2Params.encryptionScheme.encryptionAlgorithmParameters.bytes + ) + default: nil + } + + return PEMDocument(type: "PRIVATE KEY", derBytes: [UInt8](decryption!)) + default: + break + } + + return try PEMDocument(pemString: "") + } +} + +extension ASN1ObjectIdentifier { + static let pkcs5PBES2 = ASN1ObjectIdentifier("1.2.840.113549.1.5.13") + static let pkcs5PBKDF2 = ASN1ObjectIdentifier("1.2.840.113549.1.5.12") + static let pkcs5PBE_MD5_DES_CBC = ASN1ObjectIdentifier("1.2.840.113549.1.5.3") + static let pkcs5PBE_MD5_RC2_CBC = ASN1ObjectIdentifier("1.2.840.113549.1.5.6") + static let pkcs5PBE_SHA1_DES_CBC = ASN1ObjectIdentifier("1.2.840.113549.1.5.10") + static let pkcs5PBE_SHA1_RC2_CBC = ASN1ObjectIdentifier("1.2.840.113549.1.5.11") + + static let pkcs5Scrypt = ASN1ObjectIdentifier("1.3.6.1.4.1.11591.4.11") +} + +// Encryption schemes +extension ASN1ObjectIdentifier { + static let aes128_CBC = ASN1ObjectIdentifier("2.16.840.1.101.3.4.1.2") + static let aes192_CBC = ASN1ObjectIdentifier("2.16.840.1.101.3.4.1.22") + static let aes256_CBC = ASN1ObjectIdentifier("2.16.840.1.101.3.4.1.42") + static let des_EDE3_CBC = ASN1ObjectIdentifier("1.2.840.113549.3.7") +} + +extension ASN1ObjectIdentifier { + var encryptionAlgorithmKeyLength: Int { + switch self { + case .aes128_CBC: 16 + case .aes192_CBC: 24 + case .aes256_CBC: 32 + case .des_EDE3_CBC: 24 + default: fatalError("Not an encryption algorithm") + } + } +} + +fileprivate enum TripleDES { + fileprivate enum CBC { + static func decrypt(_ encryptedData: ArraySlice, using key: SymmetricKey, iv: ArraySlice) throws -> Data { + try encryptedData.withUnsafeBytes { encryptedPtr in + func toDESBlock(_ bytes: UnsafeBufferPointer, paddedBy padding: Int = 0) throws -> DES_cblock { + guard let baseAddress = bytes.baseAddress else { + throw _CryptoRSAError.invalidPEMDocument + } + + let bytes = baseAddress.advanced(by: padding) + return DES_cblock(bytes: ( + bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7] + )) + } + + var output = [UInt8](repeating: 0, count: encryptedData.count) + + var ks1 = DES_key_schedule(), ks2 = DES_key_schedule(), ks3 = DES_key_schedule() + try key.withUnsafeBytes { keyPtr in + guard keyPtr.count >= 24 else { throw _CryptoRSAError.invalidPEMDocument } + + let keyBytes = keyPtr.bindMemory(to: UInt8.self) + + var key1 = try toDESBlock(keyBytes) + var key2 = try toDESBlock(keyBytes, paddedBy: 8) + var key3 = try toDESBlock(keyBytes, paddedBy: 16) + + CCryptoBoringSSL_DES_set_key_unchecked(&key1, &ks1) + CCryptoBoringSSL_DES_set_key_unchecked(&key2, &ks2) + CCryptoBoringSSL_DES_set_key_unchecked(&key3, &ks3) + } + + var iv = try iv.withUnsafeBytes { ivPtr -> DES_cblock in + let ivBytes = ivPtr.bindMemory(to: UInt8.self) + return try toDESBlock(ivBytes) + } + + CCryptoBoringSSL_DES_ede3_cbc_encrypt( + encryptedPtr.baseAddress!, + &output, + encryptedPtr.count, + &ks1, + &ks2, + &ks3, + &iv, + 0 + ) + + var result = Data(output) + try result.trimCBCPadding() + return result + } + } + } +} diff --git a/Tests/_CryptoExtrasTests/EncryptedPEMTests.swift b/Tests/_CryptoExtrasTests/EncryptedPEMTests.swift new file mode 100644 index 00000000..5c907eec --- /dev/null +++ b/Tests/_CryptoExtrasTests/EncryptedPEMTests.swift @@ -0,0 +1,113 @@ + +import XCTest +import _CryptoExtras + +final class EncryptedPEMTests: XCTestCase { + func testPBES2WithAES128EncryptedKeyInit() { + let pbes2WithAES128EncryptedPrivateKey = """ + -----BEGIN ENCRYPTED PRIVATE KEY----- + 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 + -----END ENCRYPTED PRIVATE KEY----- + """ + + XCTAssertNoThrow( + try _RSA.Signing.PrivateKey( + encryptedPEMRepresentation: pbes2WithAES128EncryptedPrivateKey, + encryptionPassword: "foobar" + ) + ) + XCTAssertThrowsError( + try _RSA.Signing.PrivateKey( + encryptedPEMRepresentation: pbes2WithAES128EncryptedPrivateKey, + encryptionPassword: "wrong" + ) + ) + } + + func testPBES2WithTripleDESKeyEncryptedKeyInit() { + let pbes2WithTripleDESEncryptedPrivateKey = """ + -----BEGIN ENCRYPTED PRIVATE KEY----- + MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQ8HZLW3BDKXdsGjxA + 5BM8GgICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIUo0QnIb9O+wEggTI + GqGG0X9OWxs8opGqJ6ynfJzCUy1TJh9CGJgBBVOMS8zqz7qAkBCKhT+VPCtn7W0g + GTf+OhOkj7YnmN/GSwbih/O33NFXoVQrP+kJOTRYFne2zVQ5KvG48oN3P7T4tHMP + zRqq7+qpz6Y0906z/6RmVZWEPryAb0xYEd2DhdX4wBMyHfTf28u10ivEsfTWa5/5 + /n4ENmwAce2MLUbvNGgtXvgbiDn5ITj17Reyal3hTzRoL3J6kLj6xFpBkaAAvvQP + O8FGaVuvi4seeWPVAwwuksRiCwA+wPi3eyREPwG8Q4tS2IKwJqUrbPjrIhxl7HwK + bb2iaQ+es+FZIHXHWvfWiEUyDs2OMcErlUqx8Qaf9K/3o8KFdyqZ7qOKNjK+Z0BC + AHelXjvO62N/sNoK8318LYOkCZ1Wd820JdSTac3AVy9BGQRu7GfhcpjjNbOxsjhz + HSnrZR8PIRNujTyLC8b2fzsTpDNLUE6KYiNzZWfUDOVMmm9xi64kwCMvsKsLd47n + 4VdaPHaqqSA3XkXIDyqAZUKo6r2CUkJH6CYKuVLl6GsA6lLFxVCHtdbQu6MopymO + 0+XkLTJrZItEB4ZIbtG88/ubnYOPqOn7Jvi7W8TEDBXw9inGO4osj7wSnWNEsTRx + 8P/uF9ygpKTANuR84welaJk6c3pxf96esfmxkp7XxGdRx9o0OWbSqB1C4LUjWmKs + LpPF8TvnzFlZyfyW5VyzOs8/4zNO7B0S6X5Ywwytobo0G0/6/eilFIPGZfLTz7gw + 2LPMYKgi+OjE27KGUS3fSDlVkcQqrfrADchtEM6bSYHU1B0K8QE2bkRVM97DRVTv + lngqxvr9yeE+ILCGOf/kTfqGqvoampUUUUMi8is80oSlSVApYiZ3uWJWOMsHlH7X + H1sONAARzhbm+BQ7QRFTH41mMmHIzNSuXVYItRxbC4VkbRqMzLCfZtsCCZ7Mupo7 + a9FdDMDsLeA28EDTESzWEPREk4i0wvJ1QRLdQFJ9GL+RP/YsV1GEwRqHu9lsZCAL + Oz83V41/NfSuTrykZFKaLA2D4DjVGbyinxxcThUL/3u3k98EjBKdfMj6wMF8hKCx + eYvowNOJUdMG3+i7Bo5rKhKZ5mIeRP3MGvelvSQ8gXm03pM255iDV441Ir4F/mpJ + TaMXySqhZef4Ls6tkxsq7E2mXhsPkJVy/hSmnbqZi3FltMGvkbiM3aqNJQcG67mO + NX66Zlbb8JHi6OG8o7H3u6i3BTeyvQkPO0n+sUWrYo1vqemykDUHAdqLdSIdC4pb + kCvRncCw729CD1B3IVkvSZ6NTqNxGCqmc1g/6bkqCaNOXXOqiT4Fzxxv+FCt2sGf + m2G8BvdBVILRRVSGgmG7ahvIY5O+911duS6vkzoxF39VJjrYXcqzKRh71zfAM3J0 + h9GLgrI+lZ7HYCi4eDsSMOdARfL6C7beA6Jaa4snHfGNNrwCECuV0zKrB61n33nN + wE1Nc+gPZ4rbYeYUa8EvdchNB5JdMTyKqOAHrHrM4EberwnZAZMk4Aal/PLAup5L + mrdalZF0qlLUetwUPmAGMuW34igiV084ecKxsuZWXvKtLTHhiTN4NYBgV2rvJ2LE + PRiLIoKv+M+qjywhjPeQbD70byOdIx5J + -----END ENCRYPTED PRIVATE KEY----- + """ + + XCTAssertNoThrow( + try _RSA.Signing.PrivateKey( + encryptedPEMRepresentation: pbes2WithTripleDESEncryptedPrivateKey, + encryptionPassword: "foobar" + ) + ) + XCTAssertThrowsError( + try _RSA.Signing.PrivateKey( + encryptedPEMRepresentation: pbes2WithTripleDESEncryptedPrivateKey, + encryptionPassword: "wrong" + ) + ) + } +} diff --git a/Tests/_CryptoExtrasTests/TestRSASigning.swift b/Tests/_CryptoExtrasTests/TestRSASigning.swift index 07da7c12..2c8405bc 100644 --- a/Tests/_CryptoExtrasTests/TestRSASigning.swift +++ b/Tests/_CryptoExtrasTests/TestRSASigning.swift @@ -730,7 +730,6 @@ final class TestRSASigning: XCTestCase { K8TpFCFPBP/Yv1Kngovn4O1MskoxTQraRBDjfC6O7OfcSCSMuVgB0Oofcp9iQjLA HV3KOnbYqvzmFv7OWnAszkTh -----END PRIVATE KEY----- - """ let key = try _RSA.Signing.PrivateKey(pemRepresentation: pemKey)