Я пытаюсь
- генерировать ключи подписи/проверки (RSA)
- подписать значение (используя эти ключи) в веб-приложении Java (позволяет вызывать на стороне сервера)
- для проверки веб-клиентом — открытый ключ импортируется как
RSASSA-PKCS1-v1_5
+SHA-256
(в браузере с использованием WebCrypto API / на стороне клиента)
У меня возникают проблемы с проверкой подписанного значения (подписанного на стороне сервера Java), хотя открытый ключ подписи/проверки успешно импортирован как JWK на стороне клиента.
Мне интересно, есть ли какие-либо проблемы совместимости алгоритмов на любом из шагов (OpenSSL, Java или Javascript), с которыми я могу столкнуться.
Команды OpenSSL, используемые для генерации ключей
openssl genrsa -out privatekey.pem 2048
openssl rsa -in privatekey.pem -pubout > publickey.pub
openssl pkcs8 -topk8 -inform PEM -outform DER -in privatekey.pem -out privatekey-pkcs8.pem
Импорт ключей с помощью Java (на стороне сервера)
public static KeyPair generateSignKeyPair() throws ... {
byte[] privBytes = b64ToByteArray(PRIVATE_KEY_PEM_VALUE);
byte[] pubBytes = b64ToByteArray(PUBLIC_KEY_PEM_VALUE);
// private key
KeySpec keySpec = new PKCS8EncodedKeySpec(privBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// public key (javaPubSignKey)
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(pubBytes);
PublicKey publicKey = keyFactory.generatePublic(X509publicKey);
return new KeyPair(publicKey, privateKey);
}
Подписание значения с помощью Java (на стороне сервера)
public static byte[] generateSignature(PrivateKey signPrivateKey, byte[] data) throws ... {
Signature dsa = Signature.getInstance("SHA256withRSA");
dsa.initSign(signPrivateKey);
dsa.update(data);
return dsa.sign();
}
Отправьте их в веб-приложение для API WebCrypto для проверки в качестве клиента/браузера (клиент знает о публичном ключе, сгенерированном на первом этапе).
// Import public sign/verify key (javaPubSignVerifyKey)
var signatureAlgorithm = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {
name: 'SHA-256'
}
};
// JWK format (1)
crypto.subtle.importKey(
'jwk', javaPubSignVerifyKey, signatureAlgorithm, false, ['verify']
).then(success, error);
function success(key) {
signatureVerifyPublicKey = key;
}
Примечание (1): на стороне Java я использую com.nimbusds.jose.jwk.JWK
для экспорта publicKey в формат JWK.
Ключ подписи успешно импортирован WebCrypto. Но когда дело доходит до проверки, она терпит неудачу (логическое значение проверки равно false
).
crypto.subtle.verify(
signatureAlgorithm,
signatureVerifyPublicKey,
signature, // bytes in Int8Array format (2)
data // bytes in Int8Array format
).then(
function (valid) {
// valid === false
}
)
Примечание (2): также обратите внимание, что каждый пример, который я нашел в WebCrypto, использовал Uint8Array
для представления байтовых массивов, но поскольку Java генерирует подписанные массивы байтов, мне нужно использовать Int8Array
, чтобы значения подписи не были загрязнены ( может и в этом проблема)
EDIT: для справки, это оказалось еще одной несвязанной проблемой - я дважды преобразовывал ожидаемые данные из base64 в Javascript, не замечая этого; естественно проверка не удалась.
byte[]
хранит значения от 0 до 255. Я думаю, вам нужно использоватьUint8Array
иArrayBuffer
. Не могли бы вы попробовать использовать функциюstringToArrayBuffer
, описанную здесь stackoverflow.com/questions/36018233/ для представленияdata
иsignature
? - person pedrofb   schedule 29.11.2016pki.js
+asn1.js
для извлечения из него публичного ключа (сертификата) в Javascript для проверки WebCrypto (пример здесь) - person nuno   schedule 29.11.2016spki
(двоичный ключ RSA, преобразованный в ArrayBuffer) - person pedrofb   schedule 29.11.2016spki
, сгенерированном с помощьюBase64.encode(pubKey.getEncoded())
в Java и импортированном с помощьюstr2ab(atob(spkiBase64))
в Javascript симптом тот же: проверка не удалась. - person nuno   schedule 29.11.2016