<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" version="3" ipr="trust200902" docName="draft-hardt-httpbis-signature-key-02" submissionType="IETF" category="std" xml:lang="en" indexInclude="true">

<front>
<title abbrev="Signature-Key">HTTP Signature-Key Header</title><seriesInfo value="draft-hardt-httpbis-signature-key-02" stream="IETF" status="standard" name="Internet-Draft"/>
<author initials="D." surname="Hardt" fullname="Dick Hardt"><organization>Hellō</organization><address><postal><street/>
</postal><email>dick.hardt@gmail.com</email>
</address></author><author initials="T." surname="Meunier" fullname="Thibault Meunier"><organization>Cloudflare</organization><address><postal><street/>
</postal><email>ot-ietf@thibault.uk</email>
</address></author><date/>
<area>Applications and Real-Time</area>
<workgroup>HTTP</workgroup>
<keyword>http</keyword>
<keyword>signature</keyword>
<keyword>authentication</keyword>
<keyword>jwk</keyword>
<keyword>jwt</keyword>

<abstract>
<t>This document defines the Signature-Key HTTP header field for distributing public keys used to verify HTTP Message Signatures as defined in RFC 9421. Four initial key distribution schemes are defined: pseudonymous inline keys (hwk), identified signers with JWKS URI discovery (jwks_uri), X.509 certificate chains (x509), and JWT-based delegation (jwt). These schemes enable flexible trust models ranging from privacy-preserving pseudonymous verification to PKI-based identity chains and horizontally-scalable delegated authentication.</t>
</abstract>

<note><name>Discussion Venues</name>
<t><em>Note: This section is to be removed before publishing as an RFC.</em></t>
<t>Source for this draft and an issue tracker can be found at <eref target="https://github.com/dickhardt/signature-key">https://github.com/dickhardt/signature-key</eref>.</t>
</note>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>HTTP Message Signatures <xref target="RFC9421"/> provides a powerful mechanism for creating and verifying digital signatures over HTTP messages. To verify a signature, the verifier needs the signer's public key. While RFC 9421 defines signature creation and verification procedures, it intentionally leaves key distribution to application protocols, recognizing that different deployments have different trust requirements.</t>
<t>This document defines the Signature-Key HTTP header field to standardize key distribution for HTTP Message Signatures. The header enables signers to provide their public key or a reference to it directly in the HTTP message, allowing verifiers to obtain keying material without prior coordination.</t>
<t>The header supports four schemes, each designed for different trust models and operational requirements:</t>

<ol spacing="compact">
<li><strong>Header Web Key (hwk)</strong> - Self-contained public keys for pseudonymous verification</li>
<li><strong>JWKS URI (jwks_uri)</strong> - Identified signers with key discovery via metadata</li>
<li><strong>X.509 (x509)</strong> - Certificate-based verification with PKI trust chains</li>
<li><strong>JWT (jwt)</strong> - Delegated keys embedded in signed JWTs for horizontal scale</li>
</ol>
<t>Additional schemes may be defined through the IANA registry established by this document.</t>
<t>The Signature-Key header works in conjunction with the Signature-Input and Signature headers defined in RFC 9421, using matching labels to correlate signature metadata with keying material.</t>
</section>

<section anchor="the-signature-key-header-field"><name>The Signature-Key Header Field</name>
<t>The Signature-Key header field provides the public key or key reference needed to verify an HTTP Message Signature. The header is a Structured Field Dictionary <xref target="RFC8941"/> keyed by signature label, where each member describes how to obtain the verification key for the corresponding signature.</t>
<t><strong>Format:</strong></t>

