From 88dbad6054f3ff7248e48d7f2b268054da736ff9 Mon Sep 17 00:00:00 2001 From: Andrew Lees <32634907+Andrew-Lees11@users.noreply.github.com> Date: Tue, 16 Jul 2019 10:17:37 +0100 Subject: [PATCH] feat: Ignore newlines in key generation and allow DER formatting (#68) * feat: Ignore newlines in key generation and allow DER formatting * Add Jazzy docs for missingPEMHeaders --- Sources/SwiftJWT/BlueRSA.swift | 36 ++++++++++++++++++++++++----- Sources/SwiftJWT/JWTError.swift | 5 +++- Tests/SwiftJWTTests/TestJWT.swift | 10 ++++++++ Tests/SwiftJWTTests/privateRSA.der | Bin 0 -> 1194 bytes Tests/SwiftJWTTests/publicRSA.der | Bin 0 -> 294 bytes 5 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 Tests/SwiftJWTTests/privateRSA.der create mode 100644 Tests/SwiftJWTTests/publicRSA.der diff --git a/Sources/SwiftJWT/BlueRSA.swift b/Sources/SwiftJWT/BlueRSA.swift index 449478c..675aaa5 100644 --- a/Sources/SwiftJWT/BlueRSA.swift +++ b/Sources/SwiftJWT/BlueRSA.swift @@ -49,10 +49,22 @@ class BlueRSA: SignerAlgorithm, VerifierAlgorithm { Log.error("macOS 10.12.0 (Sierra) or higher or iOS 10.0 or higher is required by CryptorRSA") throw JWTError.osVersionToLow } - guard let keyString = String(data: key, encoding: .utf8) else { - throw JWTError.invalidPrivateKey + // Convert PEM format to DER + let keyDer: Data + if let keyString = String(data: key, encoding: .utf8) { + let strippedKey = String(keyString.filter { !" \n\t\r".contains($0) }) + var pemComponents = strippedKey.components(separatedBy: "-----") + guard pemComponents.count >= 5 else { + throw JWTError.missingPEMHeaders + } + guard let der = Data(base64Encoded: pemComponents[2]) else { + throw JWTError.invalidPrivateKey + } + keyDer = der + } else { + keyDer = key } - let privateKey = try CryptorRSA.createPrivateKey(withPEM: keyString) + let privateKey = try CryptorRSA.createPrivateKey(with: keyDer) let myPlaintext = CryptorRSA.createPlaintext(with: data) guard let signedData = try myPlaintext.signed(with: privateKey, algorithm: algorithm, usePSS: usePSS) else { throw JWTError.invalidPrivateKey @@ -85,10 +97,22 @@ class BlueRSA: SignerAlgorithm, VerifierAlgorithm { case .privateKey: return false case .publicKey: - guard let keyString = String(data: key, encoding: .utf8) else { - return false + // Convert PEM format to DER + let keyDer: Data + if let keyString = String(data: key, encoding: .utf8) { + let strippedKey = String(keyString.filter { !" \n\t\r".contains($0) }) + var pemComponents = strippedKey.components(separatedBy: "-----") + guard pemComponents.count >= 5 else { + return false + } + guard let der = Data(base64Encoded: pemComponents[2]) else { + return false + } + keyDer = der + } else { + keyDer = key } - publicKey = try CryptorRSA.createPublicKey(withPEM: keyString) + publicKey = try CryptorRSA.createPublicKey(with: keyDer) case .certificate: publicKey = try CryptorRSA.createPublicKey(extractingFrom: key) } diff --git a/Sources/SwiftJWT/JWTError.swift b/Sources/SwiftJWT/JWTError.swift index d26be22..02765aa 100644 --- a/Sources/SwiftJWT/JWTError.swift +++ b/Sources/SwiftJWT/JWTError.swift @@ -27,7 +27,7 @@ public struct JWTError: Error, Equatable { private let internalError: InternalError private enum InternalError { - case invalidJWTString, failedVerification, osVersionToLow, invalidPrivateKey, invalidData, invalidKeyID + case invalidJWTString, failedVerification, osVersionToLow, invalidPrivateKey, invalidData, invalidKeyID, missingPEMHeaders } /// Error when an invalid JWT String is provided @@ -48,6 +48,9 @@ public struct JWTError: Error, Equatable { /// Error when the KeyID field `kid` in the JWT header fails to generate a JWTSigner or JWTVerifier public static let invalidKeyID = JWTError(localizedDescription: "The JWT KeyID `kid` header failed to generate a JWTSigner/JWTVerifier", internalError: .invalidKeyID) + /// Error when a PEM string is provided without the expected PEM headers/footers. (e.g. -----BEGIN PRIVATE KEY-----) + public static let missingPEMHeaders = JWTError(localizedDescription: "The provided key did not have the expected PEM headers/footers", internalError: .missingPEMHeaders) + /// Function to check if JWTErrors are equal. Required for equatable protocol. public static func == (lhs: JWTError, rhs: JWTError) -> Bool { return lhs.internalError == rhs.internalError diff --git a/Tests/SwiftJWTTests/TestJWT.swift b/Tests/SwiftJWTTests/TestJWT.swift index 1b33695..ea7b8cd 100644 --- a/Tests/SwiftJWTTests/TestJWT.swift +++ b/Tests/SwiftJWTTests/TestJWT.swift @@ -21,6 +21,8 @@ import Foundation let rsaPrivateKey = read(fileName: "rsa_private_key") let rsaPublicKey = read(fileName: "rsa_public_key") +let rsaDERPrivateKey = read(fileName: "privateRSA.der") +let rsaDERPublicKey = read(fileName: "publicRSA.der") let ecdsaPrivateKey = read(fileName: "ecdsa_private_key") let ecdsaPublicKey = read(fileName: "ecdsa_public_key") let ec384PrivateKey = read(fileName: "ec384_private_key") @@ -158,6 +160,14 @@ class TestJWT: XCTestCase { } } + func testSignAndVerifyRSADERKey() { + do { + try signAndVerify(signer: .rs256(privateKey: rsaDERPrivateKey), verifier: .rs256(publicKey: rsaDERPublicKey)) + } catch { + XCTFail("testSignAndVerify failed: \(error)") + } + } + func testSignAndVerifyRSAPSS() { if #available(OSX 10.13, *) { do { diff --git a/Tests/SwiftJWTTests/privateRSA.der b/Tests/SwiftJWTTests/privateRSA.der new file mode 100644 index 0000000000000000000000000000000000000000..15f577cf9ed37228a444eda8bfd43861c6589159 GIT binary patch literal 1194 zcmV;b1XcSmf&``l0RRGm0RaH5jXgUFDD$vbsRDsv
@)DA%Dw
z-sgAGTP|s{IuvJY>59}X@KBtZz>?y}t(!$uG!HbZ8PQ0(?`>S