Skip to content

A comprehensive Swift library for JOSE standards implementation, supporting JWA, JWK, JWE, JWS and JWT with robust encryption and signing functionalities.

License

Notifications You must be signed in to change notification settings

beatt83/jose-swift

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

93 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Screenshot

Jose Swift Library

Swift Swift6 iOS MacOS WatchOS TvOS

This library provides comprehensive support for the Jose suite of standards, including JWA (JSON Web Algorithms), JWK (JSON Web Key), JWE (JSON Web Encryption), JWS (JSON Web Signature), and JWT (JSON Web Token). These standards are integral to modern security protocols on the web, offering methods for secure key management, data encryption, signing, and representation of claims among different parties.

Table of Contents

  1. Available Features and Algorithms
  2. Requirements
  3. Swift Package Manager (SPM)
  4. Documentation
  5. Modules
  6. Contributing
  7. References
  8. Acknowledgments
  9. License

Available Features and Algorithms

JWT

JWT supported algorithmsJWT supported typesJWT supported claims validations
Algorithms Supported
All JWE algorithms âś…
All JWS algorithms âś…
Types Supported
Signed âś…
Encrypted âś…
Nested Signed âś…
Nested Encrypted âś…
Claims Supported
iss âś…
sub âś…
aud âś…
nbf âś…
exp âś…
iat âś…
typ âś…
cty âś…
DSL Claims Builder âś…

JWE

JWE Supported TypesJWE Supported AlgorithmsJWE Supported Encodings
Type Supported
Compact String âś…
JSON âś…
JSON Flattened âś…
Algorithm Supported
RSA1_5 âś…
RSA-OAEP âś…
RSA-OAEP-256 âś…
A128KW âś…
A192KW âś…
A256KW âś…
DIRECT âś…
ECDH-ES âś…
ECDH-ES+A128KW âś…
ECDH-ES+A192KW âś…
ECDH-ES+A256KW âś…
ECDH-1PU âś…
ECDH-1PU+A128KW âś…
ECDH-1PU+A192KW âś…
ECDH-1PU+A256KW âś…
A128GCMKW âś…
A192GCMKW âś…
A256GCMKW âś…
PBES2-HS256+A128KW âś…
PBES2-HS384+A192KW âś…
PBES2-HS512+A256KW âś…
Encoding Algorithm Supported
A128CBC-HS256 âś…
A128CBC-HS384 âś…
A128CBC-HS512 âś…
A128GCMKW âś…
A192GCMKW âś…
A256GCMKW âś…
C20P âś…
XC20P âś…

JWS

JWS Supported TypesJWS Supported Algorithms
Type Supported
Compact String âś…
JSON âś…
JSON Flattened âś…
Unencoded Payload* âś…
Algorithm Supported
HS256 âś…
HS384 âś…
HS512 âś…
RS256 âś…
RS384 âś…
RS512 âś…
ES256 âś…
ES256K âś…
ES384 âś…
ES512 âś…
PS256 âś…
PS384 âś…
PS512 âś…
EdDSA âś…

Note: JWS Unencoded payload as referenced in the RFC-7797

JWK

JWK Supported Key Types
Key Type Supported
EC âś…
RSA âś…
OKT âś…
OCK âś…

Requirements

  • Swift 5.8.1 or later
  • Swift 6 or later
  • iOS 15.0 or later
  • macOS 12.0 or later
  • Mac Catalyst 15.0 or later
  • tvOS 15.0 or later
  • watchOS 8.0 or later
  • Dependencies:

Swift Package Manager (SPM)

To use the jose-swift package in your project, you need to add it as a dependency in your Package.swift file.

Step 1: Add the Dependency

Open your Package.swift file and add the jose-swift package to your dependencies array. Make sure to specify the version you want to use:

dependencies: [
    .package(url: "https://github.com/beatt83/jose-swift.git", .upToNextMinor(from: "2.4.0")),
    // ... other dependencies ...
]

Step 2: Add the Target Dependency

In the same Package.swift file, add jose-swift to the dependencies of your target:

targets: [
    .target(
        name: "YourTargetName",
        dependencies: [
            "jose-swift",
            // ... other dependencies ...
        ]
    ),
    // ... other targets ...
]

Step 3: Import and Use in Your Project

Once you've added the package as a dependency, you can import JSONWebEncryption, JWS, JWA, or JWK in your Swift files depending on what functionality you need:

import JSONWebEncryption
// or
import JSONWebSignature
// or
import JSONWebAlgorithms
// or
import JSONWebKey
// or
import JSONWebToken

Documentation

You can access here to the documentation.

Getting Started

For a quick guide on how to use the library please visit the Getting Started tutorial.

For more examples on how to use this library please try to check the unit tests, they are extensive and should provide more information.

