Kurz gesagt: Wir packen eine Verifiable Credential in ein JWS, aber die eigentliche Signatur kommt von einem externen Signierbaustein, der nur kleine Datenmengen (max. 255 Byte) verarbeiten kann. Deshalb wird nicht der ganze Token signiert, sondern dessen Hash. Das Verfahren heißt hier ES256-DH – im Kern ganz normales ECDSA über P-256 mit SHA-256, nur mit einem zusätzlichen Hash-Schritt davor.
sig_stub.rb– simuliert den Signierbaustein. Bekommt einen 32-Byte-Hash (als Hex) und gibt eine Signatur aus. Ein echter Chip könnte z.B. an dieser Stelle stehen.build_jws.rb– baut das JWS. Läuft in zwei Schritten: erst rechnet es aus, was signiert werden muss, am Ende setzt es alles zusammen.verify_jws.rb– prüft am Ende, ob die Signatur stimmt.
0. Vorbereitung
Mit oydid ein did:oyd für die Verwendung in der JWS erzeugen:
SK=$(echo "96fe0f41947d645c7a1858c48c7a0560e7e5bd3d45125b57a611a3a9a103626b" | \
oydid hex2mb -k p256 | sed 's/private key: //')
echo '{}' | oydid create --key-type p256 --doc-enc "$SK"
# created did:oyd:zQmWLt1m6QuApNgVqFRg1VVJcs3zzkKQjVYpta2ZEJBbsvEüberprüfen:
oydid read zQmWLt1m6QuApNgVqFRg1VVJcs3zzkKQjVYpta2ZEJBbsvE
echo z4oJ8dYxWkgUe1bxnyhhiSRrhF19baQncEdr8JYgaJtAAJYVUhSBWMdqcJwpEZdLmBXVe7HKfZeRXJ5HfPKFpZNe1iPta | \
oydid mb2hex
# must match public key in hex1. Ausrechnen, was signiert wird
build_jws.rb baut den Header (mit alg, typ und der Issuer-DID als kid)
und die Payload (die Credential), klebt beides zum sogenannten Signing Input
zusammen und hasht das Ergebnis. Heraus kommt ein 32-Byte-Hash – genau die
Größe, die der Signierbaustein braucht.
cat input_vc.json | ./build_jws.rb hash > input_hash.txt2. Signieren
Der Hash geht an den Baustein (hier: den Stub), zurück kommt die Signatur als rohes R||S (64 Byte).
cat input_hash.txt | ./sig_stub.rb > output_sig.txt3. Zusammensetzen
build_jws.rb fügt Header, Payload und Signatur zum fertigen JWS zusammen.
./build_jws.rb assemble input_vc.json output_sig.txt > credential.jwsFertig ist das JWS in der Form Header.Payload.Signatur (jeweils Base64URL).
cat credential.jws | ./verify_jws.rbDer Verifier baut die Signing Input genauso wieder zusammen, hasht sie und prüft die Signatur gegen den Public Key. Wird am Token irgendwas verändert – sei es in der Payload oder im Header – fällt die Prüfung durch.
Würde man nur die Credential allein signieren, könnte jemand den Header
(z.B. den kid, also den Schlüsselverweis) austauschen, ohne die Signatur zu
brechen. Weil hier die komplette Signing Input (Header + Payload) gehasht und
signiert wird, ist auch der Header geschützt.
Gearbeitet wird mit einem NIST-P-256-Schlüsselpaar. Der Stub nimmt den
privaten Schlüssel aus BSK (Hex) bzw. einem Default; der Verifier nimmt
den öffentlichen Schlüssel aus PUBKEY, PUBKEY_FILE, aus BSK
(abgeleitet) oder einem Default. Im echten Betrieb löst man den Public Key
über die kid-DID auf.
Die genaue Algorithmus-Definition samt Test-Vektor steht in ES256-DH.md.