2

我想Java的標誌/驗證密鑰和Javascript WebCrypto驗證失敗

  1. 生成登錄/在Java Web應用程序驗證密鑰(RSA)
  2. 符號的值(使用這些密鑰)(讓調用服務器端)爲了
  3. 到Web客戶端驗證 - 公共密鑰導入爲RSASSA-PKCS1-v1_5 + SHA-256,(在瀏覽器中,使用WebCrypto API /客戶端)

我^ h即使在客戶端將公共簽名/驗證密鑰成功導入爲JWK,仍會出現驗證簽名值(在Java服務器端簽名)的問題。

我想知道在我可能遇到的任何步驟(OpenSSL,Java或Javascript)中是否有任何算法兼容性問題問題。

用來生成密鑰

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(服務器端)的值。OpenSSL的命令

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應用程序,以便作爲客戶端/瀏覽器進行驗證(客戶端知道publicKe y在第一步中生成)。

// 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導出的公鑰來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的預期數據,但沒有注意到它;自然驗證失敗。

+0

的Java字節'[]'門店值。我認爲你需要使用'Uint8Array'和'ArrayBuffer'。你可以嘗試使用這裏記錄的'stringToArrayBuffer'函數http://stackoverflow.com/questions/36018233/how-to-load-a-pkcs12-digital-certificate-with-javascript-webcrypto-api/37407739#37407739代表「數據」和「簽名」? – pedrofb

+0

謝謝,依然無法正常工作。也許我需要改變實現的基本原理:考慮在Java中導入X509證書並使用'pki.js' +'asn1.js'在JavaScript中爲WebCrypto提取publicKey(證書)以驗證([example這裏](https://github.com/PeculiarVentures/PKI.js/blob/master/examples/certificate-decode-example.html)) – nuno

+0

我想這不是公鑰的問題,因爲它提出了一個當您導入JWK時例外。但是,要確保它可以以'spki'格式導入(將二進制RSA密鑰轉換爲ArrayBuffer) – pedrofb

回答

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; 
} 

我建議比較兩種代碼找到0..255的差異

+0

嗨@pedrofb,感謝您的反饋 - 事實證明,這是另一個無關的問題:我使用Javascript在Base64中轉換了預期的數據兩次,卻沒有注意到它;自然驗證失敗。作爲參考,使用'Uint8Array'沒有任何問題,因爲我最初懷疑。 – nuno

+0

另外我使用的'str2ab'和'ab2str'方法就像[this](https://gist.github.com/nunofaria11/6fa749066cb821c6e55f160e03a98889) – nuno

+0

也許你在使用'Uint16Array'時遇到了問題? – nuno