Skip to content

Commit

Permalink
feat: Ignore newlines in key generation and allow DER formatting (#68)
Browse files Browse the repository at this point in the history
* feat: Ignore newlines in key generation and allow DER formatting

* Add Jazzy docs for missingPEMHeaders
  • Loading branch information
Andrew-Lees11 authored and ianpartridge committed Jul 16, 2019
1 parent 404308b commit 88dbad6
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 7 deletions.
36 changes: 30 additions & 6 deletions Sources/SwiftJWT/BlueRSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/SwiftJWT/JWTError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
10 changes: 10 additions & 0 deletions Tests/SwiftJWTTests/TestJWT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down
Binary file added Tests/SwiftJWTTests/privateRSA.der
Binary file not shown.
Binary file added Tests/SwiftJWTTests/publicRSA.der
Binary file not shown.

0 comments on commit 88dbad6

Please sign in to comment.