<artwork><![CDATA[Signature-Key: <label>=<scheme>;<parameters>...
]]>
</artwork>
<t>Where:
- <tt>&lt;label&gt;</tt> (dictionary key) matches the label in Signature-Input and Signature headers
- <tt>&lt;scheme&gt;</tt> (token) identifies the key distribution scheme
- <tt>&lt;parameters&gt;</tt> are semicolon-separated key-value pairs whose values are structured field strings or byte sequences, varying by scheme</t>
<t>Multiple keys are comma-separated per the dictionary format. See <xref target="RFC8941"/> for definitions of dictionary, token, string, and byte sequence.</t>
<t><strong>Example:</strong></t>

<artwork><![CDATA[Signature-Input: sig=("@method" "@authority" "@path" "signature-key"); created=1732210000
Signature: sig=:MEQCIA5...
Signature-Key: sig=hwk;kty="OKP";crv="Ed25519";x="JrQLj..."
]]>
</artwork>
<t><strong>Label Correlation:</strong></t>
<t>Labels are correlated by equality of label names across Signature-Input, Signature, and Signature-Key. Signature-Key is a dictionary keyed by label; Signature-Input and Signature are the sources of what signatures are present; Signature-Key provides keying material for those labels.</t>
<t>Verifiers MUST:</t>

<ol>
<li><t>Parse Signature-Input and Signature per RFC 9421 and obtain the set of signature labels present. The verifier determines which labels it is attempting to verify based on application context and RFC 9421 processing.</t>
</li>
<li><t>Parse Signature-Key as a Structured Fields Dictionary</t>
</li>
<li><t>For each label being verified, select the Signature-Key dictionary member with the same name</t>
</li>
<li><t>If the corresponding dictionary member is missing, verification for that signature MUST fail</t>
</li>
</ol>
<blockquote><t><strong>Note:</strong> A verifier might choose to verify only a subset of labels present (e.g., the application-required signature); labels not verified can be ignored.</t>
</blockquote>
<section anchor="label-consistency"><name>Label Consistency</name>
<t>If a label appears in Signature or Signature-Input, and the verifier attempts to verify it, the corresponding member MUST exist in Signature-Key. If Signature-Key contains members for labels not being verified, verifiers MAY ignore them.</t>
</section>

<section anchor="multiple-signatures"><name>Multiple Signatures</name>
<t>The dictionary format supports multiple signatures per message. Each signature has its own dictionary member keyed by its unique label:</t>

<artwork><![CDATA[Signature-Input: sig1=(... "signature-key"), sig2=(... "signature-key")
Signature: sig1=:...:, sig2=:...:
Signature-Key: sig1=jwt;jwt="eyJ...", sig2=jwks_uri;id="https://example.com";well-known="meta";kid="k1"
]]>
</artwork>
<t>Most deployments SHOULD use a single signature. When multiple signatures are required, the complete Signature-Key header (containing all keys) MUST be populated before any signature is created, and each signature MUST cover <tt>signature-key</tt>. This ensures all signatures protect the integrity of all key material. See <eref target="#signature-key-integrity">Signature-Key Integrity</eref> in Security Considerations. Alternative key distribution mechanisms outside this specification may be used for scenarios requiring independent signature addition.</t>
</section>

<section anchor="header-web-key-hwk"><name>Header Web Key (hwk)</name>
<t>The hwk scheme provides a self-contained public key inline in the header, enabling pseudonymous verification without key discovery. The parameter names and values correspond directly to the JWK parameters defined in <xref target="RFC7517"/>.</t>
<t><strong>Parameters by key type:</strong></t>
<t>OKP (Octet Key Pair):</t>

<ul>
<li><t><tt>kty</tt> (REQUIRED, String) - "OKP"</t>
</li>
<li><t><tt>crv</tt> (REQUIRED, String) - Curve name (e.g., "Ed25519")</t>
</li>
<li><t><tt>x</tt> (REQUIRED, String) - Public key value</t>
</li>
</ul>

<artwork><![CDATA[Signature-Key: sig=hwk;kty="OKP";crv="Ed25519";x="JrQLj5P..."
]]>
</artwork>
<t>EC (Elliptic Curve):</t>

