ECDH + JWE шифрование / дешифрование с использованием jose4j - PullRequest
1 голос
/ 12 мая 2019

Я пытаюсь реализовать шифрование / дешифрование ECDH вместе с JWE в Android (Java).
Я нашел библиотеки jose4j и Nimbus JOSE , которые нацелены на то, чтобы делать все, что мне нужно, но оказалось, что это сложнее, чем я думал.

Если кто-то знаком, тогда это для 3D Secure 2.0 ...

В спецификации ниже:

  • SDK = местная сторона
  • DS = Сервер каталогов (сторона другая )

Следующая спецификация:

  • Дано: P (DS) - открытый ключ EC (предоставляется в формате PEM, может быть преобразован в PublicKey или в JWK)
  • Создание новой пары эфемерных ключей (Q (SDK), d (SDK))
  • Проведите процесс обмена ключами Диффи-Хеллмана в соответствии с JWA (RFC7518) в режиме прямого соглашения о ключах, используя кривые P-256, d (SDK) и P (DS) для получения CEK. Значения параметров, поддерживаемые в этой версии спецификации:
    • "ALG": ECDH-ES
    • "APV": DirectoryServerID
    • "epk": P (DS), формат inJSONWebKey (JWK) {"kty": "EC", "crv": "P-256"}
    • Все остальные параметры: отсутствуют
  • СЕК: "KTY": окт-256bits
  • Генерация 128-битных случайных данных как IV
  • Шифрование объекта JSON в соответствии с JWE (RFC7516) с использованием компактной сериализации CEK и JWE. Значения параметров, поддерживаемые в этой версии спецификации:
    • "ALG": каталог
    • "epk": Q (SDK) as {"kty": "EC", "crv": "P-256"}
    • "ENC": либо "A128CBC-HS256" или "A128GCM"
    • Все остальные параметры: отсутствуют
  • Если используется алгоритм A128CBC-HS256, используйте полный CEK или если алгоритм A128GCM, используйте крайние левые 128 бит CEK.
  • Удалить пару эфемерных ключей (Q (SDK), d (SDK))
  • Делает полученный JWE доступным для сервера 3DS как зашифрованные данные SDK

Если бы кто-то реализовал эту точную спецификацию и смог поделиться кодом, это было бы замечательно !!

В примерах jose4j приведен пример создания JWT с использованием ECDH:
https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples (последний пример, озаглавленный «Создание и использование вложенного (подписанного и зашифрованного) JWT»).
Но этот пример не совсем то, что мне нужно. Он создает токен, а мне нужно зашифровать текст.

Начиная с "CEK:" kty ": oct-256bits" в спецификации выше, я не понимаю, что делать.

Вот мой код (пока что) с использованием Nimbus lib:

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", "ywFN6-ФУНТ-1):" ywFN6 mh1NYw "}," APV ":" RjAwMDAwMDAwMQ», "ENC": "A128CBC-HS256", "ALG": "ECDH-ES"} * 1 086 *

nimbus_encrypt: serializedJwe = eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJBUzBHUmZBT1dJRE9OWHhhUFJfNEl1TkhjRElVSlBIYkFDakc1TDd4LW5RIiwieSI6InhvbkZuMXZSQVNLVVRkQ2tGVHdzbDE2TFJtU2UtYkFGOEVPNC1taDFOWXcifSwiYXB2IjoiUmpBd01EQXdNREF3TVEiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiRUNESC1FUyJ9..Pi48b7uj3UilvVXKewFacg.0sx9OkHxxtZvkVm-IENRFw.bu5GvOAwcZxdxaDKWIBqwA

Вот мой код (пока, используя ответ @ Brian-Campbell), используя jose4j lib:

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", "х": "prvyhexJXDWvPQmPA1xBjY8mkHEbrEiJ4Dr-7_5YfdQ", "у": "fPjw8UdfzgkVTppPSN5o_wprItKLwecoia9yrWi38yo", "CRV": "Р-256"}} * +1097 *

jose4j_encrypt: serializedJwe = eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImFwdiI6IlJqQXdNREF3TURBd01RIiwiZXBrIjp7Imt0eSI6IkVDIiwieCI6InBydnloZXhKWERXdlBRbVBBMXhCalk4bWtIRWJyRWlKNERyLTdfNVlmZFEiLCJ5IjoiZlBqdzhVZGZ6Z2tWVHBwUFNONW9fd3BySXRLTHdlY29pYTl5cldpMzh5byIsImNydiI6IlAtMjU2In19..gxWYwFQSOqLk5HAgs7acdA.mUIHBiWpWSlQaEOJ_EZGYA.eiTe-88fw-Jfuhji_W0rtg

Как видно, заголовок «alg» в конечном результате - «ECDH-ES», а не «dir», как требуется.

Если бы я реализовал обе стороны связи, то этого было бы достаточно, но с этой спецификацией кажется, что многие конфигурации здесь отсутствуют ...

Код, использующий jose4j, длиннее и кажется более настраиваемым, но я не смог создать что-то достаточно ценное для публикации здесь.

Основная недостающая часть для меня - это как генерировать CEK из приведенной выше спецификации.

Спасибо.

EDIT
Добавлен код jose4j выше и добавлены выходные данные ...

1 Ответ

2 голосов
/ 12 мая 2019

Ниже приведен пример кода с использованием 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);
...