EUDI SD-JWT

重要提示! 在继续之前,请务必阅读 EUDI Wallet 参考实现项目说明

License

目录

概述

这是一个提供 DSL(领域特定语言)的库,用于定义如何有选择地披露一组声明。

该库使用 Swift 实现了 SD-JWT 草案 12

支持的用例

颁发

要颁发 SD-JWT,Issuer 应该具备:

在下面的示例中,颁发者决定按如下方式颁发 SD-JWT

let issuersKeyPair: KeyPair!

let signedSDJWT = try SDJWTIssuer.issue(issuersPrivateKey: issuersKeyPair.private,
                                        decoys: 0, // Can be omitted
                                        header: .init(algorithm: .ES256)) {
  ConstantClaims.sub(subject: "6c5c0a49-b589-431d-bae7-219122a9ec2c")
  ConstantClaims.iss(domain: "https://example.com/issuer")
  ConstantClaims.iat(time: 1516239022)
  ConstantClaims.exp(time: 1516239022)
  ObjectClaim("address") {
    FlatDisclosedClaim("street_address", "Schulstr. 12")
    FlatDisclosedClaim("locality", "Schulpforta")
    FlatDisclosedClaim("region", "Sachsen-Anhalt")
    FlatDisclosedClaim("country", "DE")
  }
}

持有者验证

在这种情况下,SD-JWT 预计为序列化格式。

持有者必须知道

颁发者的公钥以及颁发者用于签署 SD-JWT 的算法

let unverifiedSdJwtString = "..."
let issuersKeyPair: KeyPair!

SDJWTVerifier(parser: CompactParser(serialisedString: unverifiedSdJwtString))
  .verifyIssuance { jws in
    SignatureVerifier(signedJWT: jws, publicKey: issuersKeyPair.public)
}

持有者展示

在这种情况下,由 Issuer 颁发的 SD-JWT 的 Holder,想要为 Verifier 创建一个展示。Holder 应该知道在展示中包含哪些有选择性地披露的声明。 要包含在展示中的选择性披露声明使用声明路径(根据 SD-JWT VC 草案 6)或 JSON 指针表示。

您可以在这里找到全面的示例

展示验证

以简单(非信封)格式

在这种情况下,SD-JWT 预计为组合展示格式。验证者应该知道颁发者的公钥以及颁发者用于签署 SD-JWT 的算法。 此外,如果验证包括密钥绑定,则验证者还必须知道持有者的公钥是如何包含在 SD-JWT 中的,以及持有者使用哪种算法来签署密钥绑定 JWT

// Issue a SDJWT to passed to a holder from an issuer
// Including Holders Public key
let issuerSignedSDJWT = try SDJWTIssuer
    .issue(issuersPrivateKey: issuersKeyPair.private,
           header: .init(algorithm: .ES256)) {
  // Claims disclosed or plain
  ...
  // add holders public key in the payload
  ObjectClaim("cnf") {
    ObjectClaim("jwk") {
      PlainClaim("kty", "EC")
      PlainClaim("x", "EOid5YEjFXpCzaqyEqckcA5TBGxWEVYCiKz05qO5r_c")
      PlainClaim("y", "7TTgK6fW5oxaN8m22f_HPVJ9Ny3KBKIvLcBIpUpk-7A")
      PlainClaim("crv", "P-256")
    }
  }
}

