❗ 重要提示! 在继续之前,请务必阅读 EUDI Wallet 参考实现项目说明
这是一个提供 DSL(领域特定语言)的库,用于定义如何有选择地披露一组声明。
该库使用 Swift 实现了 SD-JWT 草案 12。
要颁发 SD-JWT,Issuer
应该具备:
在下面的示例中,颁发者决定按如下方式颁发 SD-JWT
sub
,iss
, iat
, exp
)address
的声明可以选择性地披露。 这允许单独披露 address
的每个子声明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
}
所有示例都假设我们具有以下声明集
{
"sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c",
"address": {
"street_address": "Schulstr. 12",
"locality": "Schulpforta",
"region": "Sachsen-Anhalt",
"country": "DE"
}
}
该库支持验证 基于 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
除非适用法律要求或书面同意,否则按“原样”分发的软件,不附带任何形式的明示或暗示的担保或条件。 有关管理权限和限制的特定语言,请参阅许可证。