我正在尝试在 Android (Java) 中实现 ECDH 加密/解密以及 JWE。
我找到了jose4j和 Nimbus JOSE图书馆的目标是做我需要的一切,但似乎比我想象的更具挑战性。
如果有人熟悉的话,那就是 3D Secure 2.0...
在下面的规范中:
- SDK = 本地端
- DS = 目录服务器(另一端)
接下来是规范:
- Given: P(DS) - an EC public key (provided in PEM format, can be transformed to PublicKey or to JWK)
- Generate a fresh ephemeral key pair (Q(SDK), d(SDK))
- Conduct a Diffie-Hellman key exchange process according to JWA (RFC7518) in Direct Key Agreement mode using curve P-256, d(SDK) and P(DS) to produce a CEK. The parameter values supported in this version of the specification are:
- "alg":ECDH-ES
- "apv":DirectoryServerID
- "epk":P(DS),inJSONWebKey(JWK)format {"kty":"EC", "crv":"P-256"}
- All other parameters: not present
- CEK:"kty":oct-256bits
- Generate 128-bit random data as IV
- Encrypt the JSON object according to JWE (RFC7516) using the CEK and JWE Compact Serialization. The parameter values supported in this version of the specification are:
- "alg":dir
- "epk":Q(SDK) as {"kty": "EC", "crv": "P-256"}
- "enc":either"A128CBC-HS256"or"A128GCM"
- All other parameters: not present
- If the algorithm is A128CBC-HS256 use the full CEK or if the algorithm is A128GCM use the leftmost 128 bits of the CEK.
- Delete the ephemeral key pair (Q(SDK),d(SDK))
- Makes the resulting JWE available to the 3DS Server as SDK Encrypted Data
如果有人已经实现了这个确切的规范并且可以共享代码,那就太棒了!
jose4j 的示例中有一个使用 ECDH 创建 JWT 的示例:
https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples (最后一个示例,标题为“生成和使用嵌套(签名和加密)JWT”)。
但这个例子并不完全是我所需要的。当我需要加密文本时它会创建一个 token 。
从上面规范中的“CEK:”kty”:oct-256bits”开始,我不明白该怎么做。
这是我使用 Nimbus 库的代码(到目前为止):
public String nimbus_encrypt(String plainJson, ECPublicKey otherPublicKey, String directoryServerId) throws JOSEException {
JWEHeader jweHeader = new JWEHeader(
JWEAlgorithm.ECDH_ES,
EncryptionMethod.A128CBC_HS256,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
Base64URL.encode(directoryServerId),
null,
0,
null,
null,
null,
null);
JWEObject jwe = new JWEObject(jweHeader, new Payload(plainJson));
jwe.encrypt(new ECDHEncrypter(otherPublicKey));
String serializedJwe = jwe.serialize();
Log.d("[ENCRYPTION]", "nimbus_encrypt: jwe = " + jwe.getHeader());
Log.d("[ENCRYPTION]", "nimbus_encrypt: serializedJwe = " + serializedJwe);
return serializedJwe;
}
这是 nimbus 输出:
nimbus_encrypt: jwe = {"epk":{"kty":"EC","crv":"P-256","x":"AS0GRfAOWIDONXxaPR_4IuNHcDIUJPHbACjG5L7x-nQ","y":"xonFn1vRASKUTdCkFTwsl16LRmSe-bAF8EO4-mh1NYw"},"apv":"RjAwMDAwMDAwMQ","enc":"A128CBC-HS256","alg":"ECDH-ES"}
nimbus_encrypt: serializedJwe = eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJBUzBHUmZBT1dJRE9OWHhhUFJfNEl1TkhjRElVSlBIYkFDakc1TDd4LW5RIiwieSI6InhvbkZuMXZSQVNLVVRkQ2tGVHdzbDE2TFJtU2UtYkFGOEVPNC1taDFOWXcifSwiYXB2IjoiUmpBd01EQXdNREF3TVEiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiRUNESC1FUyJ9..Pi48b7uj3UilvVXKewFacg.0sx9OkHxxtZvkVm-IENRFw.bu5GvOAwcZxdxaDKWIBqwA
这是我使用 jose4j lib 的代码(到目前为止,使用@Brian-Campbell 的答案):
public String jose4j_encrypt(String plainJson, PublicKey otherPublicKey, String directoryServerId) throws JoseException {
JsonWebEncryption jwe = new JsonWebEncryption();
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.ECDH_ES);
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
jwe.setHeader(HeaderParameterNames.AGREEMENT_PARTY_V_INFO, Base64Url.encodeUtf8ByteRepresentation(directoryServerId));
jwe.setKey(otherPublicKey);
jwe.setPayload(plainJson);
String serializedJwe = jwe.getCompactSerialization();
Log.d("[ENCRYPTION]", "jose4j_encrypt: jwe = " + jwe);
Log.d("[ENCRYPTION]", "jose4j_encrypt: serializedJwe = " + serializedJwe);
return serializedJwe;
}
这是 jose4j 输出:
jose4j_encrypt: jwe = JsonWebEncryption{"alg":"ECDH-ES","enc":"A128CBC-HS256","apv":"RjAwMDAwMDAwMQ","epk":{"kty":"EC","x":"prvyhexJXDWvPQmPA1xBjY8mkHEbrEiJ4Dr-7_5YfdQ","y":"fPjw8UdfzgkVTppPSN5o_wprItKLwecoia9yrWi38yo","crv":"P-256"}}
jose4j_encrypt: serializedJwe = eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImFwdiI6IlJqQXdNREF3TURBd01RIiwiZXBrIjp7Imt0eSI6IkVDIiwieCI6InBydnloZXhKWERXdlBRbVBBMXhCalk4bWtIRWJyRWlKNERyLTdfNVlmZFEiLCJ5IjoiZlBqdzhVZGZ6Z2tWVHBwUFNONW9fd3BySXRLTHdlY29pYTl5cldpMzh5byIsImNydiI6IlAtMjU2In19..gxWYwFQSOqLk5HAgs7acdA.mUIHBiWpWSlQaEOJ_EZGYA.eiTe-88fw-Jfuhji_W0rtg
可以看出,最终结果中的“alg” header 是“ECDH-ES”,而不是所需的“dir”。
如果我要实现通信的双方,那就足够了,但是根据这个规范,似乎这里缺少许多配置......
使用 jose4j 的代码更长,并且似乎更可配置,但我无法构建足够有值(value)的东西来发布在这里。
对我来说,主要缺少的部分是如何根据上面的规范生成 CEK。
谢谢。
编辑
添加了上面的 jose4j 代码并添加了输出...
最佳答案
下面是一些使用 jose4j 的示例代码,我认为它可以满足您的需求。您指出的示例与 JWE 的明文是 JWS/JWT 类似,但它可以是任意内容。 CEK 生成/派生的详细信息由底层 JWE 功能负责。请注意,这仅加密内容,不提供完整性保护或发件人身份验证。
String encodedCert = "MIIBRjCB7KADAgECAgYBaqxRCjswDAYIKoZIzj0EAwIFADApMQswCQYDVQQGEwJDQTEMMAoGA1UE\n" +
"ChMDbWVoMQwwCgYDVQQDEwNtZWgwHhcNMTkwNTEyMTM1MjMzWhcNMjAwNTExMTM1MjMzWjApMQsw\n" +
"CQYDVQQGEwJDQTEMMAoGA1UEChMDbWVoMQwwCgYDVQQDEwNtZWgwWTATBgcqhkjOPQIBBggqhkjO\n" +
"PQMBBwNCAAQH83AhYHCehKj7M5+UTNshwLFqqqJWGrJPNj9Kr7xvxtcZnyjq+AKLGMLfdk/G7yb8\n" +
"4vIh0cJwtVs70WgIXT8xMAwGCCqGSM49BAMCBQADRwAwRAIgO0PJRzan2msHpcvcqhybzeualDea\n" +
"/X2QGAWCYT+sNiwCIDMrfhrzUQ6uIX4vnB8AYqb85Ssl7Qcl9nYtjHb08NR8";
X509Util x509Util = new X509Util();
X509Certificate x509Certificate = x509Util.fromBase64Der(encodedCert);
// the JWE object
JsonWebEncryption jwe = new JsonWebEncryption();
// The output of the ECDH-ES key agreement will be used as the content encryption key
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.ECDH_ES);
// The content encryption key is used to encrypt the payload
// with a composite AES-CBC / HMAC SHA2 encryption algorithm
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
// don't think you really need this but you had ""apv":DirectoryServerID" in the question so...
jwe.setHeader(HeaderParameterNames.AGREEMENT_PARTY_V_INFO, Base64Url.encodeUtf8ByteRepresentation("<<DirectoryServerID>>"));
// We encrypt to the receiver using their public key
jwe.setKey(x509Certificate.getPublicKey());
// and maybe put x5t to help the receiver know which key to use in decryption
jwe.setX509CertSha1ThumbprintHeaderValue(x509Certificate);
// What is going to be encrypted
jwe.setPayload("Your text here. It can be JSON or whatever.");
// Produce the JWE compact serialization, which is a string consisting of five dot ('.') separated
// base64url-encoded parts in the form Header..IV.Ciphertext.AuthenticationTag
String serializedJwe = jwe.getCompactSerialization();
System.out.println(serializedJwe);
关于java - 使用 jose4j ECDH + JWE 加密/解密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56096907/