Почему JsonWebSignature с алгоритмом шифрования ECDH каждый раз дает другую подпись?

Я хочу отправить запрос в Google FCM, чтобы отправить push-запрос в браузер, чтобы показать уведомление.

Основная цель — подписать полезную нагрузку JWT закрытым ключом, используя алгоритм ECDH с SHA256, чтобы получить токен JWT.

Я пытаюсь подписать токен с помощью алгоритма ECDH, но каждый раз получаю неверную подпись для одной и той же полезной нагрузки.

Это снимок моего кода

заранее спасибо

    JwtClaims claims = new JwtClaims();
    claims.setAudience("https://fcm.googleapis.com");
    claims.setExpirationTime(NumericDate.fromSeconds(1560388318));
    claims.setSubject("mailto:[email protected]");

    JsonWebSignature jws = new JsonWebSignature();
    jws.setHeader("typ", "JWT");
    jws.setHeader("alg", "ES256");
    jws.setPayload(claims.toJson());
    try {
        Key key = loadPrivateKey("-kmhPYsH6JKiFjG8C1cS9vx4bCz594yofAwTLa_SOEE");
        jws.setKey(key);
    } catch (NoSuchProviderException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    }
    jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256);

    try {
        System.out.println(jws.getCompactSerialization());
    } catch (JoseException e) {
        e.printStackTrace();
    }

.

 public static PrivateKey loadPrivateKey(String encodedPrivateKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] decodedPrivateKey = base64Decode(encodedPrivateKey);

    // prime256v1 is NIST P-256
    ECParameterSpec params = ECNamedCurveTable.getParameterSpec("prime256v1");
    ECPrivateKeySpec prvkey = new ECPrivateKeySpec(new BigInteger(decodedPrivateKey), params);
    KeyFactory kf = KeyFactory.getInstance("ECDH");

    return kf.generatePrivate(prvkey);
   }

.

public static byte[] base64Decode(String base64Encoded) {
    if (base64Encoded.contains("+") || base64Encoded.contains("/")) {
        return BaseEncoding.base64().decode(base64Encoded);
    } else {
        return BaseEncoding.base64Url().decode(base64Encoded);
    }
}

первая попытка, которую я получил:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTU2MDM4ODMxOCwic3ViIjoibWFpbHRvOmFkbWluQGV4YW1wbGUuY29tIn0.MpGc0pKvXtDb94Ruq5lkQjqCqxFMkVAwzVervnH90RLArvGHUAZ_kO4VcecLhGfIXTCitBKb5M-EKsYR35IT0A

Второй раз я получил:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTU2MDM4ODMxOCwic3ViIjoibWFpbHRvOmFkbWluQGV4YW1wbGUuY29tIn0.qEW0ci1BnXXUUjkfsQkdReznAyIDEPtygxV3B58Sl8v_gTlh8O4HHGzRtxsqdvL5UIJV06e_UJHYPOUDK_MF9A

Вы вообще не используете ECDH, вы используете ECDSA, который является подписью, а не шифрованием. Это оба алгоритма эллиптических кривых, но это совершенно разные алгоритмы. Однако они используют одни и те же ключи (на самом деле пары ключей), поэтому ваша KeyFactory для ECDH сработала.

dave_thompson_085 13.06.2019 16:34
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
280
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

По замыслу цифровая криптографическая подпись должна удовлетворять только следующему условию

It should be verifiable by the corresponding public key.

Если вы посмотрите на шаг 3 Алгоритм генерации подписи, там написано Select a cryptographically secure random integer k from [1,n-1]. , где n — порядок кривой (пока вы можете игнорировать этот факт).

Затем вычислите (x1, y1) = k * G, где G — образующая точка эллиптической кривой. Тогда r = x1 mod n. Этот r является частью подписи. Таким образом, при изменении k изменится и r, а значит, изменится и подпись.

Таким образом, для каждого поколения подписи алгоритм выбирает другой параметр, а затем использует его для вычисления подписи. например:

>>> from ecc import curves
>>> curve = curves.P256()
>>> pkey =  0x00c3f7c39a9be2418cd89a732e40d648b09fa0af9e909a4fb6864910144b5cbcdf
>>> s1 = c.sign(b'Hello', pkey)
(37527198291707833181859423619289327687028014812888685671525882103189540525356,7717531609084222009133798505588038563850333231389727023073200992747312618427)
>>> s2 = c.sign(b'Hello', pkey)
(55880701658034823360120047989457771316451459626784083177171213563603884569397,88917360761747520665103257272757357544674490240888454865713640275762122369837)
>>> s1 == s2
False

Каждый раз подпись разная.

ссылка:

но будет ли открытый ключ проверять cipherText без случайного целого числа? Глупый вопрос, но можно ли обойтись без случайного числа? Заранее спасибо дружище

Ridae HAMDANI 13.06.2019 10:46

@Ridae: случайное k для ECDSA (или DSA) эффективно закодировано в части r подписи, которую использует верификатор. Если вы создаете подписи ECDSA (или DSA) с фиксированным или повторяющимся значением k, они будут проверяться, но любой противник, который увидит две такие подписи на разных данных, может вычислить ваш закрытый ключ, а затем подделать «ваши» подписи на любых данных, которые они хотят, что делает вашу подписи совершенно бесполезны. Это была проблема Биткойна, где некоторые ошибки приводили к дублированию подписей k, и люди теряли много своих денег. Вопросы несколько по этому вопросу см. в разделе crypto.SX security.SX bitcoin.SX .

dave_thompson_085 13.06.2019 16:40

Да, в этом вся идея.. вам не нужно знать случайное число.. вам просто нужно проверить, верна ли последовательность вычислений. вы можете обратиться к статье «ECDSA и небольшое доказательство», чтобы увидеть, как работает проверка. И, как говорили другие, вы не должны использовать предсказуемое случайное число, так как... наличие того же k может привести к утечке закрытого ключа... в этом документе, упомянутом в ссылках, также есть доказательство того, как он приведет к утечке закрытого ключа.

prateeknischal 13.06.2019 20:07
Ответ принят как подходящий

Алгоритм шифрования ECDH использует разные безопасные случайные числа для расчета подписи для каждого поколения подписи. Вы можете указать этот Secure Random, чтобы каждый раз получать одну и ту же подпись. Для вашего случая:

ProviderContext context = new ProviderContext();
context.setSecureRandom(new SecureRandom(SecureRandom.getSeed(0)));
jws.setProviderContext(context);

s/шифрование ECDH/подпись ECDSA/, и это приведет к тому, что ваш закрытый ключ будет взломан любым злоумышленником.

dave_thompson_085 13.06.2019 16:43

Другие вопросы по теме