Java签名/验证密钥和Javascript WebCrypto验证失败

时间:2016-11-29 10:19:37

标签: javascript java cryptography digital-signature webcryptoapi

我正在尝试

  1. 生成签名/验证密钥(RSA)
  2. 在Java Web应用程序上签名(使用这些键)(让我们调用服务器端)
  3. 以便网络客户端验证 - 公开密钥导入为RSASSA-PKCS1-v1_5 + SHA-256,(在浏览器中,使用WebCrypto API /客户端)
  4. 我在验证签名值(在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生成 signed 字节数组,我需要使用{{1这样签名值就不会受到污染(也许这也是一个问题)。

    编辑:供参考,原来是另一个不相关的问题 - 我在Javascript中两次转换预期的数据而没有注意到它;自然验证失败了。

1 个答案:

答案 0 :(得分:3)

请检查基于您的这个简单代码导入RSA公钥(spki)并验证签名。我已使用类似的Java代码生成密钥和签名

var publicKeyB64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVdZDEs6htb3oxWstz7q+e5IwIRcptMNJiyemuoNyyjtiOy+0tEodjgo7RVoyUcGU3MysEivqvKdswQZ4KfwQCBLAR8DRzp3biAge5utZcKsQoQaC1rCEplfmzEo5ovIlBcMq5x1BxnrnlwEPRmM7MefRa+OeAOQJcstHcrJFO7QIDAQAB";
var dataB64 = "aGVsbG8=";
var signatureB64 = "aEOmUA7YC5gvF6QgH+TMg0erY5pzr83nykZGFtyGOOe+6ld+MC4/Qdb608XiNud+pBpzh0wqd6aajOtJim5XEfCH8vUPsv45aSPtukUIQTX00Oc1frIFDQI6jGJ4Q8dQYIwpqsyE2rkGwTDzt1fTTGiw54pLsJXjtL/D5hUEKL8=";
var signatureAlgorithm = {name: 'RSASSA-PKCS1-v1_5',modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]),hash: { name: 'SHA-256'  }};

//convert public key, data and signature to ArrayBuffer. 
var publicKey = str2ab(atob(publicKeyB64)); 
var data = str2ab(atob(dataB64));
var signature = str2ab(atob(signatureB64));            

crypto.subtle.importKey("spki", publicKey, signatureAlgorithm, false,["verify"]).
    then(function(key){
        console.log(key);
        return crypto.subtle.verify( signatureAlgorithm, key, signature, data);                    
}).then( function (valid) {
    console.log("Signature valid: "+valid);
}).catch(function(err) {
    alert("Verification failed " + err );
});

我无法完全重现这个问题。使用您已链接的str2ab实用程序功能,代码可以正常运行。

//Utility function
function str2ab(str) {
  var arrBuff = new ArrayBuffer(str.length);
  var bytes = new Uint8Array(arrBuff);
  for (var iii = 0; iii < str.length; iii++) {
    bytes[iii] = str.charCodeAt(iii);
  }
  return bytes;
}

我建议比较两个代码以找出差异