Modules

JWK (JSON Web Key)

JWK is a standard way to represent cryptographic keys in a JSON format, as defined in RFC 7517. This module provides functionalities for generating, parsing, and managing JWKs, which are essential for encryption, decryption, and signing processes.

Please check our documentation for more on JWS Signatures.

let keyJWK = JWK(keyType: .rsa, algorithm: "A256GCM", keyID: rsaKeyId, e: rsaKeyExponent, n: rsaKeyModulus)
// ---------------------
let key = secp256k1.Signing.PrivateKey()
let keyJWK = key.jwkRepresentation
// ---------------------
let key = Curve25519.KeyAgreement.PrivateKey()
let publicKeyJWK = key.jwkRepresentation.publicKey

JWS (JSON Web Signature)

JWS is a standard for digitally signing arbitrary content, as detailed in RFC 7515. This module supports creating and verifying digital signatures, ensuring the integrity and authenticity of signed data.

#### Supported Algorithms:

  • RS256 (RSA Signature with SHA-256)
  • RS384 (RSA Signature with SHA-384)
  • RS512 (RSA Signature with SHA-512)
  • HS256 (HMAC with SHA-256)
  • HS384 (HMAC with SHA-384)
  • HS512 (HMAC with SHA-512)
  • ES256 (ECDSA using P-256 and SHA-256)
  • ES384 (ECDSA using P-384 and SHA-384)
  • ES512 (ECDSA using P-521 and SHA-512)
  • ES256K (ECDSA using secp256k1 and SHA-256)
  • PS256 (RSA PSS with SHA-256)
  • PS384 (RSA PSS with SHA-384)
  • PS512 (RSA PSS with SHA-512)
  • EdDSA (EdDSA using Ed25519) - RFC 8037

Bouncy castle secp256k1 failsafe

There is a difference between the signatures given by Bouncy castle a prominent cryptographic Java library and used with Nimbus JWT and bitcoin secp256k1. The signatures are in DER format and for some reason the R and S are reverted.

To have signatures that are verifiable by Bouncy Castle you can set this flag ES256KSigner.outputFormat = .der, it will transform the signatures in DER format.

With this in mind this library provides a functionality to enable verification of Nimbus/Bouncy Castle signatures, this can be enabled by setting the flag ES256KVerifier.bouncyCastleFailSafe = true. This process requires manipualtion of the internal signature, and reverses the R and S bytes, use it at your own risk since it can add security flaw.

Example:

let payload = "Hello world".data(using: .utf8)!
let key = secp256k1.Signing.PrivateKey()

let jws = try JWS(payload: payload, key: key)

let jwsString = jws.compactSerialization

try JWS(jwsString: jwsString).verify(key: key)

If you want to add additional headers beyond the default to the JWS:

let rsaKeyId = "Hello-keyId"
var header = DefaultJWSHeaderImpl()
header.keyID = rsaKeyId
header.algorithm = .rsa512

let keyJWK = JWK(keyType: .rsa, algorithm: "RSA512", keyID: rsaKeyId, e: rsaKeyExponent, n: rsaKeyModulus)
let jwe = try JWS(payload: payload, protectedHeader: header, key: jwk)

JWS with Unencoded payload (Compact string only)

JWS also supports unencoded payloads, which is useful in scenarios where the payload is already in a compact, URL-safe form (such as in the case of small JSON objects or base64url-encoded strings). This can help reduce the overall size of the JWS and improve performance by avoiding redundant encoding steps.

To create a JWS with an unencoded payload, you need to set the b64 header parameter to false and ensure the payload is in a compatible format.

Example:

let payload = "Hello world".data(using: .utf8)!
let key = secp256k1.Signing.PrivateKey()

let jws = try JWS(payload: payload, key: key, options: [.unencodedPayload])

let jwsString = jws.compactSerialization

try JWS.verify(jwsString: jwsString, payload: payload.data(using: .utf8)!, key: key)

JWE (JSON Web Encryption)

JWE represents encrypted content using JSON-based data structures, following the guidelines of RFC 7516. This module includes functionalities for encrypting and decrypting data, managing encryption keys, and handling various encryption algorithms and methods.

Please check our documentation for more on JWE Encryption.