<ul>
<li><t><tt>kty</tt> (REQUIRED, String) - "EC"</t>
</li>
<li><t><tt>crv</tt> (REQUIRED, String) - Curve name (e.g., "P-256", "P-384")</t>
</li>
<li><t><tt>x</tt> (REQUIRED, String) - X coordinate</t>
</li>
<li><t><tt>y</tt> (REQUIRED, String) - Y coordinate</t>
</li>
</ul>

<artwork><![CDATA[Signature-Key: sig=hwk;kty="EC";crv="P-256";x="f83OJ3D...";y="x_FEzRu..."
]]>
</artwork>
<t>RSA:</t>

<ul>
<li><t><tt>kty</tt> (REQUIRED, String) - "RSA"</t>
</li>
<li><t><tt>n</tt> (REQUIRED, String) - Modulus</t>
</li>
<li><t><tt>e</tt> (REQUIRED, String) - Exponent</t>
</li>
</ul>

<artwork><![CDATA[Signature-Key: sig=hwk;kty="RSA";n="0vx7agoebGcQ...";e="AQAB"
]]>
</artwork>
<t><strong>Constraints:</strong></t>

<ul>
<li><t>The <tt>alg</tt> parameter MUST NOT be present (algorithm is specified in Signature-Input)</t>
</li>
<li><t>The <tt>kid</tt> parameter SHOULD NOT be used</t>
</li>
</ul>
<blockquote><t><strong>Design Note:</strong> The hwk parameters use structured field strings rather than byte sequences. JWK key values are base64url-encoded per <xref target="RFC7517"/>, while structured field byte sequences use base64 encoding per <xref target="RFC8941"/>. Using strings allows implementations to pass JWK values directly without converting between base64url and base64, avoiding a potential source of encoding bugs.</t>
</blockquote><t><strong>Use cases:</strong></t>

<ul>
<li><t>Privacy-preserving agents that avoid identity disclosure</t>
</li>
<li><t>Experimental or temporary access without registration</t>
</li>
<li><t>Rate limiting and reputation building on a per-key basis</t>
</li>
</ul>
</section>

<section anchor="jwks-uri-discovery-jwks-uri"><name>JWKS URI Discovery (jwks_uri)</name>
<t>The jwks_uri scheme identifies the signer and enables key discovery via a metadata document containing a <tt>jwks_uri</tt> property.</t>
<t><strong>Parameters:</strong></t>

<ul>
<li><t><tt>id</tt> (REQUIRED, String) - Signer identifier (HTTPS URL)</t>
</li>
<li><t><tt>well-known</tt> (REQUIRED, String) - Metadata document name under <tt>/.well-known/</tt></t>
</li>
<li><t><tt>kid</tt> (REQUIRED, String) - Key identifier</t>
</li>
</ul>
<blockquote><t><strong>Note:</strong> The <tt>well-known</tt> parameter may be shortened to <tt>wk</tt> once the semantics are stable.</t>
</blockquote><t><strong>Discovery procedure:</strong></t>

<ol>
<li><t>Fetch <tt>{id}/.well-known/{well-known}</tt></t>
</li>
<li><t>Parse as JSON metadata</t>
</li>
<li><t>Extract <tt>jwks_uri</tt> property</t>
</li>
<li><t>Fetch JWKS from <tt>jwks_uri</tt></t>
</li>
<li><t>Find key with matching <tt>kid</tt></t>
</li>
</ol>
<t><strong>Example:</strong></t>

<artwork><![CDATA[Signature-Key: sig=jwks_uri;id="https://agent.example";well-known="aauth-agent";kid="key-1"
]]>
</artwork>
<t><strong>Use cases:</strong></t>

<ul>
<li><t>Identified services with stable HTTPS identity</t>
</li>
<li><t>Search engine crawlers and monitoring services</t>
</li>
<li><t>Services requiring explicit entity identification</t>
</li>
</ul>
</section>

