Diese Demo signiert eine Verifiable Credential (VC) und verpackt sie in eine Verifiable Presentation (VP). Zwei verschiedene Signaturverfahren kommen zum Einsatz:
- Die VC wird vom Hersteller (Issuer) mit seinem normalen Software-Key
signiert – normales
ES256(ECDSA P-256 / SHA-256), voll JOSE-interoperabel. - Die VP wird vom Signierbaustein / Chip (Holder) signiert. Der Chip verarbeitet nur kleine Datenmengen (max. 255 Byte), deshalb wird nicht der ganze Token signiert, sondern dessen Hash. Dieses Verfahren heißt hier ES256-DH – im Kern ES256, aber mit einem zusätzlichen Hash-Schritt davor (Double-Hashing).
build_jws.rb– signiert die VC in einem Schritt mit dem Hersteller-Key (Standard-ES256).verify_jws.rb– prüft die VC.sig_stub.rb– simuliert den Chip: bekommt einen 32-Byte-Hash und gibt eine Signatur (R||S) zurück. An dieser Stelle wäre der echte Chip.build_vp.rb– baut die VP um die VC herum und lässt sie vom Chip signieren (ES256-DH, zwei Schritte).verify_vp.rb– prüft beide Lagen der VP.
0. Vorbereitung – mit oydid eine
did:oyd / did:web für den Hersteller erzeugen (das ist der issuer in
input_vc.json):
Hinweis: Der erzeugte Schlüssel liegt multibase-kodiert (z…) in der Datei
<id>_private_key.enc. Die Ruby-Scripts brauchen den rohen Hex – daher den
Dateinamen aus der DID ableiten, die Datei lesen (SK) und per oydid mb2hex
nach Hex wandeln (SK_HEX):
DID=$(echo '{}' | oydid create --key-type p256 --json-output | jq -r '.did')
SK=$(cat "$(cut -c1-10 <<< "${DID#did:oyd:}")_private_key.enc")
SK_HEX=$(oydid mb2hex <<< "$SK" | sed 's/.*: //')
DID="${DID/did:oyd:/did:web:oydid.ownyourdata.eu:}"
echo $DID
# did:web:oydid.ownyourdata.eu:zQmVPmNLuo9ntbyvSsJNVHA3xJY5DHw8mf86AWaikssKvM71. VC signieren – der kid ergibt sich aus dem issuer-Feld der VC; der
Schlüssel kommt als Hex aus ISSUER_SK (oder als Argument):
cat input_vc.json | ISSUER_SK=$SK_HEX ./build_jws.rb > credential.jws2. VC prüfen – der Verifier baut die Signing Input nach und prüft die
Signatur gegen den über die kid-DID aufgelösten Public Key:
cat credential.jws | ./verify_jws.rbDie VP verpackt die VC, ohne sie zu entpacken: die komplette VC-JWS wird als
String in ein Objekt vom Typ EnvelopedVerifiableCredential eingebettet
(id: "data:application/vc+jwt,<VC-JWS>"). Das VP-JSON wird dann selbst zur
Payload eines JWS und vom Chip signiert.
0. Vorbereitung – Holder-DID (das Gerät) erzeugen. Der Chip signiert mit
seinem eigenen, fest vorgegebenen Schlüssel (BSK in sig_stub.rb); dieser
muss zum #key-doc der Holder-DID passen. Der Key-Hex ist hier fix (nicht aus
einer Datei) und entspricht dem Default in sig_stub.rb:
HOLDER_SK_HEX="96fe0f41947d645c7a1858c48c7a0560e7e5bd3d45125b57a611a3a9a103626b"
HOLDER_SK=$(echo "$HOLDER_SK_HEX" | oydid hex2mb -k p256 | sed 's/private key: //') # Multibase fuer oydid
HOLDER_DID=$(echo '{}' | oydid create --key-type p256 --doc-enc "$HOLDER_SK" --json-output | jq -r '.did')
HOLDER_DID="${HOLDER_DID/did:oyd:/did:web:oydid.ownyourdata.eu:}"
echo $HOLDER_DID
# did:web:oydid.ownyourdata.eu:zQmYmDf3nBJEGy9qznPMEsxi5CrGA9Ha61o5Gfgk5yX2XtD1. VP-Signing-Input hashen (für den Chip):
cat credential.jws | ./build_vp.rb hash $HOLDER_DID > vp_hash.txt
# optional: AUD=<verifier> NONCE=<zufall> voranstellen (in beiden build_vp-Aufrufen identisch!)2. Chip signiert (mit seinem eigenen BSK, Double-Hashing):
cat vp_hash.txt | ./sig_stub.rb > vp_sig.txt3. VP zusammensetzen:
cat credential.jws | ./build_vp.rb assemble $HOLDER_DID vp_sig.txt > presentation.jws4. VP prüfen – zwei Lagen: erst die äußere VP (ES256-DH, Public Key über die
Holder-DID), dann die eingebettete VC, die an verify_jws.rb (Standard-ES256,
Issuer-DID) delegiert wird:
cat presentation.jws | ./verify_vp.rb
# optional Erwartungswerte: EXPECT_AUD=... EXPECT_NONCE=...Beide Signaturen müssen gültig sein und der holder im VP-Payload muss zum
VP-Signierschlüssel (kid) passen. Hinweis: Die innere VC ist mit Standard-
ES256 voll JOSE-interoperabel; nur die äußere VP braucht einen
ES256-DH-fähigen Verifier.
Würde man nur den Inhalt allein signieren, könnte jemand den Header (z.B. den
kid, also den Schlüsselverweis) austauschen, ohne die Signatur zu brechen.
Weil sowohl bei VC als auch VP die komplette Signing Input (Header + Payload)
signiert wird, ist auch der Header geschützt.
Gearbeitet wird mit NIST-P-256-Schlüsselpaaren. Die VC wird mit dem
Hersteller-/Issuer-Key signiert (ISSUER_SK), die VP mit dem Chip-Key (BSK in
sig_stub.rb). Die Verifier bekommen den öffentlichen Schlüssel nicht
vorgegeben, sondern lösen ihn über die kid-DID im jeweiligen Header auf: das
DID-Dokument wird abgerufen und der Schlüssel aus der Verification Method
#key-doc (publicKeyJwk, P-256) gelesen. Unterstützt werden did:web
(Auflösung nach W3C-Regel) und did:oyd (über den oyd-Resolver, Basis via
OYD_RESOLVER änderbar).
Die genaue Algorithmus-Definition samt Test-Vektor steht in ES256-DH.md.