Supported Algorithms:

  1. Key Management Algorithms:

    • RSA1_5 (RSAES-PKCS1-v1_5)
    • RSA-OAEP (RSAES OAEP using default parameters)
    • RSA-OAEP-256 (RSAES OAEP using SHA-256 and MGF1 with SHA-256)
    • A128KW (AES Key Wrap with default 128-bit key)
    • A192KW (AES Key Wrap with 192-bit key)
    • A256KW (AES Key Wrap with 256-bit key)
    • dir (Direct use of a shared symmetric key)
    • ECDH-ES (Elliptic Curve Diffie-Hellman Ephemeral Static key agreement)
    • ECDH-ES+A128KW (ECDH-ES using Concat KDF and A128KW wrapping)
    • ECDH-ES+A192KW (ECDH-ES using Concat KDF and A192KW wrapping)
    • ECDH-ES+A256KW (ECDH-ES using Concat KDF and A256KW wrapping)
    • ECDH-1PU (Elliptic Curve Diffie-Hellman One-Pass Unified Model)
    • ECDH-1PU+A128KW (ECDH-1PU using Concat KDF and A128KW wrapping)
    • ECDH-1PU+A192KW (ECDH-1PU using Concat KDF and A192KW wrapping)
    • ECDH-1PU+A256KW (ECDH-1PU using Concat KDF and A256KW wrapping)
    • A128GCMKW (Key wrapping with AES GCM using 128-bit key)
    • A192GCMKW (Key wrapping with AES GCM using 192-bit key)
    • A256GCMKW (Key wrapping with AES GCM using 256-bit key)
    • PBES2-HS256+A128KW (PBES2 with HMAC SHA-256 and "A128KW" wrapping)
    • PBES2-HS384+A192KW (PBES2 with HMAC SHA-384 and "A192KW" wrapping)
    • PBES2-HS512+A256KW (PBES2 with HMAC SHA-512 and "A256KW" wrapping)
    • Note: ECDH-1PU is specified in draft-ietf-jose-cfrg-curves-10
  2. Content Encryption Algorithms:

    • A128CBC-HS256 (AES CBC using 128-bit key with HMAC SHA-256)
    • A192CBC-HS384 (AES CBC using 192-bit key with HMAC SHA-384)
    • A256CBC-HS512 (AES CBC using 256-bit key with HMAC SHA-512)
    • A128GCM (AES GCM using 128-bit key)
    • A192GCM (AES GCM using 192-bit key)
    • A256GCM (AES GCM using 256-bit key)
    • C20P (ChaCha20-Poly1305)
    • XC20P (XChaCha20-Poly1305)
    • Note: ChaChaPoly20-Poly1305 and XChaChaPoly20-Poly1305 is specified in draft-amringer-jose-chacha-02
  3. Compression Algorithms:

    • DEFLATE (zip)

Example1:

let payload = "Hello world".data(using: .utf8)!
let keyJWK = JWK(keyType: .rsa, algorithm: "A256GCM", keyID: rsaKeyId, e: rsaKeyExponent, n: rsaKeyModulus)


let serialization = try JWE(
    payload: payload,
    keyManagementAlg: .a256KW,
    encryptionAlgorithm: .a256GCM,
    compressionAlgorithm: .zip,
    recipientKey: keyJWK
)

let compact = serialization.compactSerialization()

let jwe = try JWE(compactString: compact)
let decrypted = try jwe.decrypt(recipientKey: recipientJWK)

Example2:

let payload = "Hello world".data(using: .utf8)!
let key = P256.Signing.PrivateKey()


let serialization = try JWE(
    payload: payload,
    keyManagementAlg: .a256KW,
    encryptionAlgorithm: .a256GCM,
    compressionAlgorithm: .zip,
    recipientKey: key
)

let compact = serialization.compactSerialization()

let jwe = try JWE(compactString: compact)
let decrypted = try jwe.decrypt(recipientKey: recipientJWK)

If you want to add additional headers beyond the default to the JWE:

let rsaKeyId = "Hello-keyId"
var header = DefaultJWEHeaderImpl()
header.keyID = rsaKeyId
header.keyManagementAlgorithm = .rsaOAEP256
header.encodingAlgorithm = .a256GCM
let keyJWK = JWK(keyType: .rsa, algorithm: "A256GCM", keyID: rsaKeyId, e: rsaKeyExponent, n: rsaKeyModulus)
let jwe = try JWE(payload: wrappedPayload, protectedHeader: header, recipientKey: jwk)

JWT (JSON Web Token)

JWT is a compact, URL-safe means of representing claims to be transferred between two parties. This module offers tools for creating, parsing, validating, and manipulating JWTs, with support for various signing and encryption methods, as specified in RFC 7519.

Please check our documentation for more on JWT tokens.

