From f37bfb357967a19c16ecc47a0a757db4140e2431 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Mon, 25 Mar 2019 16:53:38 +0100 Subject: [PATCH 1/6] Implement RSA privkey decryption via OpenSSL --- TunnelKit/Sources/Core/TLSBox.h | 2 ++ TunnelKit/Sources/Core/TLSBox.m | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/TunnelKit/Sources/Core/TLSBox.h b/TunnelKit/Sources/Core/TLSBox.h index d47e50d..d162e11 100644 --- a/TunnelKit/Sources/Core/TLSBox.h +++ b/TunnelKit/Sources/Core/TLSBox.h @@ -52,6 +52,8 @@ extern NSString *const TLSBoxPeerVerificationErrorNotification; @interface TLSBox : NSObject + (nullable NSString *)md5ForCertificatePath:(NSString *)path error:(NSError **)error; ++ (nullable NSString *)decryptedPrivateKeyFromPath:(NSString *)path passphrase:(NSString *)passphrase error:(NSError **)error; ++ (nullable NSString *)decryptedPrivateKeyFromPEM:(NSString *)pem passphrase:(NSString *)passphrase error:(NSError **)error; - (instancetype)initWithCAPath:(NSString *)caPath clientCertificatePath:(nullable NSString *)clientCertificatePath diff --git a/TunnelKit/Sources/Core/TLSBox.m b/TunnelKit/Sources/Core/TLSBox.m index 274b9d9..9b62356 100644 --- a/TunnelKit/Sources/Core/TLSBox.m +++ b/TunnelKit/Sources/Core/TLSBox.m @@ -39,6 +39,8 @@ #import #import #import +#import +#import #import "TLSBox.h" #import "Allocation.h" @@ -107,6 +109,61 @@ int TLSBoxVerifyPeer(int ok, X509_STORE_CTX *ctx) { return hex; } ++ (NSString *)decryptedPrivateKeyFromPath:(NSString *)path passphrase:(NSString *)passphrase error:(NSError * _Nullable __autoreleasing *)error +{ + BIO *bio; + if (!(bio = BIO_new_file([path cStringUsingEncoding:NSASCIIStringEncoding], "r"))) { + return NULL; + } + NSString *ret = [[self class] decryptedPrivateKeyFromBIO:bio passphrase:passphrase error:error]; + BIO_free(bio); + return ret; +} + ++ (NSString *)decryptedPrivateKeyFromPEM:(NSString *)pem passphrase:(NSString *)passphrase error:(NSError * _Nullable __autoreleasing *)error +{ + BIO *bio; + if (!(bio = BIO_new_mem_buf([pem cStringUsingEncoding:NSASCIIStringEncoding], (int)[pem length]))) { + return NULL; + } + NSString *ret = [[self class] decryptedPrivateKeyFromBIO:bio passphrase:passphrase error:error]; + BIO_free(bio); + return ret; +} + ++ (NSString *)decryptedPrivateKeyFromBIO:(BIO *)bio passphrase:(NSString *)passphrase error:(NSError * _Nullable __autoreleasing *)error +{ + RSA *rsaKey; + if (!(rsaKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, (void *)passphrase.UTF8String))) { + return NULL; + } + + EVP_PKEY *evpKey = EVP_PKEY_new(); + if (!EVP_PKEY_set1_RSA(evpKey, rsaKey)) { + EVP_PKEY_free(evpKey); + return NULL; + } + BIO *output = BIO_new(BIO_s_mem()); + if (!PEM_write_bio_PKCS8PrivateKey(output, evpKey, NULL, NULL, 0, NULL, NULL)) { + BIO_free(output); + EVP_PKEY_free(evpKey); + return NULL; + } + + const int decLength = (int)BIO_ctrl_pending(output); + char *decKeyBytes = malloc(decLength + 1); + if (BIO_read(output, decKeyBytes, decLength) < 0) { + BIO_free(output); + EVP_PKEY_free(evpKey); + return NULL; + } + BIO_free(output); + EVP_PKEY_free(evpKey); + + decKeyBytes[decLength] = '\0'; + return [NSString stringWithCString:decKeyBytes encoding:NSASCIIStringEncoding]; +} + - (instancetype)init { [NSException raise:NSInvalidArgumentException format:@"Use initWithCAPath:clientCertificatePath:clientKeyPath:"]; From e5393f81b8cff9f74b3253d08fc79a630b206e7d Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Mon, 25 Mar 2019 17:41:38 +0100 Subject: [PATCH 2/6] Test decrypt .pem with encrypted private key --- TunnelKit.xcodeproj/project.pbxproj | 30 ++++- TunnelKitTests/EncryptionTests.swift | 17 +++ TunnelKitTests/tunnelbear.enc.key | 54 +++++++++ TunnelKitTests/tunnelbear.enc.ovpn | 158 +++++++++++++++++++++++++++ TunnelKitTests/tunnelbear.key | 52 +++++++++ 5 files changed, 305 insertions(+), 6 deletions(-) create mode 100644 TunnelKitTests/tunnelbear.enc.key create mode 100644 TunnelKitTests/tunnelbear.enc.ovpn create mode 100644 TunnelKitTests/tunnelbear.key diff --git a/TunnelKit.xcodeproj/project.pbxproj b/TunnelKit.xcodeproj/project.pbxproj index 787406b..f72999a 100644 --- a/TunnelKit.xcodeproj/project.pbxproj +++ b/TunnelKit.xcodeproj/project.pbxproj @@ -61,8 +61,14 @@ 0E3B15C82152B05E00984B17 /* CryptoCTR.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E3B15C52152B05E00984B17 /* CryptoCTR.h */; }; 0E3B15C92152B05E00984B17 /* CryptoCTR.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B15C62152B05E00984B17 /* CryptoCTR.m */; }; 0E3B15CA2152B05E00984B17 /* CryptoCTR.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B15C62152B05E00984B17 /* CryptoCTR.m */; }; + 0E3B65742249253A00EFF4DA /* tunnelbear.enc.ovpn in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B656E224923EC00EFF4DA /* tunnelbear.enc.ovpn */; }; + 0E3B65752249253B00EFF4DA /* tunnelbear.enc.ovpn in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B656E224923EC00EFF4DA /* tunnelbear.enc.ovpn */; }; + 0E3B65762249253F00EFF4DA /* tunnelbear.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B65712249247E00EFF4DA /* tunnelbear.key */; }; + 0E3B65772249254000EFF4DA /* tunnelbear.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B65712249247E00EFF4DA /* tunnelbear.key */; }; 0E3E0F212108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; }; 0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; }; + 0E500EA622493B5B00CAE560 /* tunnelbear.enc.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.key */; }; + 0E500EA722493B5B00CAE560 /* tunnelbear.enc.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.key */; }; 0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */; }; 0E58BF3322405410006FB157 /* lzoconf.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF2F22405410006FB157 /* lzoconf.h */; }; 0E58BF3422405410006FB157 /* lzoconf.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF2F22405410006FB157 /* lzoconf.h */; }; @@ -76,14 +82,14 @@ 0E58BF3D2240547F006FB157 /* CompressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF3B2240547F006FB157 /* CompressionTests.swift */; }; 0E58BF4C22405C2F006FB157 /* StandardLZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF4922405C2F006FB157 /* StandardLZO.m */; }; 0E58BF4D22405C2F006FB157 /* StandardLZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF4922405C2F006FB157 /* StandardLZO.m */; }; - 0E58BF5622411F3D006FB157 /* LZO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF5522411F37006FB157 /* LZO.h */; }; - 0E58BF5722411F3E006FB157 /* LZO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF5522411F37006FB157 /* LZO.h */; }; - 0E58BF5922411FEF006FB157 /* LZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF5822411FEF006FB157 /* LZO.m */; }; - 0E58BF5A22411FEF006FB157 /* LZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF5822411FEF006FB157 /* LZO.m */; }; 0E58BF502240F98F006FB157 /* CompressionAlgorithmNative.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */; }; 0E58BF512240F98F006FB157 /* CompressionAlgorithmNative.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */; }; 0E58BF532240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */; }; 0E58BF542240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */; }; + 0E58BF5622411F3D006FB157 /* LZO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF5522411F37006FB157 /* LZO.h */; }; + 0E58BF5722411F3E006FB157 /* LZO.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E58BF5522411F37006FB157 /* LZO.h */; }; + 0E58BF5922411FEF006FB157 /* LZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF5822411FEF006FB157 /* LZO.m */; }; + 0E58BF5A22411FEF006FB157 /* LZO.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BF5822411FEF006FB157 /* LZO.m */; }; 0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; }; 0E749F5F2178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; }; 0E749F602178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; }; @@ -290,7 +296,10 @@ 0E39BCE7214B2AB60035E9DE /* ControlPacket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ControlPacket.m; sourceTree = ""; }; 0E3B15C52152B05E00984B17 /* CryptoCTR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoCTR.h; sourceTree = ""; }; 0E3B15C62152B05E00984B17 /* CryptoCTR.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptoCTR.m; sourceTree = ""; }; + 0E3B656E224923EC00EFF4DA /* tunnelbear.enc.ovpn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.enc.ovpn; sourceTree = ""; }; + 0E3B65712249247E00EFF4DA /* tunnelbear.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.key; sourceTree = ""; }; 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+PushReply.swift"; sourceTree = ""; }; + 0E500EA522493B5B00CAE560 /* tunnelbear.enc.key */ = {isa = PBXFileReference; lastKnownFileType = text; path = tunnelbear.enc.key; sourceTree = ""; }; 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlChannelTests.swift; sourceTree = ""; }; 0E58BF2F22405410006FB157 /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = ""; }; 0E58BF3022405410006FB157 /* lzodefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzodefs.h; sourceTree = ""; }; @@ -298,10 +307,10 @@ 0E58BF3222405410006FB157 /* minilzo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = minilzo.c; sourceTree = ""; }; 0E58BF3B2240547F006FB157 /* CompressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompressionTests.swift; sourceTree = ""; }; 0E58BF4922405C2F006FB157 /* StandardLZO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StandardLZO.m; sourceTree = ""; }; - 0E58BF5522411F37006FB157 /* LZO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LZO.h; sourceTree = ""; }; - 0E58BF5822411FEF006FB157 /* LZO.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LZO.m; sourceTree = ""; }; 0E58BF4F2240F98E006FB157 /* CompressionAlgorithmNative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressionAlgorithmNative.h; sourceTree = ""; }; 0E58BF522240FAA6006FB157 /* SessionProxy+CompressionAlgorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+CompressionAlgorithm.swift"; sourceTree = ""; }; + 0E58BF5522411F37006FB157 /* LZO.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LZO.h; sourceTree = ""; }; + 0E58BF5822411FEF006FB157 /* LZO.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LZO.m; sourceTree = ""; }; 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSTests.swift; sourceTree = ""; }; 0E6479DD212EAC96008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0E6479E0212EACD6008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -454,6 +463,9 @@ 0EB2B45A20F0BE4C004233D7 /* TestUtils.swift */, 0E749F612178911C00BB2701 /* pia-2048.pem */, 0E011F832196E25400BA59EE /* pia-hungary.ovpn */, + 0E3B656E224923EC00EFF4DA /* tunnelbear.enc.ovpn */, + 0E500EA522493B5B00CAE560 /* tunnelbear.enc.key */, + 0E3B65712249247E00EFF4DA /* tunnelbear.key */, ); path = TunnelKitTests; sourceTree = ""; @@ -928,6 +940,9 @@ buildActionMask = 2147483647; files = ( 0E011F852196E25900BA59EE /* pia-hungary.ovpn in Resources */, + 0E500EA622493B5B00CAE560 /* tunnelbear.enc.key in Resources */, + 0E3B65762249253F00EFF4DA /* tunnelbear.key in Resources */, + 0E3B65742249253A00EFF4DA /* tunnelbear.enc.ovpn in Resources */, 0E749F622178911D00BB2701 /* pia-2048.pem in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -961,6 +976,9 @@ buildActionMask = 2147483647; files = ( 0E011F862196E25A00BA59EE /* pia-hungary.ovpn in Resources */, + 0E500EA722493B5B00CAE560 /* tunnelbear.enc.key in Resources */, + 0E3B65772249254000EFF4DA /* tunnelbear.key in Resources */, + 0E3B65752249253B00EFF4DA /* tunnelbear.enc.ovpn in Resources */, 0EA82A3E2190B2BC007960EB /* pia-2048.pem in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/TunnelKitTests/EncryptionTests.swift b/TunnelKitTests/EncryptionTests.swift index 606e98b..3e2677f 100644 --- a/TunnelKitTests/EncryptionTests.swift +++ b/TunnelKitTests/EncryptionTests.swift @@ -95,6 +95,23 @@ class EncryptionTests: XCTestCase { print(md5) XCTAssertEqual(md5, exp) } + + func testPrivateKeyDecryption() { + let bundle = Bundle(for: EncryptionTests.self) + let encryptedPath = bundle.path(forResource: "tunnelbear", ofType: "enc.key")! + let decryptedPath = bundle.path(forResource: "tunnelbear", ofType: "key")! + + XCTAssertThrowsError(try TLSBox.decryptedPrivateKey(fromPath: encryptedPath, passphrase: "wrongone")) + let decryptedViaPath = try! TLSBox.decryptedPrivateKey(fromPath: encryptedPath, passphrase: "foobar") + print(decryptedViaPath) + let encryptedPEM = try! String(contentsOfFile: encryptedPath, encoding: .utf8) + let decryptedViaString = try! TLSBox.decryptedPrivateKey(fromPEM: encryptedPEM, passphrase: "foobar") + print(decryptedViaString) + XCTAssertEqual(decryptedViaPath, decryptedViaString) + + let expDecrypted = try! String(contentsOfFile: decryptedPath) + XCTAssertEqual(decryptedViaPath, expDecrypted) + } func testCTR() { let (client, server) = clientServer("aes-256-ctr", "sha256") diff --git a/TunnelKitTests/tunnelbear.enc.key b/TunnelKitTests/tunnelbear.enc.key new file mode 100644 index 0000000..1e82c4d --- /dev/null +++ b/TunnelKitTests/tunnelbear.enc.key @@ -0,0 +1,54 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,1C7216F89C74E56260FE71A9C1BAB35E + +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 +-----END RSA PRIVATE KEY----- diff --git a/TunnelKitTests/tunnelbear.enc.ovpn b/TunnelKitTests/tunnelbear.enc.ovpn new file mode 100644 index 0000000..c64ec13 --- /dev/null +++ b/TunnelKitTests/tunnelbear.enc.ovpn @@ -0,0 +1,158 @@ +client +dev tun0 +proto udp +comp-lzo +nobind +ns-cert-type server +persist-key +persist-tun +reneg-sec 0 +dhcp-option DNS 8.8.8.8 +dhcp-option DNS 8.8.4.4 +redirect-gateway +verb 1 +auth-user-pass + +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + + +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + + +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,1C7216F89C74E56260FE71A9C1BAB35E + +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 +-----END RSA PRIVATE KEY----- + +remote no.lazerpenguin.com 443 +cipher AES-256-CBC +auth SHA256 +keysize 256 diff --git a/TunnelKitTests/tunnelbear.key b/TunnelKitTests/tunnelbear.key new file mode 100644 index 0000000..6472551 --- /dev/null +++ b/TunnelKitTests/tunnelbear.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +1LXZrtjAB4U+/F7um6YyAavpHYq9hg== +-----END PRIVATE KEY----- From b07ec88ff2195f90b49502e957ceb46a1c0a3bc5 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Mon, 25 Mar 2019 15:51:43 +0100 Subject: [PATCH 3/6] Add passphrase parameter to ConfigurationParser Use it to decrypt encrypted PEMs. --- .../Sources/Core/ConfigurationParser.swift | 40 +++++++++++++------ TunnelKit/Sources/Core/CryptoContainer.swift | 8 ++++ TunnelKitTests/ConfigurationParserTests.swift | 6 +++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/TunnelKit/Sources/Core/ConfigurationParser.swift b/TunnelKit/Sources/Core/ConfigurationParser.swift index 60e2c46..01640bb 100644 --- a/TunnelKit/Sources/Core/ConfigurationParser.swift +++ b/TunnelKit/Sources/Core/ConfigurationParser.swift @@ -114,25 +114,27 @@ public class ConfigurationParser { Parses an .ovpn file from an URL. - Parameter url: The URL of the configuration file. + - Parameter passphrase: The optional passphrase for encrypted data. - Parameter returnsStripped: When `true`, stores the stripped file into `ParsingResult.strippedLines`. Defaults to `false`. - Returns: The `ParsingResult` outcome of the parsing. - Throws: `ParsingError` if the configuration file is wrong or incomplete. */ - public static func parsed(fromURL url: URL, returnsStripped: Bool = false) throws -> ParsingResult { + public static func parsed(fromURL url: URL, passphrase: String? = nil, returnsStripped: Bool = false) throws -> ParsingResult { let lines = try String(contentsOf: url).trimmedLines() - return try parsed(fromLines: lines, originalURL: url, returnsStripped: returnsStripped) + return try parsed(fromLines: lines, passphrase: passphrase, originalURL: url, returnsStripped: returnsStripped) } /** Parses an .ovpn file as an array of lines. - Parameter lines: The array of lines holding the configuration. + - Parameter passphrase: The optional passphrase for encrypted data. - Parameter originalURL: The optional original URL of the configuration file. - Parameter returnsStripped: When `true`, stores the stripped file into `ParsingResult.strippedLines`. Defaults to `false`. - Returns: The `ParsingResult` outcome of the parsing. - Throws: `ParsingError` if the configuration file is wrong or incomplete. */ - public static func parsed(fromLines lines: [String], originalURL: URL? = nil, returnsStripped: Bool = false) throws -> ParsingResult { + public static func parsed(fromLines lines: [String], passphrase: String? = nil, originalURL: URL? = nil, returnsStripped: Bool = false) throws -> ParsingResult { var strippedLines: [String]? = returnsStripped ? [] : nil var warning: ParsingError? = nil @@ -209,10 +211,20 @@ public class ConfigurationParser { clientCertificate = CryptoContainer(pem: currentBlock.joined(separator: "\n")) case "key": + let isEncrypted = normalizeEncryptedPEMBlock(block: ¤tBlock) let container = CryptoContainer(pem: currentBlock.joined(separator: "\n")) - clientKey = container - if container.isEncrypted { - unsupportedError = ParsingError.unsupportedConfiguration(option: "encrypted client certificate key") + if isEncrypted { + guard let passphrase = passphrase else { + unsupportedError = ParsingError.unsupportedConfiguration(option: "encrypted client certificate key (missing passphrase)") + break + } + do { + clientKey = try container.decrypted(with: passphrase) + } catch let e { + unsupportedError = ParsingError.unsupportedConfiguration(option: e.localizedDescription) + } + } else { + clientKey = container } case "tls-auth": @@ -451,6 +463,16 @@ public class ConfigurationParser { warning: warning ) } + + private static func normalizeEncryptedPEMBlock(block: inout [String]) -> Bool { + + // XXX: restore blank line after encryption header (easier than tweaking trimmedLines) + if block.count >= 3 && block[1].contains("ENCRYPTED") { + block.insert("", at: 3) + return true + } + return false + } } private extension SocketType { @@ -472,9 +494,3 @@ extension String { } } } - -extension CryptoContainer { - var isEncrypted: Bool { - return pem.contains("ENCRYPTED") - } -} diff --git a/TunnelKit/Sources/Core/CryptoContainer.swift b/TunnelKit/Sources/Core/CryptoContainer.swift index c56ea6c..d556d34 100644 --- a/TunnelKit/Sources/Core/CryptoContainer.swift +++ b/TunnelKit/Sources/Core/CryptoContainer.swift @@ -36,6 +36,7 @@ // import Foundation +import __TunnelKitNative /// Represents a cryptographic container in PEM format. public struct CryptoContainer: Equatable { @@ -73,3 +74,10 @@ extension CryptoContainer: Codable { try container.encode(pem) } } + +extension CryptoContainer { + func decrypted(with passphrase: String) throws -> CryptoContainer { + let decryptedPEM = try TLSBox.decryptedPrivateKey(fromPEM: pem, passphrase: passphrase) + return CryptoContainer(pem: decryptedPEM) + } +} diff --git a/TunnelKitTests/ConfigurationParserTests.swift b/TunnelKitTests/ConfigurationParserTests.swift index 61bb49d..fec82bb 100644 --- a/TunnelKitTests/ConfigurationParserTests.swift +++ b/TunnelKitTests/ConfigurationParserTests.swift @@ -80,6 +80,12 @@ class ConfigurationParserTests: XCTestCase { XCTAssertThrowsError(try ConfigurationParser.parsed(fromLines: lines)) } + func testEncryptedCertificateKey() throws { + let url = Bundle(for: ConfigurationParserTests.self).url(forResource: "tunnelbear", withExtension: "enc.ovpn")! + XCTAssertThrowsError(try ConfigurationParser.parsed(fromURL: url)) + XCTAssertNoThrow(try ConfigurationParser.parsed(fromURL: url, passphrase: "foobar")) + } + private func url(withName name: String) -> URL { return Bundle(for: ConfigurationParserTests.self).url(forResource: name, withExtension: "ovpn")! } From ffcccb542043adc1cf661dcfad8d17db7a8efab1 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Mon, 25 Mar 2019 18:30:52 +0100 Subject: [PATCH 4/6] Throw specific error on missing passphrase So that client can retry with a passphrase. --- TunnelKit/Sources/Core/ConfigurationParser.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TunnelKit/Sources/Core/ConfigurationParser.swift b/TunnelKit/Sources/Core/ConfigurationParser.swift index 01640bb..27f4fca 100644 --- a/TunnelKit/Sources/Core/ConfigurationParser.swift +++ b/TunnelKit/Sources/Core/ConfigurationParser.swift @@ -40,6 +40,9 @@ public class ConfigurationParser { /// The file includes an unsupported option. case unsupportedConfiguration(option: String) + + /// Passphrase required to decrypt private keys. + case encryptionPassphrase } /// Result of the parser. @@ -215,7 +218,7 @@ public class ConfigurationParser { let container = CryptoContainer(pem: currentBlock.joined(separator: "\n")) if isEncrypted { guard let passphrase = passphrase else { - unsupportedError = ParsingError.unsupportedConfiguration(option: "encrypted client certificate key (missing passphrase)") + unsupportedError = ParsingError.encryptionPassphrase break } do { From 00c76f707f511a2c43aee73a9e7f53743f763849 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Mon, 25 Mar 2019 19:24:35 +0100 Subject: [PATCH 5/6] Throw specific error if unable to decrypt Normally a bad passphrase. --- TunnelKit/Sources/Core/ConfigurationParser.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TunnelKit/Sources/Core/ConfigurationParser.swift b/TunnelKit/Sources/Core/ConfigurationParser.swift index 27f4fca..7af7ca0 100644 --- a/TunnelKit/Sources/Core/ConfigurationParser.swift +++ b/TunnelKit/Sources/Core/ConfigurationParser.swift @@ -43,6 +43,9 @@ public class ConfigurationParser { /// Passphrase required to decrypt private keys. case encryptionPassphrase + + /// Encryption passphrase is incorrect or key is corrupt. + case unableToDecrypt(error: Error) } /// Result of the parser. @@ -224,7 +227,7 @@ public class ConfigurationParser { do { clientKey = try container.decrypted(with: passphrase) } catch let e { - unsupportedError = ParsingError.unsupportedConfiguration(option: e.localizedDescription) + unsupportedError = ParsingError.unableToDecrypt(error: e) } } else { clientKey = container From b5b68474af78e22b9b07a73b6cb5ab815c64e35c Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Mon, 25 Mar 2019 20:30:01 +0100 Subject: [PATCH 6/6] Update CHANGELOG and README Fixes #72 --- CHANGELOG.md | 1 + README.md | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cf1b14..ddd13ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Scramble endpoints via `--remote-random`. [#76](https://github.com/keeshux/tunnelkit/issues/76) +- Support for encrypted certificate private keys. [#72](https://github.com/keeshux/tunnelkit/issues/72) ### Fixed diff --git a/README.md b/README.md index edb9559..bcab6d9 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,6 @@ Unsupported: - Compression via `--compress` other than empty or `lzo` - Proxy - External file references (inline `` only) -- Encrypted client certificate keys - `` blocks Ignored: