2014-04-03 159 views
3

我有興趣將SHA-1哈希與RSA簽名應用於某些數據,但我需要分兩步執行 - 應用哈希首先簽署數據。 Signature.sign()函數似乎創建了一個最終簽名的更復雜(ASN.1?)數據結構(請參見this question)。如何在不使用任何外部庫如BouncyCastle的情況下創建這兩個等價物?如何使「MessageDigest SHA-1和簽名NONEwithRSA」等效於「簽名SHA1withRSA」

應用散列,並與簽名單步登錄:

PrivateKey privatekey = (PrivateKey) keyStore.getKey(alias, null); 
... 
sig = Signature.getInstance("SHA1withRSA", "SunMSCAPI"); 
sig.initSign(privatekey); 
sig.update(data_to_sign); 
byte[] bSignedData_CAPISHA1_CAPIRSA = sig.sign(); 

通過消息摘要應用散列,然後用簽名來簽署:

PrivateKey privatekey = (PrivateKey) keyStore.getKey(alias, null); 
... 
MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); 
byte[] data_to_sign = sha1.digest(bdataToSign); 
Signature sig = Signature.getInstance("NONEwithRSA", "SunMSCAPI"); 
sig.initSign(privatekey); 
sig.update(data_to_sign); 
byte[] bSignedData_JAVASHA1_CAPIRSA = sig.sign(); 
... 

我要尋找以下等效:

bSignedData_JAVASHA1_CAPIRSA == bSignedData_CAPISHA1_CAPIRSA 

我的最終目標是創建散列,然後用PKCS11令牌簽名,bu t爲了驗證目的,我要求籤名數據與傳統數據的格式相同。

回答

2

我能夠通過執行以下操作來解決這個問題:

  1. 要簽名需要被在 DigestInfo DER編碼的字節數組正確格式化的數據。簽名SHA1withRSA爲您提供 照顧,但如果您想在兩步 過程中完成此操作,則需要創建自己的DigestInfo。儘管我不希望使用第三方 party lib,但我最終還是從BouncyCastle拷貝了 很少量的ASN.1類到我的 項目中來完成此任務。

  2. 如果您嘗試使用加密API到DigestInfo加密時, PKCS1填充將是隨機的,不適合用於數字簽名 。我需要靜態填充。

  3. 的Signature.getInstance(「NONEwithRSA」,「SunMSCAPI」)拒絕 DER編碼格式DigestInfo,如果你試圖 籤數據將返回錯誤。但是,由於我最終希望使用PKCS11 API來生成簽名,因此我最終使用PKCS11 C_SignInit和C_Sign函數對DER編碼的DigestInfo進行了簽名。

總之,什麼工作對我來說是:

  1. 生成數據的SHA-1散列使用Java消息摘要API
  2. 產生DigestInfo DER-ASN.1編碼簽署對象中嵌入了SHA-1哈希和SHA-1 OID。
  3. 使用來自第三方庫的PKCS11 C_Sign函數對DigestInfo進行了簽名。

以下鏈接是最有幫助的解決我的問題:

Oracle Forums: SHA1withRSA - how to do that in 2 steps?

StackOverflow: Using SHA1 and RSA with java.security.Signature vs. MessageDigest and Cipher

1

埃姆斯沃思的回答是,當我莫大的幫助,同樣的問題掙扎(但使用SHA512時)。然而,它仍然缺少一個提示,我需要再花幾天的時間才能自己找出答案。

簽名的構建方式有多種。例如,當使用RSASSA-PKCS1-v1_5(來自RFC 5246,TLS 1.2)時,DER編碼的DigestInfo並不是通常的方式。例如,如果使用BouncyCastle

DigestInfo digestInfo = new DigestInfo(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512), hash); 
byte[] digestedHash = digestInfo.getEncoded(ASN1Encoding.DER); 

不會產生預期結果。 RFC 3447定義瞭如何在第42頁上構造DER編碼。例如,在SHA-512的情況下,DER編碼如下:

// code example includes MessageDigest for the sake of completeness 
byte[] input = ... // the raw data 
MessageDigest md = MessageDigest.getInstance("SHA-512"); 
md.update(input); 
byte[] hash = md.digest(); 

// Taken from RFC 3447, page 42 for SHA-512, create input for signing 
byte[] modifierBytes = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; 
ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
baos.write(modifierBytes); 
baos2.write(hash); 

// create signature 
Signature s = Signature.getInstance("NONEwithRSA"); 
s.initSign(MyTlsCredentials.THE_CLIENT_KEY); 
s.update(baos.toByteArray()); 
byte[] signature = s.sign(); 

// add prefix as specified in RFC 3447, im my case it had always been the shown values 
// but I have not understand the RFC completely in this point as the code seems to be 
// contradictious to the interpretation of the byte values for the hash function from page 42. 
ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); 
baos2.write(new byte[] { 1, 0 }); 
baos2.write(signature5);