diff --git a/input/fsh/examples/signature-extension-example.fsh b/input/fsh/examples/signature-extension-example.fsh deleted file mode 100644 index 0928e21..0000000 --- a/input/fsh/examples/signature-extension-example.fsh +++ /dev/null @@ -1,40 +0,0 @@ -Instance: ExampleSignatureLibrary -InstanceOf: Library -* description = """ -This example now demonstrates how to properly attach an artifact signature to a -FHIR Library resource using the CRMI signature extension. - -The generated SHA256 checksum of the current resource (which excludes `id`, -`text`, and `meta`), in minified JSON form is: -``` -892c98e8660c3b84f88cffc4759880ea6f73afa9f58a5ee5dd2f8b7c48250dca -``` - -The signature `data` value after base64 decoding is a JWT: -``` -eyJhbGciOiJSUzM4NCJ9.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDozMDAwL29pZGMiLCJoYXNoIjoiODkyYzk4ZTg2NjBjM2I4NGY4OGNmZmM0NzU5ODgwZWE2ZjczYWZhOWY1OGE1ZWU1ZGQyZjhiN2M0ODI1MGRjYSJ9.T581_ZkQee7RnJpePnApDIgWtHCO6GUFltHF3riM0wEEAMuVK8X63OrBZpRMCFZWwJ9_RQk3Jo9q4Tyu5WxnZaFxyH0cDCs21gFuCtUanRf4jep2ZfShjVjmm90AGyAzz6EeTodpWyNL48Js__ZSmK8HahkFos5DWZdi93BZalOPvR-pAnzKgxyrrkdmLFZBjKC6drzqhfTyTY0P2yLZV0x6X3btvkdcci8_tqKDl8xz84Gut4iHr0fivP7CbzBoIO6Dlw1gScFWaE9ATRDvkTnSYu3JVptMZo4xgKhrL3ZQktrQZm1CIQ8tnMn5hCdT7W-DysejxxH9t128FYBA1Q -``` - -The decoded JWT payload contains the following fields: -- `iss`: The issuer of the signature, which is the CRMI server URL. -- `hash`: The SHA256 checksum of the resource in minified JSON form. -``` -{ - "iss": "https://localhost:3000/oidc", - "hash": "892c98e8660c3b84f88cffc4759880ea6f73afa9f58a5ee5dd2f8b7c48250dca" -} -``` - -The signature is created using the private key of the CRMI server, ensuring the -integrity and authenticity of the resource. Clients can verify JWT signature -using the public key provided by the CRMI server, and then verify the SHA256 -checksum against the resource's content to ensure it has not been altered. -""" -* status = #active -* type = http://terminology.hl7.org/CodeSystem/library-type#logic-library -* meta.extension[CRMIArtifactSignature].valueSignature - * type = http://uri.etsi.org/01903/v1.2.2#ProofOfCreation - * when = "2025-05-12T10:17:55.135Z" - * who.display = "CRMI Server" - * sigFormat = #application/jwt - * data = "ZXlKaGJHY2lPaUpTVXpNNE5DSjkuZXlKcGMzTWlPaUpvZEhSd2N6b3ZMMnh2WTJGc2FHOXpkRG96TURBd0wyOXBaR01pTENKb1lYTm9Jam9pT0RreVl6azRaVGcyTmpCak0ySTROR1k0T0dObVptTTBOelU1T0Rnd1pXRTJaamN6WVdaaE9XWTFPR0UxWldVMVpHUXlaamhpTjJNME9ESTFNR1JqWVNKOS5UNTgxX1prUWVlN1JuSnBlUG5BcERJZ1d0SENPNkdVRmx0SEYzcmlNMHdFRUFNdVZLOFg2M09yQlpwUk1DRlpXd0o5X1JRazNKbzlxNFR5dTVXeG5aYUZ4eUgwY0RDczIxZ0Z1Q3RVYW5SZjRqZXAyWmZTaGpWam1tOTBBR3lBeno2RWVUb2RwV3lOTDQ4SnNfX1pTbUs4SGFoa0ZvczVEV1pkaTkzQlphbE9QdlItcEFuektneHlycmtkbUxGWkJqS0M2ZHJ6cWhmVHlUWTBQMnlMWlYweDZYM2J0dmtkY2NpOF90cUtEbDh4ejg0R3V0NGlIcjBmaXZQN0NiekJvSU82RGx3MWdTY0ZXYUU5QVRSRHZrVG5TWXUzSlZwdE1abzR4Z0tockwzWlFrdHJRWm0xQ0lROHRuTW41aENkVDdXLUR5c2VqeHhIOXQxMjhGWUJBMVEK" diff --git a/input/fsh/extensions/artifact-signature-extension.fsh b/input/fsh/extensions/artifact-signature-extension.fsh deleted file mode 100644 index 52e537c..0000000 --- a/input/fsh/extensions/artifact-signature-extension.fsh +++ /dev/null @@ -1,6 +0,0 @@ -Extension: CRMIArtifactSignature -Id: crmi-artifact-signature -Title: "CRMI Artifact Signature Extension" -Description: "This extension is used to sign FHIR knowledge artifacts, ensuring their integrity and authenticity." -Context: Resource.meta -* value[x] only Signature \ No newline at end of file diff --git a/input/ignoreWarnings.txt b/input/ignoreWarnings.txt index 2107196..9c115cc 100644 --- a/input/ignoreWarnings.txt +++ b/input/ignoreWarnings.txt @@ -59,10 +59,6 @@ WARNING: StructureMap/publishable-example: StructureMap.group[0].input[1]: Group WARNING: StructureMap/shareable-example: StructureMap.group[0].input[0]: Group Examples parameter Source has no type, so the paths cannot be validated WARNING: StructureMap/shareable-example: StructureMap.group[0].input[1]: Group Examples parameter Destination has no type, so the paths cannot be validated -# 2.0.0-ballot:013 - This code system is used in the R5 signature, but doesn't validate in R4, these are verified correct here: https://hl7.org/fhir/valueset-signature-type.html -WARNING: Library/ExampleSignatureLibrary: Library.meta.extension[0].value.ofType(Signature).type[0].system: A definition for CodeSystem 'http://uri.etsi.org/01903/v1.2.2' could not be found, so the code cannot be validated -INFORMATION: Library/ExampleSignatureLibrary: Library.meta.extension[0].value.ofType(Signature).type[0]: The code provided (http://uri.etsi.org/01903/v1.2.2#ProofOfCreation) is not in the value set 'Signature Type Codes' (http://hl7.org/fhir/ValueSet/signature-type|4.0.1), and a code is recommended to come from this value set - # 2.0.0-ballot:014 - These errors are coming from the URL checking but are in inherited content. The links are valid, but are to published content coming from the extensions pack, either in the R5 spec, or the currently published CRMI spec ERROR: ActivityDefinition.extension: The reference http://hl7.org/fhir/ValueSet/version-algorithm could not be resolved ERROR: Group.extension: The reference http://hl7.org/fhir/ValueSet/knowledge-representation-level could not be resolved diff --git a/input/pages/artifact-signing.md b/input/pages/artifact-signing.md index 9952b77..3c6b77b 100644 --- a/input/pages/artifact-signing.md +++ b/input/pages/artifact-signing.md @@ -2,7 +2,11 @@ {: #artifact-signing} -### Use Case: Protecting Intellectual Property and License Compliance in Clinical Decision Support +## Overview + +Digital signatures provide cryptographic proof of authenticity, integrity, and non-repudiation for FHIR knowledge artifacts. This page describes how CRMI knowledge artifacts align with the [FHIR Digital Signatures specification](https://build.fhir.org/signatures.html) and addresses specific considerations for canonical resource management. + +### Use Case: Protecting Intellectual Property and License Compliance **Background** @@ -17,7 +21,7 @@ A large healthcare system is implementing a comprehensive sepsis detection and m - Treatment recommendation pathways - Quality measures and performance indicators -The research institution has invested significant resources in developing this protocol through clinical trials and expert consensus. They distribute it under a specific license that: +The research institution distributes it under a specific license that: - Allows use in clinical care settings - Requires attribution to the original authors - Prohibits commercial redistribution without permission @@ -27,196 +31,394 @@ The research institution has invested significant resources in developing this p Without proper artifact signing and integrity verification: -- **Unauthorized Modifications**: The healthcare system's IT team might inadvertently or intentionally modify the protocol to fit their local systems, potentially invalidating the evidence base and violating license terms that require use of the unmodified version. +- **Unauthorized Modifications**: The healthcare system's IT team might inadvertently or intentionally modify the protocol, potentially invalidating the evidence base and violating license terms. +- **Attribution Loss**: Original authorship and licensing information could be stripped away or corrupted. +- **Version Confusion**: Multiple versions might circulate, making it unclear which version is authentic. +- **Compliance Auditing**: Organizations cannot definitively prove they are using the authentic, unmodified version. + +**The Solution: Digital Signing** -- **Attribution Loss**: As the artifact moves through different systems and implementations, the original authorship and licensing information could be stripped away or corrupted, making it impossible to verify compliance with attribution requirements. +By digitally signing artifacts, institutions can: -- **Version Confusion**: Multiple versions of the protocol might circulate within the organization, making it unclear which version is the authentic, licensed version and which might be unauthorized derivatives. +- **Ensure Integrity**: Verify that content hasn't been tampered with since signing +- **Preserve Attribution**: Maintain metadata about authors, copyright holders, and licensing terms +- **Enable Compliance**: Demonstrate use of authentic, unmodified versions +- **Track Provenance**: Create an immutable record of the artifact's origin -- **Compliance Auditing**: During license compliance audits, the organization cannot definitively prove they are using the authentic, unmodified version of the protocol as required by their license agreement. +## FHIR Digital Signatures Specification -**The Solution: Digital Signing** +This implementation guide follows the [FHIR Digital Signatures specification](https://build.fhir.org/signatures.html) (R6 Ballot), which defines: + +- **Canonicalization methods** for JSON and XML representations +- **Signature formats** including JWS (JSON Web Signatures) and XML Digital Signatures +- **Detached signatures** carried in Provenance resources +- **Signature verification** requirements for interoperability + +### Key Principles + +1. **Detached Signatures**: Both JWS and XML signatures MUST be detached (the signed content is separate from the signature itself) +2. **Provenance-Based**: Signatures are carried in Provenance resources, either: + - Embedded within the signed Bundle/resource structure, or + - Referenced externally via custom headers or linking mechanisms +3. **Canonicalization**: Content must be canonicalized consistently to ensure signature verification across systems +4. **Certificate Verification**: The signing certificate must be verified against a trusted certificate authority + +## Clarifications on FHIR Digital Signatures + +### Signature Attachment Methods + +The FHIR specification describes several methods for associating signatures with signed content: -By digitally signing the sepsis protocol artifact, the research institution can: +#### 1. Embedded Signatures in Bundles -- **Ensure Integrity**: The digital signature allows any implementer to verify that the artifact hasn't been tampered with or modified since it was signed by the original authors. +For Document Bundles, signatures are commonly embedded within the Bundle structure: -- **Preserve Attribution**: The signature includes metadata about the original authors, copyright holders, and licensing terms that cannot be separated from the artifact without breaking the signature. +- A Provenance resource is included as an entry in the Bundle +- The `Provenance.target` references `Bundle/[id]` to indicate it signs the Bundle +- The `Provenance.signature` element contains the detached signature of the Bundle (with the signing Provenance entry removed) +- This creates a self-contained signed document -- **Enable Compliance**: Healthcare organizations can demonstrate to auditors that they are using authentic, unmodified versions of licensed content by verifying the digital signatures. +**Clarification**: While the signature data itself is "detached" (the payload is omitted from the JWS/XML signature), the Provenance resource carrying the signature is "embedded" within the Bundle structure. This is the recommended approach for signing Bundles. -- **Track Provenance**: The signature creates an immutable record of the artifact's origin, supporting proper attribution and licensing compliance throughout its lifecycle. +#### 2. External Provenance References -### Signing Implementation +For non-Bundle resources or when signatures need to be managed separately: + +- The Provenance resource is stored separately from the signed resource +- The signed resource can reference the Provenance using: + - **X-Provenance header** (custom header, currently used): `X-Provenance: [provenance-url]` + - **Link header** (proposed for improved interoperability): `Link: <[provenance-url]>; rel="provenance"` + +**Recommendation**: To improve interoperability and align with web standards, implementers SHOULD support the standard `Link` header as an alternative to or in addition to the custom `X-Provenance` header. + +### Canonicalization Variants + +The FHIR specification defines several canonicalization methods to balance security and practicality: + +| Canonicalization | Description | Use Case | +|------------------|-------------|----------| +| `http://hl7.org/fhir/canonicalization/json` | Full resource | Maximum integrity verification | +| `http://hl7.org/fhir/canonicalization/json#data` | Excludes `Resource.text` | Signs structured data only | +| `http://hl7.org/fhir/canonicalization/json#static` | Excludes `Resource.text` and `Resource.meta` | Allows resource mobility across servers | +| `http://hl7.org/fhir/canonicalization/json#narrative` | Only `Resource.id` and `Resource.text` | Human attestation of narrative | + +For CRMI knowledge artifacts, the `#static` variant is recommended as it excludes metadata that changes during resource management while preserving the substantive content. + +## Implementation Guidance + +### Signing CRMI Knowledge Artifacts *Implementer note: The code shown throughout this page is pseudo-code and intended to be informative.* A CRMI Knowledge Artifact Server that supports signing will need to implement: -1. JWKS support (https://datatracker.ietf.org/doc/html/rfc7517) with a `/.well-known/jwks` endpoint to expose public signing keys -2. The artifact signing process, which involves three key steps to ensure the integrity and authenticity of FHIR knowledge artifacts as shown below: +1. **JWKS support** ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) with a `/.well-known/jwks.json` endpoint to expose public signing keys +2. **The artifact signing process**, which involves the following steps: -**Step 1: Generate SHA256 Checksum** +#### Step 1: Canonicalize the Resource {: #implementation-1 } -First, a SHA256 checksum is calculated from the resource in its minimal FHIR JSON representation. To focus on the substantive content of the resource rather than implementation-specific metadata, the following elements are excluded from the checksum calculation: -- `id` - Implementation-specific identifier that may vary across systems -- `text` - Human-readable narrative that doesn't affect the computational logic -- `meta` - Metadata that includes versioning, profiles, and tags that may change during processing - -For example: +Canonicalize the resource according to the chosen canonicalization method. For CRMI artifacts using the `#static` variant: ```javascript -// Exclude id, text, and meta elements to focus on substantive content -delete resourceCopy.id +// Create a copy and exclude meta and text elements +let resourceCopy = deepCopy(resource) delete resourceCopy.text delete resourceCopy.meta - -minimalJson = toJson(resourceCopy) -computedHash = sha256(minimalJson) -``` -This ensures that the checksum represents the core clinical or operational content of the artifact, making the signature portable across different FHIR implementations while maintaining integrity verification of the essential elements. - -**Step 2: Create Digital Signature** - -An authoritative CRMI (Canonical Resource Management Infrastructure) service creates a digital signature using the FHIR Signature datatype. The signature process involves: +// Canonicalize according to RFC 8785 (JSON Canonicalization Scheme) +let canonicalJson = canonicalizeJson(resourceCopy) +``` -1. **JWT Creation**: The CRMI server generates a JSON Web Token (JWT) containing exactly two properties: - - `checksum`: The SHA256 hash calculated in Step 1 - - `iss`: The URL of the CRMI server that created the signature (the issuer) +#### Step 2: Create the Signature -2. **Signing**: The JWT is signed using the server's JSON Web Key Set (JWKS), providing cryptographic proof that the signature was created by the authorized CRMI service. +Create a JWS detached signature: -For example: ```javascript -payload = { - iss: "--CRMI Server--", - hash: "--computed SHA256--" -} +// The payload is the canonicalized resource +let payload = base64url(canonicalJson) + +// Create JWS with detached payload (payload is omitted from the JWS) +let jws = createJWS({ + protectedHeader: { + alg: 'RS256', + typ: 'JOSE', + sigT: new Date().toISOString(), + canon: 'http://hl7.org/fhir/canonicalization/json#static', + x5c: [certificateChain], // or use 'kid' for key reference + srCms: [{ + commId: { + id: 'urn:oid:1.2.840.10065.1.12.1.1', + desc: "Author's Signature" + } + }] + }, + payload: payload, // This is used for signing but omitted from output + privateKey: signingKey +}) -JWTSign(payload) - .setProtectedHeader({ alg: 'RS384' }) - .sign(CRMIPrivateKey) +// Result is: base64url(header) + ".." + base64url(signature) +// Note the empty payload section ``` -**Step 3: Attach Signature to Resource** +#### Step 3: Create Provenance Resource -The resulting signature is attached to the FHIR resource by adding it to the `Resource.meta` element [as an extension](StructureDefinition-crmi-artifact-signature.html) with the following characteristics: +Create a Provenance resource to carry the signature: -- **Extension URL**: `artifact-signature` -- **Signature Type**: `ProofOfCreation` (code) -- **Signature Data**: The signed JWT from Step 2 - -For example: -```jsonc +```json { - "meta": { - "extension": [ - { - "url": "http://hl7.org/fhir/uv/crmi/StructureDefinition/artifact-signature", - "valueSignature": { - "type": "ProofOfCreation", - "who": { - "display": "--CRMI Server that signed it--" - }, - "sigFormat": "application/jwt", - "when": "2025-05-12T10:17:55.135Z", - "data": "eyJhbGciOiJSUzM4NCJ9.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDozMDAwL29pZGMiLCJoYXNoIjoiODkyYzk4ZTg2NjBjM2I4NGY4OGNmZmM0NzU5ODgwZWE2ZjczYWZhOWY1OGE1ZWU1ZGQyZjhiN2M0ODI1MGRjYSJ9.T581_ZkQee7RnJpePnApDIgWtHCO6GUFltHF3riM0wEEAMuVK8X63OrBZpRMCFZWwJ9_RQk3Jo9q4Tyu5WxnZaFxyH0cDCs21gFuCtUanRf4jep2ZfShjVjmm90AGyAzz6EeTodpWyNL48Js__ZSmK8HahkFos5DWZdi93BZalOPvR-pAnzKgxyrrkdmLFZBjKC6drzqhfTyTY0P2yLZV0x6X3btvkdcci8_tqKDl8xz84Gut4iHr0fivP7CbzBoIO6Dlw1gScFWaE9ATRDvkTnSYu3JVptMZo4xgKhrL3ZQktrQZm1CIQ8tnMn5hCdT7W-DysejxxH9t128FYBA1Q" - } + "resourceType": "Provenance", + "id": "example-signature", + "target": [{ + "reference": "ActivityDefinition/example" + }], + "recorded": "2025-10-21T10:00:00Z", + "agent": [{ + "type": { + "coding": [{ + "system": "urn:iso-astm:E1762-95:2013", + "code": "1.2.840.10065.1.12.1.1", + "display": "Author's Signature" + }] + }, + "who": { + "identifier": { + "system": "http://example.org/certificates", + "value": "CN=Example Authority" } - ] - }, - // Rest of resource... + } + }], + "signature": [{ + "type": [{ + "system": "urn:iso-astm:E1762-95:2013", + "code": "1.2.840.10065.1.12.1.1" + }], + "when": "2025-10-21T10:00:00Z", + "who": { + "identifier": { + "system": "http://example.org/certificates", + "value": "CN=Example Authority" + } + }, + "targetFormat": "application/fhir+json;canonicalization=http://hl7.org/fhir/canonicalization/json#static", + "sigFormat": "application/jose", + "data": "[base64-encoded-jws]" + }] } ``` -Where the decrypted token payload is: -```json -{ - "alg": "RS384" -} -{ - "iss": "https://registry.example.org/oidc", - "hash": "892c98e8660c3b84f88cffc4759880ea6f73afa9f58a5ee5dd2f8b7c48250dca" -} +#### Step 4: Associate Provenance with Resource + +**For Bundles:** +Include the Provenance resource as an entry in the Bundle. + +**For individual resources:** +Reference the Provenance using headers: + +``` +Link: ; rel="provenance" ``` -This approach ensures that: -- The signature travels with the resource -- The signature can be verified independently by any system with access to the CRMI server's public keys -- The integrity of the core resource content can be validated -- The authenticity of the signature can be confirmed through the issuer identification +Or using the custom header: -### Signature Validation Implementation +``` +X-Provenance: https://example.org/fhir/Provenance/example-signature +``` -A client that intends to verify the integrity and authenticity of a signed FHIR knowledge artifact, would need to implement the following validation process. +### Signature Verification +A client verifying a signed FHIR knowledge artifact should: -**Step 1: Generate SHA256 checksum of the current resource** +#### Step 1: Locate the Provenance Resource -Create a SHA256 hash of the resource in its minimal form, excluding the same elements (`id`, `text`, `meta`), the same as seen in [step 1 of the implementation](#implementation-1) shown above. - -**Step 2: Extract signature data from the resource meta extension** +- For Bundles: Find Provenance entries with `target` referencing `Bundle/[id]` +- For individual resources: Follow `Link` or `X-Provenance` headers -Locate the `artifact-signature` extension in the resource's meta element and extract the JWT from the `valueSignature.data` field. +#### Step 2: Extract Signature Data ```javascript -signatureExtension = findExtension(resource.meta.extension, - "http://hl7.org/fhir/StructureDefinition/artifact-signature") - - -signatureData = signatureExtension.valueSignature.data +let signature = provenance.signature[0] +let jwsData = signature.data // base64-encoded JWS +let targetFormat = signature.targetFormat +let canonicalization = extractCanonicalization(targetFormat) ``` -Parse the JWT to extract the header (for algorithm information) and payload (for issuer and signed hash). +#### Step 3: Reconstruct the Canonicalized Resource -**Step 3: Decode JWT** +```javascript +// Apply the same canonicalization used during signing +let resourceCopy = deepCopy(resource) + +if (canonicalization.includes('#static')) { + delete resourceCopy.text + delete resourceCopy.meta +} else if (canonicalization.includes('#data')) { + delete resourceCopy.text +} + +let canonicalJson = canonicalizeJson(resourceCopy) +let payload = base64url(canonicalJson) +``` -Parse the JWT to extract the header (for algorithm information) and payload (for issuer and signed hash). +#### Step 4: Verify the JWS Signature ```javascript -jwtPayload = decodeJwtPayload(signatureData) +// Parse the JWS +let [headerB64, , signatureB64] = jwsData.split('.') +let header = JSON.parse(base64urlDecode(headerB64)) -issuer = jwtPayload.iss -signedHash = jwtPayload.hash -``` +// Retrieve the public key +let publicKey = await getPublicKey(header) // from x5c or kid -**Step 4: Retrieve and validate against JWKS** +// Reconstruct and verify +let signatureInput = headerB64 + '.' + payload +let isValid = verifySignature(signatureInput, signatureB64, publicKey, header.alg) -- Retrieve the JWKS from the issuer's well-known endpoint -- Use the appropriate public key to verify the JWT signature -- Confirm the JWT was signed by the claimed issuer +if (!isValid) { + throw new Error("Signature verification failed") +} +``` + +#### Step 5: Verify the Certificate ```javascript -// Fetch the JWKS from the issuer's well-known endpoint -jwksUrl = issuer + "/.well-known/jwks.json" -jwks = fetchJwks(jwksUrl) - -// Verify JWT signature using the appropriate public key from JWKS -isValidSignature = verifyJwtSignature(signatureData, jwks, jwtHeader.alg) - -if (!isValidSignature) { - throw "JWT failed signature check" +// Verify the certificate chain against a trusted CA +let certificate = parseCertificate(header.x5c[0]) +let isTrusted = verifyCertificateChain(certificate, trustedCAs) + +if (!isTrusted) { + throw new Error("Certificate verification failed") } ``` -**Step 5: Compare computed hash with signed hash** +A successful validation confirms: +- **Authenticity**: The artifact was signed by the claimed authority +- **Integrity**: The content has not been modified since signing +- **Non-repudiation**: The signature provides cryptographic proof of origin -Validate that the computed SHA256 of the current resource matches the hash value stored in the JWT payload. +## Worked Example: Non-Bundle Resource -```javascript -if (computedHash !== signedHash) { - throw "Resource integrity check failed: computed hash does not match signed hash" +This example demonstrates signing an individual ActivityDefinition resource with an externally referenced Provenance resource. + +### Unsigned Resource + +```json +{ + "resourceType": "ActivityDefinition", + "id": "example-activity", + "url": "http://example.org/ActivityDefinition/example-activity", + "version": "1.0.0", + "status": "active", + "description": "Example activity definition", + "kind": "Task" } +``` -// If we reach here, the signature is valid +### Signed Resource Response + +When retrieved, the resource includes a `Link` header: ``` +HTTP/1.1 200 OK +Content-Type: application/fhir+json +Link: ; rel="provenance" -A successful validation confirms: -- **Authenticity**: The artifact was signed by the claimed CRMI server -- **Integrity**: The substantive content has not been modified since signing -- **Non-repudiation**: The signature provides cryptographic proof of the artifact's origin +{ + "resourceType": "ActivityDefinition", + "id": "example-activity", + "url": "http://example.org/ActivityDefinition/example-activity", + "version": "1.0.0", + "status": "active", + "description": "Example activity definition", + "kind": "Task" +} +``` + +### Provenance Resource + +Retrieved from `https://example.org/fhir/Provenance/activity-signature`: + +```json +{ + "resourceType": "Provenance", + "id": "activity-signature", + "target": [{ + "reference": "ActivityDefinition/example-activity" + }], + "recorded": "2025-10-21T10:00:00Z", + "agent": [{ + "type": { + "coding": [{ + "system": "urn:iso-astm:E1762-95:2013", + "code": "1.2.840.10065.1.12.1.1" + }] + }, + "who": { + "display": "Example CRMI Server", + "identifier": { + "system": "http://example.org/certificates", + "value": "CN=crmi.example.org" + } + } + }], + "signature": [{ + "type": [{ + "system": "urn:iso-astm:E1762-95:2013", + "code": "1.2.840.10065.1.12.1.1" + }], + "when": "2025-10-21T10:00:00Z", + "who": { + "identifier": { + "system": "http://example.org/certificates", + "value": "CN=crmi.example.org" + } + }, + "targetFormat": "application/fhir+json;canonicalization=http://hl7.org/fhir/canonicalization/json#static", + "sigFormat": "application/jose", + "data": "eyJ0eXAiOiJKT1NFIiwiYWxnIjoiUlMyNTYiLCJzaWdUIjoiMjAyNS0xMC0yMVQxMDowMDowMFoiLCJjYW5vbiI6Imh0dHA6Ly9obDcub3JnL2ZoaXIvY2Fub25pY2FsaXphdGlvbi9qc29uI3N0YXRpYyJ9..dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" + }] +} +``` + +This approach allows: +- The signature to be managed independently of the resource +- Multiple signatures to be associated with a single resource +- Signature verification without modifying the original resource +- Standard HTTP header semantics for discovery + +## Additional Considerations + +### Long-Term Signatures + +For signatures that must remain valid over extended periods: + +- Use **JAdES-B-LT** (JSON) or **XAdES-X-L** (XML) profiles +- Include timestamping to prove signature creation time +- Include certificate revocation status +- Archive signing certificates for later verification + +### Signature Rotation + +When signing keys are rotated: + +- Maintain multiple valid signatures with different keys +- Include timestamp information to establish signature order +- Preserve older signatures for auditability +- Update documentation about active signing keys + +### Performance Considerations + +- Cache JWKS endpoints to reduce network calls +- Pre-validate certificates before signature operations +- Consider batch signing for multiple resources +- Use appropriate canonicalization to minimize signature breakage + +## References -If validation fails, the artifact should be considered potentially compromised and appropriate security measures should be taken before using it. \ No newline at end of file +- [FHIR Digital Signatures](https://build.fhir.org/signatures.html) (R6 Ballot) +- [RFC 7515: JSON Web Signature (JWS)](https://tools.ietf.org/html/rfc7515) +- [RFC 8785: JSON Canonicalization Scheme (JCS)](https://www.rfc-editor.org/rfc/rfc8785) +- [RFC 7517: JSON Web Key (JWK)](https://datatracker.ietf.org/doc/html/rfc7517) +- [XML Signature Syntax and Processing Version 1.1](https://www.w3.org/TR/xmldsig-core/) +- [ETSI TS 119 182-1: JAdES](https://www.etsi.org/deliver/etsi_ts/119100_119199/11918201/01.01.01_60/ts_11918201v010101p.pdf) +- [W3C XAdES](https://www.w3.org/TR/XAdES/) \ No newline at end of file diff --git a/oids.ini b/oids.ini index c9d9d40..7fb426a 100644 --- a/oids.ini +++ b/oids.ini @@ -118,7 +118,6 @@ crmi-configurationFor = 2.16.840.1.113883.4.642.40.38.42.63 crmi-endpointSupportsPost = 2.16.840.1.113883.4.642.40.38.42.64 crmi-implementationguide = 2.16.840.1.113883.4.642.40.38.42.65 crmi-manifestparameters = 2.16.840.1.113883.4.642.40.38.42.66 -crmi-artifact-signature = 2.16.840.1.113883.4.642.40.38.42.67 crmi-license-detail = 2.16.840.1.113883.4.642.40.38.42.68 crmi-license = 2.16.840.1.113883.4.642.40.38.42.69 crmi-publishablegroup = 2.16.840.1.113883.4.642.40.38.42.70 @@ -197,7 +196,6 @@ shareable-example = 2.16.840.1.113883.4.642.40.38.28.9 manifest-example-initial-draft = 2.16.840.1.113883.4.642.40.38.28.10 outcome-manifest-example = 2.16.840.1.113883.4.642.40.38.28.11 ExampleLicensedLibrary = 2.16.840.1.113883.4.642.40.38.28.12 -ExampleSignatureLibrary = 2.16.840.1.113883.4.642.40.38.28.13 ExampleSignedLibrary = 2.16.840.1.113883.4.642.40.38.28.14 [Measure]