如何使“MessageDigest SHA-1和Signature NONEwithRSA”等同于“Signature SHA1withRSA”

我有兴趣将带有RSA签名的SHA-1哈希应用于某些数据,但我需要分两步完成 – 首先应用哈希然后对数据进行签名。 Signature.sign()函数似乎创建了一个最终签名的更复杂(ASN.1?)的数据结构(请参阅此问题 )。 如何在不使用任何外部库(如BouncyCastle)的情况下制作两个等效文件?

使用签名应用哈希并签名:

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(); 

通过MessageDigest应用哈希,然后使用签名进行签名:

 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令牌进行签名,但我要求签名数据与遗留数据的格式相同,以便进行validation。

我能够通过以下方式解决这个问题:

  1. 要签名的数据需要在DigestInfo DER编码的字节数组中正确格式化。 签名SHA1withRSA会为您解决此问题,但如果您想通过两个步骤完成此任务,则需要创建自己的DigestInfo。 尽管我不想使用第三方库,但我最终还是将BouncyCastle中非常少量的ASN.1类复制到我的项目中来实现这一目标。

  2. 如果您尝试使用Cipher API加密DigestInfo,则PKCS1填充将是随机的,不适合数字签名。 我需要静态填充。

  3. Signature.getInstance(“NONEwithRSA”,“SunMSCAPI”)拒绝DER编码的DigestInfo格式,如果您尝试对该数据进行签名,则会返回错误。 但是,由于我最终想要使用PKCS11 API来生成签名,我最终使用PKCS11 C_SignInit和C_Sign函数签署了DER编码的DigestInfo。

总结一下,对我有用的是:

  1. 使用Java MessageDigest API生成要签名的数据的SHA-1哈希
  2. 生成DigestInfo DER编码的ASN.1对象,其中SHA-1散列和SHA-1 OID嵌入在对象中。
  3. 使用来自第三方库的PKCS11 C_Sign函数签署了DigestInfo。

以下链接最有助于解决我的问题:

Oracle论坛:SHA1withRSA – 如何分两步完成?

StackOverflow:将SHA1和RSA与java.security.Signature与MessageDigest和Cipher一起使用

emsworth的回答是在为同一个问题挣扎时为我提供了很大帮助(但使用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定义了如何构造DER编码(第42页)。例如,在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);