// Issue a SDJWT for presentation to a verifier
// Expect a verifier Challenge in json format to sign
// And prove identity
// Chose the subset of disclosures to present if needed
let holderSDJWTRepresentation = try SDJWTIssuer
  .presentation(holdersPrivateKey: holdersKeyPair.private,
                signedSDJWT: issuerSignedSDJWT,
                disclosuresToPresent: issuerSignedSDJWT.disclosures.filter({ _ in true }),
                keyBindingJWT: KBJWT(header: .init(algorithm: .ES256),
                                     kbJwtPayload: VerifiersChallenge.json)

SDJWTVerifier(sdJwt: holderSDJWTRepresentation)
.verifyPresentation { jws in
    try SignatureVerifier(signedJWT: jws, publicKey: issuersKeyPair.public)
  } keyBindingVerifier: { jws, holdersPublicKey in
    try KeyBindingVerifier(challenge: jws, extractedKey: holdersPublicKey)
}                                                           

以信封格式

在这种情况下,SD-JWT 预计为信封格式。验证者应该知道

let sdjwtOnPayload = "...."

try SDJWTVerifier(parser: CompactParser(serialisedString: sdjwtOnPayload))
  .verifyEnvelope(envelope: envelopedJws) { jws in
    // to verify the enveloped sdjwt
    try SignatureVerifier(signedJWT: jws, publicKey: issuersKeyPair.public)
  } holdersSignatureVerifier: {
    // to verify the container jwt
    try SignatureVerifier(signedJWT: envelopedJws, publicKey: holdersKeyPair.public)
  } claimVerifier: { audClaim, iat in
    ClaimsVerifier(iat: iat,
                   iatValidWindow: .init(startTime: Date(),
                                         endTime: Date()),
                   audClaim: audClaim,
                   expectedAud: "clientId")
  }

重新创建原始声明

给定一个复杂的结构,如 示例 3:复杂结构化 SD-JWT 和声明的子集,我们可以重新创建 SD-JWT 的初始 JSON。

["2GLC42sKQveCfGfryNRN9w", "time", "2012-04-23T18:25Z"]
["Pc33JM2LchcU_lHggv_ufQ", {"_sd": ["9wpjVPWuD7PK0nsQDL8B06lmdgV3LVybhHydQpTNyLI", "G5EnhOAOoU9X_6QMNvzFXjpEA_Rc-AEtm1bG_wcaKIk", "IhwFrWUB63RcZq9yvgZ0XPc7Gowh3O2kqXeBIswg1B4", "WpxQ4HSoEtcTmCCKOeDslB_emucYLz2oO8oHNr1bEVQ"]}]
["eI8ZWm9QnKPpNPeNenHdhQ", "method", "pipp"]
["G02NSrQfjFXQ7Io09syajA", "given_name", "Max"]
["lklxF5jMYlGTPUovMNIvCA", "family_name", "M\u00fcller"]
["y1sVU5wdfJahVdgwPgS7RQ", "address", {"locality": "Maxstadt", "postal_code": "12344", "country": "DE", "street_address": "Weidenstra\u00dfe 22"}]
let example3SDJWTSerialisedFormat = "..."
let sdjwt = CompactParser(serialisedString: example3SDJWTSerialisedFormat).getSignedSdJwt()

// array of digests of disclosures found on payload for collision 
sdjwt.recreateClaims().digestsFoundOnPayload checking
// the recreated JSON
sdjwt.recreateClaims().recreatedClaims 

重新创建的 JSON 输出

{
  "iat" : 1683000000,
  "verified_claims" : {
    "claims" : {
      "given_name" : "Max",
      "family_name" : "Müller",
      "address" : {
        "country" : "DE",
        "locality" : "Maxstadt",
        "street_address" : "Weidenstraße 22",
        "postal_code" : "12344"
      }
    },
    "verification" : {
      "trust_framework" : "de_aml",
      "time" : "2012-04-23T18:25Z",
      "evidence" : [
        {
          "method" : "pipp"
        }
      ]
    }
  },
  "iss" : "https://example.com/issuer",
  "exp" : 1883000000
}

DSL 示例

所有示例都假设我们具有以下声明集

{
  "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c",
  "address": {
    "street_address": "Schulstr. 12",
    "locality": "Schulpforta",
    "region": "Sachsen-Anhalt",
    "country": "DE"
  }
}

SD-JWT VC 支持

该库支持验证 基于 SD-JWT 的可验证凭据。 更具体地说,SDJWTVerifier 提供了颁发者签名的 JWT 验证密钥验证支持。

请查看 PresentationTest,获取有关创建持有者展示的代码示例。

请查看 VcVerifierTest,获取有关验证颁发 SD-JWT VC 和展示 SD-JWT VC(包括密钥绑定 JWT 的验证)的代码示例。

如何贡献

我们欢迎对该项目做出贡献。 为确保每个参与者都能顺利进行,请遵循 CONTRIBUTING.md 中找到的指南。

许可

第三方组件许可

许可详细信息

版权所有 (c) 2023 欧盟委员会

根据 Apache 许可证 2.0 版(“许可证”)获得许可; 除非遵守许可证,否则您不得使用此文件。 您可以在以下位置获取许可证副本:

https://apache.ac.cn/licenses/LICENSE-2.0

除非适用法律要求或书面同意,否则按“原样”分发的软件,不附带任何形式的明示或暗示的担保或条件。 有关管理权限和限制的特定语言,请参阅许可证。