ECDH + PLAY encryption/decryption 使用 jose4j
ECDH + JWE encryption/decryption using jose4j
我正在尝试在 Android (Java) 中实现 ECDH encryption/decryption 和 JWE。
我发现 jose4j and 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(最后一个例子,标题为"Producing and consuming a nested (signed and encrypted) JWT")。
但是这个例子并不是我所需要的。当我需要加密文本时,它会创建一个令牌。
从上面规范中的 "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_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 库的代码(到目前为止,使用@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 的代码更长,似乎更易于配置,但我无法构建足够有价值的东西来 post 在这里。
我主要缺少的部分是如何从上面的规范生成 CEK。
谢谢。
编辑
在上面添加了 jose4j 代码并添加了输出...
下面是一些使用 jose4j 的示例代码,我认为它可以满足您的需求。您指向的示例类似于 JWE 的明文是 JWS/JWT 但它可以是任意内容。 CEK generation/derivation 的细节由底层 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);
我正在尝试在 Android (Java) 中实现 ECDH encryption/decryption 和 JWE。
我发现 jose4j and 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(最后一个例子,标题为"Producing and consuming a nested (signed and encrypted) JWT")。
但是这个例子并不是我所需要的。当我需要加密文本时,它会创建一个令牌。
从上面规范中的 "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_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 库的代码(到目前为止,使用@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 的代码更长,似乎更易于配置,但我无法构建足够有价值的东西来 post 在这里。
我主要缺少的部分是如何从上面的规范生成 CEK。
谢谢。
编辑
在上面添加了 jose4j 代码并添加了输出...
下面是一些使用 jose4j 的示例代码,我认为它可以满足您的需求。您指向的示例类似于 JWE 的明文是 JWS/JWT 但它可以是任意内容。 CEK generation/derivation 的细节由底层 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);