<section anchor="x-509-certificates-x509"><name>X.509 Certificates (x509)</name>
<t>The x509 scheme provides certificate-based verification using PKI trust chains.</t>
<t><strong>Parameters:</strong></t>

<ul>
<li><t><tt>x5u</tt> (REQUIRED, String) - URL to X.509 certificate chain (PEM format, <xref target="RFC7517"/> Section 4.6)</t>
</li>
<li><t><tt>x5t</tt> (REQUIRED, Byte Sequence) - Certificate thumbprint: SHA-256 hash of DER-encoded leaf certificate</t>
</li>
</ul>
<t><strong>Verification procedure:</strong></t>

<ol>
<li><t>Check cache for certificate with matching <tt>x5t</tt></t>
</li>
<li><t>If not cached or expired, fetch PEM from <tt>x5u</tt></t>
</li>
<li><t>Validate certificate chain to trusted root CA</t>
</li>
<li><t>Check certificate validity and revocation status</t>
</li>
<li><t>Verify <tt>x5t</tt> matches leaf certificate</t>
</li>
<li><t>Extract public key from end-entity certificate</t>
</li>
<li><t>Verify signature using extracted key</t>
</li>
<li><t>Cache certificate indexed by <tt>x5t</tt></t>
</li>
</ol>
<t><strong>Example:</strong></t>

<artwork><![CDATA[Signature-Key: sig=x509;x5u="https://agent.example/.well-known/cert.pem";x5t=:bWcoon4QTVn8Q6xiY0ekMD6L8bNLMkuDV2KtvsFc1nM=:
]]>
</artwork>
<t><strong>Use cases:</strong></t>

<ul>
<li><t>Enterprise environments with PKI infrastructure</t>
</li>
<li><t>Integration with existing certificate management systems</t>
</li>
<li><t>Scenarios requiring certificate revocation checking</t>
</li>
<li><t>Regulated industries requiring certificate-based authentication</t>
</li>
</ul>
</section>

<section anchor="jwt-confirmation-key-jwt"><name>JWT Confirmation Key (jwt)</name>
<t>The jwt scheme embeds a public key inside a signed JWT using the <tt>cnf</tt> (confirmation) claim <xref target="RFC7800"/>, enabling delegation and horizontal scale.</t>
<t><strong>Parameters:</strong></t>

<ul spacing="compact">
<li><tt>jwt</tt> (REQUIRED, String) - Compact-serialized JWT</li>
</ul>
<t><strong>JWT requirements:</strong></t>

<ul>
<li><t>MUST contain <tt>cnf.jwk</tt> claim with embedded JWK</t>
</li>
<li><t>SHOULD contain standard claims: <tt>iss</tt>, <tt>sub</tt>, <tt>exp</tt>, <tt>iat</tt></t>
</li>
</ul>
<blockquote><t><strong>Note:</strong> The mechanism by which the JWT is obtained is out of scope of this specification.</t>
</blockquote><t><strong>Verification procedure:</strong></t>

<ol>
<li><t>Validate the JWT: verify signature using issuer's public key and verify claims per policy (<tt>iss</tt>, <tt>exp</tt>, etc.)</t>
</li>
<li><t>Extract JWK from <tt>cnf.jwk</tt></t>
</li>
<li><t>Verify HTTP Message Signature using extracted key</t>
</li>
</ol>
<t><strong>Example:</strong></t>

<artwork><![CDATA[Signature-Key: sig=jwt;jwt="eyJhbGciOiJFUzI1NiI..."
]]>
</artwork>
<t><strong>JWT payload example:</strong></t>

<sourcecode type="json"><![CDATA[{
  "iss": "https://issuer.example",
  "sub": "instance-123",
  "exp": 1732210000,
  "cnf": {
    "jwk": {
      "kty": "OKP",
      "crv": "Ed25519",
      "x": "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs"
    }
  }
}
]]>
</sourcecode>
<t><strong>Use cases:</strong></t>

