Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/IBM-Swift/BlueRSA.git", from: "1.0.24"),
.package(url: "https://github.com/IBM-Swift/BlueRSA.git", from: "1.0.31"),
.package(url: "https://github.com/IBM-Swift/BlueCryptor.git", from: "1.0.0"),
.package(url: "https://github.com/IBM-Swift/BlueECC.git", from: "1.1.0"),
.package(url: "https://github.com/IBM-Swift/LoggerAPI.git", from: "1.7.0"),
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,11 @@ The supported algorithms for signing and verifying JWTs are:
* ES256 - ECDSA using using SHA-256 and a P-256 curve
* ES384 - ECDSA using using SHA-384 and a P-384 curve
* ES512 - ECDSA using using SHA-512 and a P-521 curve
* PS256 - RSA-PSS using SHA-256
* PS384 - RSA-PSS using SHA-384
* none - Don't sign or verify the JWT

Note: ECDSA algorithms require a minimum Swift version of 4.1.
Note: ECDSA and RSA-PSS algorithms require a minimum Swift version of 4.1.

### Validate claims

Expand Down
8 changes: 5 additions & 3 deletions Sources/SwiftJWT/BlueRSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ class BlueRSA: SignerAlgorithm, VerifierAlgorithm {
private let key: Data
private let keyType: RSAKeyType
private let algorithm: Data.Algorithm
private let usePSS: Bool

init(key: Data, keyType: RSAKeyType?=nil, algorithm: Data.Algorithm) {
init(key: Data, keyType: RSAKeyType?=nil, algorithm: Data.Algorithm, usePSS: Bool = false) {
self.key = key
self.keyType = keyType ?? .publicKey
self.algorithm = algorithm
self.usePSS = usePSS
}

func sign(header: String, claims: String) throws -> String {
Expand All @@ -52,7 +54,7 @@ class BlueRSA: SignerAlgorithm, VerifierAlgorithm {
}
let privateKey = try CryptorRSA.createPrivateKey(withPEM: keyString)
let myPlaintext = CryptorRSA.createPlaintext(with: data)
guard let signedData = try myPlaintext.signed(with: privateKey, algorithm: algorithm) else {
guard let signedData = try myPlaintext.signed(with: privateKey, algorithm: algorithm, usePSS: usePSS) else {
throw JWTError.invalidPrivateKey
}
return signedData.data
Expand Down Expand Up @@ -92,7 +94,7 @@ class BlueRSA: SignerAlgorithm, VerifierAlgorithm {
}
let myPlaintext = CryptorRSA.createPlaintext(with: data)
let signedData = CryptorRSA.createSigned(with: signature)
return try myPlaintext.verify(with: publicKey, signature: signedData, algorithm: algorithm)
return try myPlaintext.verify(with: publicKey, signature: signedData, algorithm: algorithm, usePSS: usePSS)
}
catch {
Log.error("Verification failed: \(error)")
Expand Down
12 changes: 12 additions & 0 deletions Sources/SwiftJWT/JWTSigner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ public struct JWTSigner {
return JWTSigner(name: "RS512", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha512))
}

/// Initialize a JWTSigner using the RSA-PSS 256 bits algorithm and the provided privateKey.
/// - Parameter privateKey: The UTF8 encoded PEM private key, with a "BEGIN RSA PRIVATE KEY" header.
public static func ps256(privateKey: Data) -> JWTSigner {
return JWTSigner(name: "PS256", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha256, usePSS: true))
}

/// Initialize a JWTSigner using the RSA-PSS 384 bits algorithm and the provided privateKey.
/// - Parameter privateKey: The UTF8 encoded PEM private key, with a "BEGIN RSA PRIVATE KEY" header.
public static func ps384(privateKey: Data) -> JWTSigner {
return JWTSigner(name: "PS384", signerAlgorithm: BlueRSA(key: privateKey, keyType: .privateKey, algorithm: .sha384, usePSS: true))
}

/// Initialize a JWTSigner using the HMAC 256 bits algorithm and the provided privateKey.
/// - Parameter key: The HMAC symmetric password data.
public static func hs256(key: Data) -> JWTSigner {
Expand Down
12 changes: 12 additions & 0 deletions Sources/SwiftJWT/JWTVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ public struct JWTVerifier {
return JWTVerifier(verifierAlgorithm: BlueRSA(key: certificate, keyType: .certificate, algorithm: .sha512))
}

/// Initialize a JWTVerifier using the RSA-PSS 256 bits algorithm and the provided publicKey.
/// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header.
public static func ps256(publicKey: Data) -> JWTVerifier {
return JWTVerifier(verifierAlgorithm: BlueRSA(key: publicKey, keyType: .publicKey, algorithm: .sha256, usePSS: true))
}

/// Initialize a JWTVerifier using the RSA-PSS 384 bits algorithm and the provided publicKey.
/// - Parameter publicKey: The UTF8 encoded PEM public key, with a "BEGIN PUBLIC KEY" header.
public static func ps384(publicKey: Data) -> JWTVerifier {
return JWTVerifier(verifierAlgorithm: BlueRSA(key: publicKey, keyType: .publicKey, algorithm: .sha384, usePSS: true))
}

/// Initialize a JWTSigner using the HMAC 256 bits algorithm and the provided privateKey.
/// - Parameter key: The HMAC symmetric password data.
public static func hs256(key: Data) -> JWTVerifier {
Expand Down
40 changes: 40 additions & 0 deletions Tests/SwiftJWTTests/TestJWT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ let rsaJWTDecoder = JWTDecoder(jwtVerifier: .rs256(publicKey: rsaPublicKey))
let certPrivateKey = read(fileName: "cert_private_key")
let certificate = read(fileName: "certificate")
let rsaEncodedTestClaimJWT = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZSwic3ViIjoiMTIzNDU2Nzg5MCIsImlhdCI6MTUxNjIzOTAyMn0.HbPVSMBtR3l0zyrHIlGRyXkNECgE0RrQreebA2xuIWhN-64MP29-lf8lg5pWKk3gTrnbOxEpek5AvBNgz4VK34enkzhrrMKonBywvZZ8CQtM5FlArgx5ZQqxjD32B7WCqlDOelly1W2rlFNIopBit-OuKBw1ioxQwzDMLb1Ol3Q"
let rsaPSSEncodedTestClaimJWT = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.DOfeHpVUsym3n0xuF2EWqYh9y8kf1DGYxWV7w-R7LTsn_ukD46WpJZQB-juwHOjeJajV_5lZHv-9MgSZvAhL8uCVBYfAwmOEfT9ytxgMegkDedjZDr4anRTMDbt0myw-teToC77sHuUyChF7D4tscgRB3LF1qr-_YZDGv9DP-Qc"
let certificateEncodedTestClaimJWT = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZSwic3ViIjoiMTIzNDU2Nzg5MCJ9.CpnzQLuWGfH5Kba36vg0ZZKBnzwlrIgapFVfBfk_nea-eej84ktHZANqIeolskZopRJ4DQ3oaLtHWEg16-ZsujxmkOdiAIbk0-C4QLOVFLZH78WLZAqkyNLS8rFuK9hloLNwz1j6VVUd1f0SOT-wIRzL0_0VRYqQd1bVcCj7wc7BmXENlOfHY7KGHS-6JX-EClT1DygDSoCmdvBExBf3vx0lwMIbP4ryKkyhOoU13ZfSUt1gpP9nZAfzqfRTPxZc_f7neiAlMlF6SzsedsskRCNegW8cg5e_NuVmZZkj0_bnswXFDMmIaxiPdtOEWkmyEOca-EHSwbO5PgCgXOIrgg"
// A `TestClaims` encoded using HMAC with "Super Secret Key" from "www.jwt.io"
let hmacEncodedTestClaimJWT = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZSwic3ViIjoiMTIzNDU2Nzg5MCJ9.8kIE0ZCq1Vw7aW1kACpgJLcgY2DpTXgO6P5T3cdCuTs"
Expand Down Expand Up @@ -110,6 +111,7 @@ class TestJWT: XCTestCase {
return [
("testSignAndVerify", testSignAndVerify),
("testSignAndVerifyRSA", testSignAndVerifyRSA),
("testSignAndVerifyRSAPSS", testSignAndVerifyRSAPSS),
("testSignAndVerifyCert", testSignAndVerifyCert),
("testSignAndVerifyHMAC", testSignAndVerifyHMAC),
("testSignAndVerifyECDSA", testSignAndVerifyECDSA),
Expand Down Expand Up @@ -153,6 +155,14 @@ class TestJWT: XCTestCase {
}
}

func testSignAndVerifyRSAPSS() {
do {
try signAndVerify(signer: .ps256(privateKey: rsaPrivateKey), verifier: .ps256(publicKey: rsaPublicKey))
} catch {
XCTFail("testSignAndVerify failed: \(error)")
}
}

func testSignAndVerifyCert() {
do {
try signAndVerify(signer: .rs256(privateKey: certPrivateKey), verifier: .rs256(certificate: certificate))
Expand Down Expand Up @@ -188,6 +198,14 @@ class TestJWT: XCTestCase {
}
}

func testSignAndVerifyRSAPSS384() {
do {
try signAndVerify(signer: .ps384(privateKey: rsaPrivateKey), verifier: .ps384(publicKey: rsaPublicKey))
} catch {
XCTFail("testSignAndVerify failed: \(error)")
}
}

func testSignAndVerifyCert384() {
do {
try signAndVerify(signer: .rs384(privateKey: certPrivateKey), verifier: .rs384(certificate: certificate))
Expand Down Expand Up @@ -471,6 +489,28 @@ class TestJWT: XCTestCase {
}
}

// From jwt.io
func testJWTRSAPSS() {
let ok = JWT<TestClaims>.verify(rsaPSSEncodedTestClaimJWT, using: .ps256(publicKey: rsaPublicKey))
XCTAssertTrue(ok, "Verification failed")

if let decoded = try? JWT<TestClaims>(jwtString: rsaPSSEncodedTestClaimJWT) {
XCTAssertEqual(decoded.header.alg, "PS256", "Wrong .alg in decoded")
XCTAssertEqual(decoded.header.typ, "JWT", "Wrong .typ in decoded")

XCTAssertEqual(decoded.claims.sub, "1234567890", "Wrong .sub in decoded")
XCTAssertEqual(decoded.claims.name, "John Doe", "Wrong .name in decoded")
XCTAssertEqual(decoded.claims.admin, true, "Wrong .admin in decoded")
XCTAssertEqual(decoded.claims.iat, Date(timeIntervalSince1970: 1516239022), "Wrong .iat in decoded")


XCTAssertEqual(decoded.validateClaims(), .success, "Validation failed")
}
else {
XCTFail("Failed to decode")
}
}

func testJWTUsingHMAC() {
guard let hmacData = "Super Secret Key".data(using: .utf8) else {
return XCTFail("Failed to convert hmacKey to Data")
Expand Down