While WebAuthn was designed primarily for authentication, the framework can be extended to support signing with keypairs generated on authenticators without requiring custom clients for end users.
During a standard WebAuthn interaction, a WebAuthn authenticator creates a unique asymmetric keypair with a relying party at registration, with the private key residing only on the authenticator and the public key sent back. As part of the registration event, a challenge from the relying party is signed with the unique keypair and returned. Afterwards, an authentication event uses this key pair to sign a challenge which includes a random string provided by the relying party. In short, the registration and authentication events consists of a cryptographic signing process with the public key returned, which is then used to sign a data set which is provided by the relying party. Replacing the randomly generated challenge with a hash of a file to be signed allows this signature flow to be applied to actual documentation, with the public key also available for verification after the fact.
The main advantages to this flow is it allows for any cryptographic algorithm supported by the WebAuthn framework to be used, and can leverage the existing WebAuthn support in browsers or local APIs. All of the modifications required would be limited to the WebAuthn libraries/servers owned by the relying party, permitting a service to implement arbitrary file signing without requiring significant user commitment.
This section will discuss at a high level the architecture necessary for adapting a WebAuthn flow for file signing. The actual implementation will depend on the environment, WebAuthn server or library and the WebAuthn client (browser or local) in use, and is outside of the scope of this document.
In the MakeCredential operation, the challenge is defined as a random string. To ensure each credential and keypair generated is unique, this challenge should remain unique for the MakeCredential operation. However, in the MakeCredential operation, the algorithm for the keypair must be defined. The WebAuthn framework defines the algorithm to be used when the authentication generates the keypair via the identifier registered in the IANA COSE Algorithm Registry, as detailed in the Cryptographic Algorithm Identifier section. For example, EdDSA is defined as (-8). The requirements for detailing the algorithm to be used is well defined in the WebAuthn specification, however not all authenticators support all algorithms. It is recommended to include logic to capture the case where a user attempts to use an algorithm not supported by their authenticator.
Further, the User Verification behavior is also defined by the MakeCredential operation, allowing a relying party to define if the credential will require a PIN or biometric supplied by the user to be created. This is recommended to ensure the user has defined a PIN and/or biometric identifier for their WebAuthn Authenticator. This can allow for securing keypairs used for signing to only valid users of the WebAuthn Authenticator, by also defining the User Verification as required for the Authentication events leveraged for signing.
In addition, it is also recommended Attestation is enabled during the registration event to verify the authenticity of the WebAuthn authenticator being used. Other fields in the MakeCredential can be provided with other identifying information or left to default values. However, it is important to note the originID (specifically, the rp.id value) should match the endpoint the user is provided to access the signing service, as is the case in a standard WebAuthn service.
With the MakeCredential operation creating a keypair with the algorithm defined, the GetAssertion operation is used to actually sign the data. The GetAssertion flow also supplies a challenge from the relying party to the authenticator to be signed as part of the Authentication ceremony. This challenge can be replaced with a hash of the document to be signed, creating an association with the document or data set to be signed. The method in which this hash is generated should be determined by the capabilities of the environment and security requirements under which the application is run.
No modifications are required to this operation to support the signature, but both the client data and authenticator data will need to be recorded along with the returned signature. This is because the signature returned by WebAuthn is for an object comprising both the client data (including the hash string) and authenticator data, and both sets of data will be required to validate the signature in the future. To assist with the, the PublicKeyCredential returned will include the client data (as a JSON object), the authenticator data and the signature. Simply archiving everything returned will allow for ease of signature validation. Other metadata, such as the timestamp of the signing event, may be useful to include as well.
The signature can be verified with the public key previously returned during the registration event. With a verified signature, the hash within the signed data can be validated against a hash regenerated from the signed file. Should a user need to demonstrate they possess the WebAuthn authenticator used in the signature, a simple WebAuthn authentication event using the identity registered may be used to verify their authenticator.