<ul>
<li><t>Distributed services with ephemeral instance keys</t>
</li>
<li><t>Delegation scenarios where instances act on behalf of an authority</t>
</li>
<li><t>Short-lived credentials for horizontal scaling</t>
</li>
</ul>
</section>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>

<section anchor="key-validation"><name>Key Validation</name>
<t>Verifiers MUST validate all cryptographic material before use:</t>

<ul>
<li><t><strong>hwk</strong>: Validate JWK structure and key parameters</t>
</li>
<li><t><strong>jwks_uri</strong>: Verify HTTPS transport and validate fetched JWKS</t>
</li>
<li><t><strong>x509</strong>: Validate complete certificate chain, check revocation status</t>
</li>
<li><t><strong>jwt</strong>: Verify JWT signature and validate embedded JWK</t>
</li>
</ul>
</section>

<section anchor="caching-and-performance"><name>Caching and Performance</name>
<t>Verifiers MAY cache keys to improve performance but MUST implement appropriate cache expiration:</t>

<ul>
<li><t><strong>jwks_uri</strong>: Respect cache-control headers, implement reasonable TTLs</t>
</li>
<li><t><strong>x509</strong>: Cache by <tt>x5t</tt>, invalidate on certificate expiry</t>
</li>
<li><t><strong>jwt</strong>: Cache embedded keys until JWT expiration</t>
</li>
</ul>
<t>Verifiers SHOULD implement cache limits to prevent resource exhaustion attacks.</t>
</section>

<section anchor="scheme-specific-risks"><name>Scheme-Specific Risks</name>
<t><strong>hwk</strong>: No identity verification - suitable only for scenarios where pseudonymous access is acceptable.</t>
<t><strong>jwks_uri</strong>: Relies on HTTPS security - vulnerable to DNS/CA compromise. Verifiers should implement certificate pinning where appropriate.</t>
<t><strong>x509</strong>: Requires robust certificate validation including revocation checking. Verifiers MUST NOT skip certificate chain validation.</t>
<t><strong>jwt</strong>: Delegation trust depends on JWT issuer verification. Verifiers MUST validate JWT signatures and claims before trusting embedded keys.</t>
</section>

<section anchor="algorithm-selection"><name>Algorithm Selection</name>
<t>The <tt>alg</tt> parameter in Signature-Input (RFC 9421) determines the signature algorithm. Verifiers MUST:</t>

<ul>
<li><t>Validate algorithm against policy (reject weak algorithms)</t>
</li>
<li><t>Ensure key type matches algorithm requirements</t>
</li>
<li><t>Reject algorithm/key mismatches</t>
</li>
</ul>
</section>

<section anchor="signature-key-integrity"><name>Signature-Key Integrity</name>
<t>The Signature-Key header SHOULD be included as a covered component in Signature-Input:</t>

<artwork><![CDATA[Signature-Input: sig=("@method" "@authority" "@path" "signature-key"); created=1732210000
]]>
</artwork>
<t>If <tt>signature-key</tt> is not covered, an attacker can modify the header without invalidating the signature. Attacks include:</t>
<t><strong>Scheme substitution</strong>: An attacker extracts the public key from an <tt>hwk</tt> scheme and republishes it via <tt>jwks_uri</tt> under their own identity, causing verifiers to attribute the request to the attacker.</t>
<t><strong>Identity substitution</strong>: An attacker modifies the <tt>id</tt> parameter in a <tt>jwks_uri</tt> scheme to point to their own metadata endpoint that returns the same public key, impersonating a different signer.</t>
<t>Verifiers SHOULD reject requests where <tt>signature-key</tt> is not a covered component.</t>
</section>
</section>

<section anchor="privacy-considerations"><name>Privacy Considerations</name>

<section anchor="pseudonymity-vs-identity"><name>Pseudonymity vs. Identity</name>
<t>The hwk scheme enables pseudonymous operation where the signer's identity is not disclosed. Verifiers should be aware that:</t>

