Recommendations JWS and x5c / X.509 certificate chains?

Are there any well known packages which provide support for verifying X.509 certificate chains? Specifically I’m looking to verify a JWS which has an x5c header. Any packages which support JWS decoding would also be very much appreciated!

I'm looking to build a Vapor-based server which runs on a Linux machine (first time with Vapor!) so the packages will need to support that.

For context: My initial use-case is for Apple's StoreKit 2 (Apple Developer Documentation) transactions + notifications.

I've spiked this in JWTKit. The code isn't pretty but it works. You can have a look at the PR at Add JWS Support by 0xTim · Pull Request #75 · vapor/jwt-kit · GitHub

If you add a dependency on JWTKit to use that branch, you can then use it something like:

struct Payload: JWTPayload {
    func verify(using signer: JWTSigner) throws {}
    let quantity: Int
    let productId: String
}
let receipt: Payload
do {
    receipt = try req.application.jwt.signers.verifyJWSWithX5C(receiptData.receiptData, as: Payload.self, rootCert: appleRootCert)
} catch {
    // If we failed, try the test store cert
    do {
        receipt = try req.application.jwt.signers.verifyJWSWithX5C(receiptData.receiptData, as: Payload.self, rootCert: testStoreCert)
    } catch {
        throw Abort(.badRequest, reason: "Token was invalid")
    }
}
2 Likes

@0xTim This is beyond awesome. Thank you so much!

I had to put this off due to the lack of a good library for this stuff. Now that this exists, I'll be able to put it on the docket for the next sprint in about 2 weeks. I'll let you know how it goes.

For those interested in a solution using the jwt-kit version 4.9.0 which merged in the discussed changes after some modifications, I came up with the following for validating App Store notification payloads:

let appleRootCertString = """
-----BEGIN CERTIFICATE-----
MIICQzCCAcmgAw...
6BgD56KyKA==
-----END CERTIFICATE-----
"""
let notification = try req.content.decode(SKSignedPayload.self)
let x5cVerifier = try X5CVerifier(rootCertificates: [appleRootCertString])
let payload = try x5cVerifier.verifyJWS(notification.signedPayload, as: SKNotificationPayload.self)

Thank you @0xTim and @willft for putting in the work to get this PR merged.

1 Like