我试着
- 生成签名/验证密钥 (RSA)
- 在 Java Web 应用程序上签署一个值(使用这些键)(让我们调用服务器端)
RSASSA-PKCS1-v1_5
为了让 Web 客户端验证 - 导入为+的公钥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();
}
将它们发送到 WebCrypto API 的 Web 应用程序,以作为客户端/浏览器进行验证(客户端知道第一步中生成的 publicKey)。
// 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
它以防止签名值受到污染(也许这也是一个问题)。
编辑:作为参考,它原来是另一个不相关的问题 - 我在 JavaScript 中两次转换来自 base64 的预期数据而没有注意到它;自然验证失败。