<ul>
<li><t>hwk provides no identity linkage across requests (unless keys are reused)</t>
</li>
<li><t>Key reuse enables tracking but may be necessary for reputation/rate-limiting</t>
</li>
<li><t>Verifiers should not log or retain hwk keys beyond operational necessity</t>
</li>
</ul>
<t>The jwks_uri, x509, and jwt schemes all reveal signer identity. Protocols using these schemes should inform signers that their identity will be disclosed to verifiers.</t>
</section>

<section anchor="key-discovery-tracking"><name>Key Discovery Tracking</name>
<t>The jwks_uri and x509 schemes require verifiers to fetch resources from signer-controlled URLs. This creates potential tracking vectors:</t>

<ul>
<li><t>Signers can observe when and from where keys are fetched</t>
</li>
<li><t>Verifiers should cache keys to minimize fetches</t>
</li>
<li><t>Verifiers may wish to use shared caching infrastructure to reduce fingerprinting</t>
</li>
</ul>
</section>

<section anchor="jwt-contents"><name>JWT Contents</name>
<t>JWTs in the jwt scheme may contain additional claims beyond <tt>cnf</tt>. Verifiers should:</t>

<ul>
<li><t>Only process claims necessary for verification</t>
</li>
<li><t>Not log or retain unnecessary JWT claims</t>
</li>
<li><t>Be aware that JWT contents are visible to network observers unless using TLS</t>
</li>
</ul>
</section>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>

<section anchor="http-field-name-registration"><name>HTTP Field Name Registration</name>
<t>This document registers the Signature-Key header field in the "Hypertext Transfer Protocol (HTTP) Field Name Registry" defined in <xref target="RFC9110"/>.</t>
<t>Header field name: Signature-Key</t>
<t>Applicable protocol: http</t>
<t>Status: standard</t>
<t>Author/Change controller: IETF</t>
<t>Specification document(s): [this document]</t>
</section>

<section anchor="signature-key-scheme-registry"><name>Signature-Key Scheme Registry</name>
<t>This document establishes the "HTTP Signature-Key Scheme" registry. This registry allows for the definition of additional key distribution schemes beyond those defined in this document.</t>

<section anchor="registration-procedure"><name>Registration Procedure</name>
<t>New scheme registrations require Specification Required per <xref target="RFC8126"/>.</t>
</section>

<section anchor="initial-registry-contents"><name>Initial Registry Contents</name>
<table>
<thead>
<tr>
<th>Scheme</th>
<th>Description</th>
<th>Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td>hwk</td>
<td>Header Web Key - inline public key</td>
<td>[this document]</td>
</tr>

<tr>
<td>jwks_uri</td>
<td>JWKS URI Discovery - key discovery via metadata</td>
<td>[this document]</td>
</tr>

<tr>
<td>x509</td>
<td>X.509 Certificate - PKI certificate chain</td>
<td>[this document]</td>
</tr>

<tr>
<td>jwt</td>
<td>JWT Confirmation Key - delegated key in JWT</td>
<td>[this document]</td>
</tr>
</tbody>
</table></section>

<section anchor="registration-template"><name>Registration Template</name>

<dl spacing="compact">
<dt>Scheme Name:</dt>
<dd>The token value used in the Signature-Key header</dd>
<dt>Description:</dt>
<dd>A brief description of the scheme</dd>
<dt>Specification:</dt>
<dd>Reference to the specification defining the scheme</dd>
<dt>Parameters:</dt>
<dd>List of parameters defined for this scheme</dd>
</dl>
</section>
</section>
</section>

</middle>

<back>
<references><name>Normative References</name>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7517.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7800.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8126.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8941.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9110.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9421.xml"/>
</references>

<section anchor="acknowledgments"><name>Acknowledgments</name>
<t>The author would like to thank reviewers for their feedback on this specification.</t>
</section>

</back>

</rfc>
