Подписанные Verifiable Credentials

VC принимается подписанной издателем и проверяется по его DID: подпись — compact JWS (VC, защищённый JOSE, Content-Type: application/vc+jwt), ключ — из assertionMethod DID издателя. Сервис хранит JWS вместе с записью и перепроверяет его в verify.

Режимы

Переменная VC_PROOF:

Значение

Поведение

off, optional (по умолчанию)

принимаются и подписанные VC, и JSON-запросы без подписи

required

JSON-запрос без подписи — 401, кроме издателей, за которых подписывает сам сервис

С IDENTITY_SYSTEM_KEY сервис сам подписывает VC, которые выпускает identity-система (did:i:is:identity-system): системный DID публикует этот ключ в assertionMethod.

Формат

  • protected header: alg (EdDSA, ES256, ES384, ES512, RS256, PS256), kid — DID URL ключа издателя (did:example:org#key-1), typ: vc+jwt;

  • payload — сам VC (VC Data Model 2.0): @context, id, type, issuer, validFrom, credentialSubject.id обязательны, остальные поля сохраняются в подписанном JWS.

Проверки при приёме:

  1. kid принадлежит DID из issuer;

  2. ключ входит в assertionMethod текущей версии DID издателя, подпись сходится;

  3. DID издателя и субъекта зарегистрированы и не деактивированы.

В записи сохраняется proof: формат, JWS, keyId и issuerVersionId — версия DID издателя, ключом которой проверена подпись.

Сценарий: организация выпускает VC о runtime ИИ-агента

  1. DID организации с ключом в assertionMethod (см. подписанные операции DID).

  2. VC vc.json:

{ "@context": ["https://www.w3.org/ns/credentials/v2"], "id": "urn:uuid:7b0e4b8e-1f6c-4d2a-9f3e-runtime-0001", "type": ["VerifiableCredential", "AgentRuntimeCredential"], "issuer": "did:example:org", "validFrom": "2026-10-15T00:00:00Z", "credentialSubject": { "id": "did:ru:agent:z7iAK46JKsNfPgWkUErXXhtWXH7827MKHqRWeZnmA19C1", "model": "qwen-open-weights", "weightsDigest": "sha256:...", "seed": 42 } }
  1. Подпись и приём:

JWT=$(bin/didsign sign-vc -seed $ORG_SEED -kid did:example:org#key-1 -body vc.json) curl -X POST $V/credentials -H 'Content-Type: application/vc+jwt' --data-binary "$JWT" # 201; тот же VC, подписанный чужим ключом с kid организации, — 422 invalid credential proof
  1. Проверка:

curl -X POST "$V/credentials/urn%3Auuid%3A7b0e4b8e-1f6c-4d2a-9f3e-runtime-0001/verify"
result: registry_valid, cryptographicallyVerified: true checks.proof: valid, checks.issuerKey: current

Отзыв

Отзыв подписывает издатель: заголовок Credential-Operation-Proof — compact JWS (typ: vc-operation+jws, claims {"op":"revoke","credentialId":"…","iat":…}) ключом из assertionMethod или capabilityInvocation DID издателя. В режиме required отзыв без подписи — 401; подпись не издателя или не сходится — 422, VC остаётся действующим.

P=$(bin/didsign sign-revoke -seed $ORG_SEED -kid did:example:org#key-1 -id urn:uuid:7b0e4b8e-1f6c-4d2a-9f3e-runtime-0001) curl -X POST "$V/credentials/urn%3Auuid%3A7b0e4b8e-1f6c-4d2a-9f3e-runtime-0001/revoke" -H "Credential-Operation-Proof: $P"

Что проверяет verify

checks.proof

Значение

valid

подпись сходится с ключом той версии DID издателя, что приняла VC, и сохранённый VC совпадает с подписанным

invalid

подпись не сходится или VC изменён в базе после приёма; result: invalid

missing

VC принят без подписи; в режиме required — result: invalid

checks.issuerKey

Значение

current

издатель по-прежнему публикует этот ключ

rotated

ключ из DID издателя убран после выпуска; подпись остаётся действительной — она проверяется по версии DID на момент приёма. Если ключ скомпрометирован, VC отзывают

issuer_deactivated

DID издателя деактивирован; checks.issuerDid: deactivated, result: invalid

Цепочка сертификатов (x5c в ключе издателя) и подписи ГОСТ проверяются PKI-сервисом и в этот сценарий не входят.

Журнал изменений

Дата

Перечень изменений

Автор

2026-11-05

Перенесён из архива DID-VC-итоги-2026-11-03 без изменения содержания.

Дис