CoderPatros.Jss
0.2.0
dotnet add package CoderPatros.Jss --version 0.2.0
NuGet\Install-Package CoderPatros.Jss -Version 0.2.0
<PackageReference Include="CoderPatros.Jss" Version="0.2.0" />
<PackageVersion Include="CoderPatros.Jss" Version="0.2.0" />
<PackageReference Include="CoderPatros.Jss" />
paket add CoderPatros.Jss --version 0.2.0
#r "nuget: CoderPatros.Jss, 0.2.0"
#:package CoderPatros.Jss@0.2.0
#addin nuget:?package=CoderPatros.Jss&version=0.2.0
#tool nuget:?package=CoderPatros.Jss&version=0.2.0
CoderPatros.Jss Library
A .NET library for signing and verifying JSON documents using JSON Signature Scheme (JSS) as defined in ITU-T X.590 (10/2023), with JSON Canonicalization Scheme (JCS/RFC 8785).
Installation
Requires .NET 8.0 or later.
Quick start
using CoderPatros.Jss;
using CoderPatros.Jss.Keys;
using CoderPatros.Jss.Models;
using System.Text.Json.Nodes;
var service = new JssSignatureService();
// Generate a key pair
var (signingKey, verificationKey, publicKeyPemBody) = PemKeyHelper.GenerateKeyPair("ES256");
// Sign a document
var document = new JsonObject { ["message"] = "hello" };
var signed = service.Sign(document, new SignatureOptions
{
Algorithm = "ES256",
HashAlgorithm = "sha-256",
Key = signingKey,
PublicKey = publicKeyPemBody
});
// Verify
var result = service.Verify(signed, new VerificationOptions
{
AllowEmbeddedPublicKey = true
});
Console.WriteLine(result.IsValid); // True
signingKey.Dispose();
verificationKey.Dispose();
API reference
JssSignatureService
The main entry point for all operations. Optionally accepts custom SignatureAlgorithmRegistry and HashAlgorithmRegistry instances.
var service = new JssSignatureService();
// or with custom registries:
var service = new JssSignatureService(signatureRegistry, hashRegistry);
Signing methods
| Method | Description |
|---|---|
Sign(JsonObject, SignatureOptions) |
Sign a document, returns a new JsonObject with the signature appended to the signatures array |
Sign(string, SignatureOptions) |
Sign a JSON string, returns the signed JSON string |
Countersign(JsonObject, CountersignOptions) |
Add a countersignature to an existing signature |
All signing methods are non-mutating and return new documents.
Verification methods
| Method | Description |
|---|---|
Verify(JsonObject, VerificationOptions, int?) |
Verify a specific signature (default: last one) |
Verify(string, VerificationOptions, int?) |
Verify a specific signature from a JSON string |
VerifyAll(JsonObject, VerificationOptions) |
Verify all signatures in the document |
VerifyCountersignature(JsonObject, VerificationOptions, int) |
Verify the countersignature on a specific signature |
All verification methods return a VerificationResult with IsValid and an optional Error message.
SignatureOptions
Configuration for signing operations.
new SignatureOptions
{
Algorithm = "ES256", // Required: signature algorithm identifier
HashAlgorithm = "sha-256", // Required: hash algorithm identifier
Key = signingKey, // Required: signing key
PublicKey = publicKeyPemBody, // Optional: PEM body of public key to embed
PublicCertChain = new[] { "..." }, // Optional: X.509 certificate chain
CertUrl = "https://...", // Optional: URL to retrieve certificate
Thumbprint = "...", // Optional: certificate thumbprint
Metadata = new Dictionary<string, JsonNode?> // Optional: additional metadata properties
{
["custom"] = "value"
}
}
CountersignOptions
Configuration for countersigning operations.
new CountersignOptions
{
Algorithm = "ES256", // Required: signature algorithm identifier
HashAlgorithm = "sha-256", // Required: hash algorithm identifier
Key = signingKey, // Required: signing key
SignatureIndex = 0, // Index of signature to countersign (default: 0)
PublicKey = publicKeyPemBody, // Optional: PEM body of public key to embed
PublicCertChain = new[] { "..." }, // Optional: X.509 certificate chain
CertUrl = "https://...", // Optional: URL to retrieve certificate
Thumbprint = "...", // Optional: certificate thumbprint
}
VerificationOptions
Configuration for verification operations.
new VerificationOptions
{
Key = verificationKey, // Optional: explicit verification key
KeyResolver = sig => ResolveKey(sig), // Optional: resolve key per signature
AllowEmbeddedPublicKey = false, // Optional: allow using embedded public key (default: false)
AcceptedAlgorithms = new HashSet<string> // Optional: whitelist of accepted algorithms
{
"ES256", "ES384"
}
}
When AllowEmbeddedPublicKey is true, signatures containing an embedded public key can be verified without providing an explicit key. Only enable this when you trust the source of the document, as an attacker can embed any public key in a signature they create.
VerificationResult
var result = service.Verify(signed, options);
if (result.IsValid)
{
// Signature is valid
}
else
{
Console.WriteLine(result.Error); // Description of what failed
}
Key management
Generating key pairs
// Generate a key pair for any supported algorithm
var (signingKey, verificationKey, publicKeyPemBody) = PemKeyHelper.GenerateKeyPair("ES256");
Creating keys from .NET types
// ECDSA
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
var signingKey = SigningKey.FromECDsa(ecdsa);
var verificationKey = VerificationKey.FromECDsa(ecdsa);
// RSA
using var rsa = RSA.Create(2048);
var rsaSigningKey = SigningKey.FromRsa(rsa);
var rsaVerificationKey = VerificationKey.FromRsa(rsa);
// EdDSA (raw key bytes)
var edSigningKey = SigningKey.FromEdDsa(privateKeyBytes, "Ed25519");
var edVerificationKey = VerificationKey.FromEdDsa(publicKeyBytes, "Ed25519");
Both SigningKey and VerificationKey implement IDisposable. Always use using statements or explicitly dispose keys when finished.
Embedded public keys
JSS uses PEM body format (base64 SubjectPublicKeyInfo without header/footer lines) for embedded public keys.
// Export a public key PEM body from a .NET key
var pemBody = PemKeyHelper.ExportPublicKeyPemBody(ecdsa);
// Embed in signature
var signed = service.Sign(document, new SignatureOptions
{
Algorithm = "ES256",
HashAlgorithm = "sha-256",
Key = signingKey,
PublicKey = pemBody
});
// Verify using the embedded key
var result = service.Verify(signed, new VerificationOptions
{
AllowEmbeddedPublicKey = true
});
PEM import/export
// Import keys from PEM files
var signingKey = PemKeyHelper.ImportPrivateKeyPem(pemString, "ES256");
var verificationKey = PemKeyHelper.ImportPublicKeyPem(pemString, "ES256");
// Export keys to PEM format
var privatePem = PemKeyHelper.ExportPrivateKeyPem(signingKey, "ES256");
var publicPem = PemKeyHelper.ExportPublicKeyPem(publicKeyPemBody);
Supported algorithms
| Family | Algorithms | Key type |
|---|---|---|
| ECDSA | ES256, ES384, ES512 | ECDsa (P-256, P-384, P-521) |
| RSA PKCS#1 v1.5 | RS256, RS384, RS512 | RSA |
| RSA-PSS | PS256, PS384, PS512 | RSA |
| EdDSA | Ed25519, Ed448 | Raw bytes (via BouncyCastle) |
Hash algorithms: sha-256, sha-384, sha-512
Multiple signatures
Multiple parties can independently sign a document. Each signature is appended to the signatures array.
var doc = new JsonObject { ["message"] = "hello" };
// First signer
var withSigner1 = service.Sign(doc, new SignatureOptions
{
Algorithm = "ES256",
HashAlgorithm = "sha-256",
Key = signer1Key,
PublicKey = signer1PublicKeyPemBody
});
// Second signer adds their signature
var withBoth = service.Sign(withSigner1, new SignatureOptions
{
Algorithm = "RS256",
HashAlgorithm = "sha-256",
Key = signer2Key,
PublicKey = signer2PublicKeyPemBody
});
// Verify all signatures
var result = service.VerifyAll(withBoth, new VerificationOptions
{
AllowEmbeddedPublicKey = true
});
Countersignatures
A countersignature is a signature on an existing signature, providing a way to endorse or timestamp a signature.
// Countersign the first signature (index 0)
var countersigned = service.Countersign(signed, new CountersignOptions
{
Algorithm = "ES256",
HashAlgorithm = "sha-256",
Key = countersignerKey,
SignatureIndex = 0,
PublicKey = countersignerPublicKeyPemBody
});
// Verify the countersignature
var result = service.VerifyCountersignature(countersigned, new VerificationOptions
{
AllowEmbeddedPublicKey = true
});
Algorithm whitelisting
Restrict which algorithms are accepted during verification to prevent algorithm confusion attacks.
var result = service.Verify(signed, new VerificationOptions
{
Key = verificationKey,
AcceptedAlgorithms = new HashSet<string> { "ES256", "ES384" }
});
How JSS signing works
- A copy of the document is made and any existing
signaturesarray is removed - A signature object is created with
algorithm,hash_algorithm, and optionallypublic_key— but novalueyet - The signature object is placed as the sole entry in a
signaturesarray on the document - The entire document is canonicalized using JCS (RFC 8785)
- The canonical bytes are hashed using the specified
hash_algorithm - The hash is signed with the private key and the base64url-encoded result is set as
value - The original signatures are restored at the start of the array, with the new signature appended at the end
Verification reverses the process: remove value, rebuild the document with just that signature, canonicalize, hash, and verify.
Exceptions
| Exception | Description |
|---|---|
JssException |
Base exception for all JSS operations |
License
Apache-2.0
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- BouncyCastle.Cryptography (>= 2.6.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.