Создайте действительную подпись ES256 в Java - PullRequest
1 голос
/ 25 января 2020

Я пытаюсь интегрировать Apple Map Web Snapshot, которому требуется параметр запроса подписи в URL. Я смог успешно сгенерировать и проверить подпись ES256 в пакете JWA из NPM, но не из Java. Пожалуйста, помогите мне найти эквивалентную библиотеку для генерации правильной подписи. Я пробовал несколько библиотек JWA в Java.

// Required modules.
const { readFileSync } = require("fs");
const { sign } = require("jwa")("ES256");

/* Read your private key from the file system. (Never add your private key
 * in code or in source control. Always keep it secure.)
 */ 
const privateKey = readFileSync("[file_system_path]");
// Replace the team ID and key ID values with your actual values.
const teamId = "[team ID]";
const keyId = "[key ID]";

// Creates the signature string and returns the full Snapshot request URL including the signature.
function sign(params) {
    const snapshotPath = `/api/v1/snapshot?${params}`;
    const completePath = `${snapshotPath}&teamId=${teamId}&keyId=${keyId}`;
    const signature = sign(completePath, privateKey);
    // In this example, the jwa module returns the signature as a Base64 URL-encoded string.

    // Append the signature to the end of the request URL, and return.
    return `${completePath}&signature=${signature}`;
}

// Call the sign function with a simple map request.
sign("center=apple+park") 

// The return value expected is: "/api/v1/snapshot?center=apple+park&teamId=[team ID]&keyId=[key ID]&signature=[base64_url_encoded_signature]"

Apache CXF - эта библиотека генерирует аналогично модулю JWA в узле, но не прошла аутентификацию .

      String teamId = [Team Id];
       String keyId = [Key id];
       String privateKey = [private key path];

       String privateKeyContent = getKeyFileContent(privateKey);

       String API_VERSION_PATH = "/api/v1/snapshot?";

       String param = [QueryParam];

       //example -> param = "center=[city,country or lat,lang]&size=90x90&lang=en&radius=2";

       String params = param + "&teamId="+ teamId + "&keyId=" + keyId;

       String payload = API_VERSION_PATH + params;

       PrivateKey key = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(
               Base64.decodeBase64(privateKeyContent)));

       JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
       compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
       //compactProducer.getJwsHeaders().setKeyId(keyId);


       compactProducer.signWith(key);

       String signed = compactProducer.getEncodedSignature();

       String encodedSignature = new String(Base64.encodeBase64URLSafe(compactProducer.getEncodedSignature().getBytes()));

       System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);

JJWT - Эта библиотека генерирует большую подпись, а затем подпись, сгенерированную в модуле узла.

String signed = new String(Base64.encodeBase64URLSafe(Jwts.builder().setPayload(payload)
                .signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, key).compact().getBytes()));

        System.out.println(SNAPSHOT_API_PATH + payload + "&signature=" + signed);

образец выходной подписи

compactProducer.getEncodedSignature() signed --> qQ5G9_lwGJ9w158FVSmtPx_iH43xlg2_gx9BlHEJbER73xpAeIHtDRnT8wnveH_UEPxNe7Zgv4csJ48Oiq-ZIQ

Base64.encodeBase64URLSafe(signature) --> cVE1RzlfbHdHSjl3MTU4RlZTbXRQeF9pSDQzeGxnMl9neDlCbEhFSmJFUjczeHBBZUlIdERSblQ4d252ZUhfVUVQeE5lN1pndjRjc0o0OE9pcS1aSVE

JJWT signed -> ZXlKaGJHY2lPaUpGVXpJMU5pSjkuTDJGd2FTOTJNUzl6Ym1Gd2MyaHZkRDlqWlc1MFpYSTlRM1Z3WlhKMGFXNXZMRlZUUVNaMFpXRnRTV1E5V0ZaWU5GWlhSbEZUTXlaclpYbEpaRDFWUVRWTlNGWlhWMWhMLlExUEtoeGwzSjFoVWVUWGtmeXRLckliYm5zeDdZem5lZVpxTVc4WkJOVU9uLVlYeFhyTExVU05ZVTZCSG5Xc3FheFd3YVB5dlF0Yml4TVBSZGdjamJ3

1 Ответ

3 голосов
/ 26 января 2020

Сигнатура в коде NodeJS генерируется методом jwa('ES256')#sign, который имеет следующие функциональные возможности:

  1. ES256: ECDSA с использованием кривой P-256 и SHA-256 га sh алгоритм [1] .
  2. Подпись будет представлять собой пару (r, s), где r и s - это 256-разрядные целые числа без знака [2] .
  3. Сигнатура в кодировке base64url [3] .

Ad 1: соответствующая реализация для ES256 возможна в Java с использованием встроенных средств (Поставщик SunE C, Java 1,7 или выше), [4] :

Signature ecdsa = Signature.getInstance("SHA256withECDSA");
ecdsa.initSign(privateKey);     
String payload = "The quick brown fox jumps over the lazy dog";
ecdsa.update(payload.getBytes(StandardCharsets.UTF_8));
byte[] signatureDER = ecdsa.sign();

Здесь privateKey - это закрытый ключ типа java.security.PrivateKey, аналогичный key в коде CXF.

Объявление 2. Код Java возвращает подпись в формате ASN.1 DER и поэтому должен быть преобразован в формат (r, s) [5] . Может быть реализован пользовательский метод или метод из вспомогательной библиотеки, например, метод com.nimbusds.jose.crypto.impl.ECDSA.transcodeSignatureToConcat из библиотеки Nimbus JOSE + JWT [6] [7] [8] :

byte[] signature = transcodeSignatureToConcat(signatureDER, 64);

Объявление 3: кодирование Base64url возможно в Java со встроенными средствами [9] :

String signatureBase64url = Base64.getUrlEncoder().withoutPadding().encodeToString(signature);

Поскольку каждый раз генерируется другая подпись, прямое сравнение подписей, сгенерированных в обоих кодах, невозможно. Однако совместимость с библиотекой jwa- npm можно проверить, проверив подпись, сгенерированную в коде Java с библиотекой jwa- npm:

const jwa = require("jwa");
const ecdsa = jwa('ES256');

var message = "The quick brown fox jumps over the lazy dog";
var verify = ecdsa.verify(message, signatureBase64url, publicKey);

Здесь signatureBase64url - это подпись, сгенерированная с кодом Java. publicKey - это соответствующий ключ publi c в формате PEM X.509 (-----BEGIN PUBLIC KEY-----...) [10] .


Функциональность метода jwa('ES256')#sign отличается от кода отправленного кода JJWT или Apache CXF: последние два генерируют JWT [11] . Заголовок - это кодировка base64url {"alg": "ES256"}. Соответственно, подпись такова, что для кодированного заголовка Base64url и полезной нагрузки, кодированной Base64url, оба разделены точкой:

String payload = "The quick brown fox jumps over the lazy dog";

//JJWT
String jwtJJWT = Jwts.builder().setPayload(payload).signWith(io.jsonwebtoken.SignatureAlgorithm.ES256, privateKey).compact();

//CXF
JwsCompactProducer compactProducer = new JwsCompactProducer(payload);
compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES256);
String jwtCXF = compactProducer.signWith(privateKey);
String signatureCXF = compactProducer.getEncodedSignature(); // signature, 3. portion of JWT

Пример для JWT, сгенерированного следующим образом:

eyJhbGciOiJFUzI1NiJ9.VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw.rcrzqr3ovu7SH9ci-S6deLn6BuiQkNv9CmeOnUPwva30pfK9BOX0YOgjZP5T08wjxCBTTHV3aex0M76toL8qpw
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...