java - 使用 jose4j ECDH + JWE 加密/解密

标签 java 3d-secure jwe jose4j ecdh

我正在尝试在 Android (Java) 中实现 ECDH 加密/解密以及 JWE。
我找到了jose4jNimbus 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/

相关文章:

java - 在 javascript 或 jquery 中从数据库检索图像的最佳方法

java - 如何初始化 Optional<List<T>>?

python - 在 Python 中解码 3D-Secure PaRes

java - 解密 JWE 时出错

java - JCS 缓存 - 删除功能不删除特定元素

java - 如何使 * 运算符与 java 中的泛型变量一起使用? - 如果我知道变量是数字类型

PayPal 高级卡支付 - 如何测试 3D Secure?

angularjs - Angular $http.post 返回 404 错误

jwt - 为什么客户端需要解密JWE?

java - 如何在Android上为JWE从字符串生成公钥?