Features:

  1. Signed JWTs:

    • Supports digital signatures to verify the authenticity and integrity of the token.
    • Utilizes JWS (JSON Web Signature) standards.
    • Supports all JWS algorithms previously mentioned.
  2. Encrypted JWTs:

    • Facilitates encryption of token content for confidentiality.
    • Uses JWE (JSON Web Encryption) for robust encryption standards.
    • Supports all JWE algorithms previously mentioned.
  3. Nested JWT (JWS + JWE):

    • Implements Nested JWTs where a JWT is signed and then encrypted, providing both the benefits of JWS and JWE.
    • Ensures that a token is first authenticated (JWS) and then secured for privacy (JWE).
  4. Domain-specific language (DSL) for Claim Creation:

    • Allows for a more declarative approach to creating claims using a domain-specific language (DSL).
    • Facilitates the creation of both standard and custom claims in a readable and structured manner.
  5. Claim Validation:

    • Offers extensive capabilities to validate JWT claims.
    • Includes standard claims like issuer (iss), subject (sub), audience (aud), expiration (exp), not before (nbf), and issued at (iat).
    • Custom claim validation to meet specific security requirements.

Example:

  • Signed JWT
let key = P256.Signing.PrivateKey()
let mockClaims = DefaultJWTClaims(
    iss: "testAlice",
    sub: "Alice",
    exp: expiredAt
)

let jwt = try JWT.signed(
    payload: mockClaims,
    protectedHeader: DefaultJWSHeaderImpl(algorithm: .ES256),
    key: key
)

let jwtString = jwt.jwtString

let verifiedJWT = try JWT<DefaultJWTClaims>.verify(jwtString: jwtString, senderKey: key)
let verifiedPayload = verifiedJWT.payload
  • Encrypted JWT
let key = Curve25519.KeyAgreement.PrivateKey()
let mockClaims = DefaultJWTClaims(
    iss: "testAlice",
    sub: "Alice",
    exp: expiredAt
)

let jwt = try JWT.encrypt(
    payload: payload,
    protectedHeader: DefaultJWSHeaderImpl(keyManagementAlgorithm: .a128KW, encodingAlgorithm: .a128CBCHS256),
    recipientKey: key
)

let jwtString = jwt.jwtString

let verifiedJWT = try JWT<DefaultJWTClaims>.verify(jwtString: jwtString, recipientKey: key)
let verifiedPayload = verifiedJWT.payload
  • DSL for Creating Claims

    • Standard Claims on signing a JWT
    let key = P256.Signing.PrivateKey()
    
    let jwt = try JWT.signed(
        payload: {
            IssuerClaim(value: "testIssuer")
            SubjectClaim(value: "testSubject")
            ExpirationTimeClaim(value: Date())
            IssuedAtClaim(value: Date())
            NotBeforeClaim(value: Date())
            JWTIdentifierClaim(value: "ThisIdentifier")
            AudienceClaim(value: "testAud")
        },
        protectedHeader: DefaultJWSHeaderImpl(algorithm: .ES256),
        key: key
    ).jwtString
    • Custom Claims
    let jsonClaimsObject = JWTClaimsBuilder.build {
        StringClaim(key: "testStr1", value: "value1")
        NumberClaim(key: "testN1", value: 0)
        NumberClaim(key: "testN2", value: 1.1)
        NumberClaim(key: "testN3", value: Double(1.233232))
        BoolClaim(key: "testBool1", value: true)
        ArrayClaim(key: "testArray") {
            ArrayElementClaim.string("valueArray1")
            ArrayElementClaim.string("valueArray2")
            ArrayElementClaim.bool(true)
            ArrayElementClaim.array {
                ArrayElementClaim.string("nestedNestedArray1")
            }
            ArrayElementClaim.object {
                StringClaim(key: "nestedNestedObject", value: "nestedNestedValue")
            }
        }
        ObjectClaim(key: "testObject") {
            StringClaim(key: "testDicStr1", value: "valueDic1")
        }
    }
    
    // Output
    // {
    //    "testBool1":true,
    //    "testArray":[
    //         "valueArray1",
    //          "valueArray2",
    //          true,
    //          ["nestedNestedArray1"],
    //          {
    //              "nestedNestedObject":"nestedNestedValue"
    //          }
    //      ],
    //      "testObject":{
    //          "testDicStr1":"valueDic1"
    //      },
    //      "testN1":0,
    //      "testStr1":"value1",
    //      "testN3":1.233232,
    //      "testN2":1.1
    // }

JWA (JSON Web Algorithms)

JWA specifies cryptographic algorithms used in the context of Jose to perform digital signing and content encryption, as detailed in RFC 7518. It includes standards for various types of algorithms like RSA, AES, HMAC, and more.

Contributing

Contributions to the library are welcome. Please ensure that your contributions adhere to the Jose standards and add value to the existing functionalities.

References

Acknowledgments

Special thanks to the swift-jose repository by Zsombor Szabo for serving as an inspiration for this project. I have adopted parts of the JWK implementation and several test vectors from their work, which have been instrumental in shaping aspects of this library. Their contributions to the open-source community are sincerely appreciated.

License

This project is licensed under the Apache License 2.0. See the LICENSE file for details.