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.
- Available Features and Algorithms
- Requirements
- Swift Package Manager (SPM)
- Documentation
- Modules
- Contributing
- References
- Acknowledgments
- License
JWT supported algorithms | JWT supported types | JWT supported claims validations | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
JWE Supported Types | JWE Supported Algorithms | JWE Supported Encodings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
JWS Supported Types | JWS Supported Algorithms | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
Note: JWS Unencoded payload as referenced in the RFC-7797
JWK Supported Key Types | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
- Swift 5.7.1 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:
To use the jose-swift
package in your project, you need to add it as a dependency in your Package.swift
file.
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 ...
]
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 ...
]
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
You can access here to the documentation.
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.
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 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
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 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 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.
-
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
-
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)
- C20PKW (ChaCha20-Poly1305)
- XC20PKW (XChaCha20-Poly1305)
- Note: ChaChaPoly20-Poly1305 and XChaChaPoly20-Poly1305 is specified in draft-amringer-jose-chacha-02
-
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 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.
-
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.
-
Encrypted JWTs:
- Facilitates encryption of token content for confidentiality.
- Uses JWE (JSON Web Encryption) for robust encryption standards.
- Supports all JWE algorithms previously mentioned.
-
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).
-
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.
-
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 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.
Contributions to the library are welcome. Please ensure that your contributions adhere to the Jose standards and add value to the existing functionalities.
- JSON Web Signature (JWS) - RFC 7515
- JSON Web Encryption (JWE) - RFC 7516
- JSON Web Key (JWK) - RFC 7517
- JSON Web Algorithms (JWA) - RFC 7518
- JSON Web Token (JWT) - RFC 7519
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.
This project is licensed under the Apache License 2.0. See the LICENSE file for details.