SHA256withRSA和SHA256之间的区别然后是RSA [英] Difference between SHA256withRSA and SHA256 then RSA

查看:9302
本文介绍了SHA256withRSA和SHA256之间的区别然后是RSA的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用以下两种方法计算签名有什么区别?

What is the difference between compute a signature with the following two methods?


  1. 使用签名计算签名.getInstance(SHA256withRSA)

  2. 使用 MessageDigest.getInstance(SHA-256)计算SHA256 并使用 Signature.getInstance(RSA); 计算摘要以获取签名?

  1. Compute a signature with Signature.getInstance("SHA256withRSA")
  2. Compute SHA256 with MessageDigest.getInstance("SHA-256") and compute the digest with Signature.getInstance("RSA"); to get the signature?

如果它们不同,有没有办法修改方法2,以便两种方法都给出相同的输出?

If they are different, is there a way to modify the method 2 so that both methods give the same output?

我尝试了以下代码:

package mysha.mysha;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class MySHA256 {

    public static void main(String[] args) throws Exception {
        //compute SHA256 first
        Security.addProvider(new BouncyCastleProvider());
        String s = "1234";
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        messageDigest.update(s.getBytes());
        byte[] outputDigest = messageDigest.digest();       
        //sign SHA256 with RSA
        PrivateKey privateKey = Share.loadPk8("D:/key.pk8");
        Signature rsaSignature = Signature.getInstance("RSA");
        rsaSignature.initSign(privateKey);
        rsaSignature.update(outputDigest);
        byte[] signed = rsaSignature.sign();
        System.out.println(bytesToHex(signed));


        //compute SHA256withRSA as a single step
        Signature rsaSha256Signature = Signature.getInstance("SHA256withRSA");
        rsaSha256Signature.initSign(privateKey);
        rsaSha256Signature.update(s.getBytes());
        byte[] signed2 = rsaSha256Signature.sign();
        System.out.println(bytesToHex(signed2));
    }

    public static String bytesToHex(byte[] bytes) {
        final char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[bytes.length * 2];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

}

尽管如此,产出不是相同。

Nevertheless, the outputs are not the same.

以下是我的测试密钥的示例输出:

The following is the sample output with my test key:

方法1: 61427B2A2CF1902A4B15F80156AEB09D8096BA1271F89F1919C78B18D0BABA08AA043A0037934B5AE3FC0EB7702898AC5AE96517AFD93433DF540353BCCE72A470CFA4B765D5835E7EA77743F3C4A0ABB11414B0141EF7ECCD2D5285A69728D0D0709C2537D6A772418A928B0E168F81C99B538FD25BDA7496AE8E185AC46F39

方法2: BA9039B75CA8A40DC9A7AED51E174E2B3365B2D6A1CF94DF70A00D898074A51FDD9973672DDE95CBAC39EBE4F3BA529C538ED0FF9F0A3F9A8CE203F1DFFA907DC508643906AA86DA54DFF8A90B00F5F116D13A53731384C1C5C9C4E75A3E41DAF88F74D2F1BCCF818764A4AB144A081B641C1C488AC8B194EB14BC9D1928E4EA

更新1:

根据mkl的回答,我修改了我的代码,但仍然无法正确使用。我还想错过什么吗?

According to mkl's answer, I modify my code but still cannot get it right. Do I still miss something?

package mysha.mysha;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;

import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class MySHA256 { 
    public static void main(String[] args) throws Exception {
        //compute SHA256 first
        Security.addProvider(new BouncyCastleProvider());
        String s = "1234";
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        messageDigest.update(s.getBytes());
        byte[] outputDigest = messageDigest.digest();

        AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, null);
        DigestInfo di = new DigestInfo(sha256Aid, outputDigest);
        //sign SHA256 with RSA
        PrivateKey privateKey = Share.loadPk8("D:/key.pk8");
        Signature rsaSignature = Signature.getInstance("RSA");
        rsaSignature.initSign(privateKey);
        rsaSignature.update(di.toASN1Primitive().getEncoded());
        byte[] signed = rsaSignature.sign();
        System.out.println("method 1: "+bytesToHex(signed));


        //compute SHA256withRSA as a single step
        Signature rsaSha256Signature = Signature.getInstance("SHA256withRSA");
        rsaSha256Signature.initSign(privateKey);
        rsaSha256Signature.update(s.getBytes());
        byte[] signed2 = rsaSha256Signature.sign();
        System.out.println("method 2: "+bytesToHex(signed2));
    }
    public static String bytesToHex(byte[] bytes) {
        final char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[bytes.length * 2];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }
}

方法1:
675D868546777C5A9B5E74988E0CD41A46A929C1D0890B32B1FBE34F12D68F1FDB56E623294DB903F6AC60A2ADA61976B27C66056A16F5790A78168803AD2C685F9B4CF983C939305A9819CBA9D95441CD7214D40D06A98B4DDF9692A7D300DD51E808A6722A0D7C288DBD476DF4DEEBB3DAF41CFC0978F24424960F86F0284E

方法2:
BA9039B75CA8A40DC9A7AED51E174E2B3365B2D6A1CF94DF70A00D898074A51FDD9973672DDE95CBAC39EBE4F3BA529C538ED0FF9F0A3F9A8CE203F1DFFA907DC508643906AA86DA54DFF8A90B00F5F116D13A53731384C1C5C9C4E75A3E41DAF88F74D2F1BCCF818764A4AB144A081B641C1C488AC8B194EB14BC9D1928E4EA

推荐答案

差价



签订之间的区别SHA256withRSA并计算SHA256哈希并使用RSA签署(= NONEwithRSA)最重要的是,在前一种情况下,计算出的SHA-256哈希值首先封装在 Dig中estInfo 结构

The difference

The difference between signing with "SHA256withRSA" and computing the SHA256 hash and signing it with "RSA" (= "NONEwithRSA") is foremost that in the former case the calculated SHA-256 hash value is first encapsulated in a DigestInfo structure

DigestInfo ::= SEQUENCE {
    digestAlgorithm DigestAlgorithm,
    digest OCTET STRING
}

在填充然后加密之前,在后一种情况下裸体SHA256哈希值被填充和加密。

before being padded and then encrypted while in the latter case the naked SHA256 hash value is padded and encrypted.


如果它们不同,有没有办法修改方法2,以便两种方法都给出相同的输出?

If they are different, is there a way to modify the method 2 so that both methods give the same output?

首先,您必须将哈希值封装在 DigestInfo NONEwithRSA签署前的$ c>结构。

First and foremost you will have to encapsulate the hash value in a DigestInfo structure before signing using "NONEwithRSA".

RFC 3447第9.2节有助于此处在注释1中说明

RFC 3447 Section 9.2 helps here by stating in Note 1 that

1. For the six hash functions mentioned in Appendix B.1, the DER
   encoding T of the DigestInfo value is equal to the following:
   ...
   SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00
                04 20 || H.



使其正常工作



In对OP上方部分的回复用更新后的代码更新了他的问题。不幸的是,它对他来说还不起作用。因此,

Making it work

In response to the section above the OP updated his question with the updated code. Unfortunately, though, it did not yet work for him. Thus,

我执行了OP的代码(SignInSteps.java )。由于他没有提供私钥,我使用了自己的测试密钥(演示rsa2048.p12 )。其结果是:

I executed the OP's code (SignInSteps.java). As he didn't provide the private key, I used a test key of my own (demo-rsa2048.p12). The result:

GreenhandOriginal:
1B9557B6A076226FA4C26A9370A0E9E91B627F14204D427B03294EC4BFC346FDEEFB3A483B1E5A0593F26E9DE87F9202E1064F4D75B24B8FA355B23A560AF263361BB94B2339C3A01952C447CAC862AA9DCAB64B09ABAA0AD50232CDB299D1E4B5F7138F448A87ED32BFF4B5B66F35FFA08F13FD98DFCEC7114710282E463245311DA7A56CBEA958D88137A8B507D8601464535978EFE36EE37EF721260DB7112484F244409F0BD64C823ACFB13D06ABA84A9A0C5AB207E19231D6A71CC80F07FDA2A9654F0F609C2C3396D6DFFBBB10EF4C3D4B5ADFC72EACC044E81F252B699F095CFEF8630B284B1F6BD7201367BD5FDF2BB4C20BD07B9CC20B214D86C729
4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA

GreenhandUpdated:
method 1: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA
method 2: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA



因此,在对比的是OP的意见,签名的更新的代码的情况下,相等的。

Thus, in contrast to the OP's observations, signatures equal in case of the updated code.

不假设复制和粘贴错误,仍然可能存在其他差异。

Not assuming copy&paste errors, there still might be other differences involved.

我使用Java 8(1.8.0_20)进行了测试,添加了无限制的管辖区文件和BouncyCastle 1.52,1.49和1.46(由于BC API更改而对测试代码进行了少量修改)。

I tested using Java 8 (1.8.0_20) with unlimited jurisdiction files added and BouncyCastle 1.52, 1.49, and 1.46 (with a small test code modification due to the BC API changes).

评论中提到的OP:


Java是JRE 8更新66。 BouncyCastle是bcprov-jdk15on-153.jar。

The Java is JRE 8 update 66. The BouncyCastle is bcprov-jdk15on-153.jar.

因此我更新了Java,仍然没有区别。

Thus I updated Java, still no difference.

然后我将BouncyCastle更新为1.53。的确,突然的结果不同:

Then I updated BouncyCastle to 1.53. And indeed, suddenly the results differed:

GreenhandOriginal:
1B9557B6A076226FA4C26A9370A0E9E91B627F14204D427B03294EC4BFC346FDEEFB3A483B1E5A0593F26E9DE87F9202E1064F4D75B24B8FA355B23A560AF263361BB94B2339C3A01952C447CAC862AA9DCAB64B09ABAA0AD50232CDB299D1E4B5F7138F448A87ED32BFF4B5B66F35FFA08F13FD98DFCEC7114710282E463245311DA7A56CBEA958D88137A8B507D8601464535978EFE36EE37EF721260DB7112484F244409F0BD64C823ACFB13D06ABA84A9A0C5AB207E19231D6A71CC80F07FDA2A9654F0F609C2C3396D6DFFBBB10EF4C3D4B5ADFC72EACC044E81F252B699F095CFEF8630B284B1F6BD7201367BD5FDF2BB4C20BD07B9CC20B214D86C729
4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA

GreenhandUpdated:
method 1: 6BAAAC1060B6D0D56AD7D45A1BEECE82391088FF47A8D8179EFBBEB0925C4AC6C9DFC56F672E99F4A6E3C106A866B70513C25AE11B267286C584A136FBC20C4D1E7B10697352DF020BA5D67029A6EF890B2674F02C496CB1F1EBB0D4DBB580EB045DBB0FA0D7D73B418FF63F345658C6C73DA742FE260C9639C94967A928F74F61DACA03310B9986C32D83CAB8C7FC13E80612CCFC0B7E3E35BEA04EAEBDAA55FB8837B4661DC71499B4A0B1D36E1D23D9927CDB55C237D5AB2E5C088F29C6FAFAD9FE64DD4851CEC113560864E9923D485D0C6E092C8EBE82D29C312E5835B38EE9BD6B8B4BCC753EF4EE4D0977B2E781B391839E3EC31C36E5B1AA0CE90227
method 2: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA



有趣的是仅在更新后的代码的方法为1值不同。因此,在这种情况下,我查看了中间对象

Interestingly only the value for method 1 in the updated code differs. Thus, I looked at the intermediary objects in that case

[BC 1.52]
hash: 03AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4
algo: 2.16.840.1.101.3.4.2.1
info: 3031300D06096086480165030402010500042003AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4

[BC 1.53]
hash: 03AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4
algo: 2.16.840.1.101.3.4.2.1
info: 302F300B0609608648016503040201042003AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4

因此,BouncyCastle 1.53以不同方式编码DigestInfo对象! 1.52(及以下)中的编码是 RFC 3447第9.2节预期的编码。

Thus, BouncyCastle 1.53 encodes the DigestInfo object differently! And the encoding in 1.52 (and below) is the one expected by the RFC 3447 Section 9.2.

查看ASN.1转储,可以看到BC 1.52将AlgorithmIdentifier编码为

Looking at the ASN.1 dumps one sees that BC 1.52 encodes the AlgorithmIdentifier as

 2  13:   SEQUENCE {
   <06 09>
 4   9:     OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1)
      :       (NIST Algorithm)
   <05 00>
15   0:     NULL
      :     }

而BC 1.53创建

 2  11:   SEQUENCE {
   <06 09>
 4   9:     OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1)
      :       (NIST Algorithm)
      :     }

因此在1.53中,算法参数完全丢失。这建议更改行

So in 1.53 the algorithm parameters are missing altogether. This suggests changing the line

AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, null);

AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);

突然它也适用于BouncyCastle 1.53,方法1和方法2的值重合! ;)

and suddenly it works with BouncyCastle 1.53, too, the values for method 1 and method 2 coincide! ;)

TL; DR

不要使用 null 作为实例化 AlgorithmIdentifier 时的SHA-256参数,使用 DERNull.INSTANCE 相反。

Don't use null as the SHA-256 parameters when instantiating the AlgorithmIdentifier, use DERNull.INSTANCE instead.

在评论中OP表示他喜欢了解更多信息

In a comment the OP indicated that he'd like to know more about



  1. 如何检查BouncyCastle的中间对象和

  2. 如何生成ASN.1转储。


所以...

非常简单。首先,我将该行分开

Quite simple. First I split up the line

rsaSignature.update(di.toASN1Primitive().getEncoded());

byte[] encodedDigestInfo = di.toASN1Primitive().getEncoded();
rsaSignature.update(encodedDigestInfo);

然后添加控制台输出

System.out.println("    hash: " + bytesToHex(outputDigest));
System.out.println("    algo: " + sha256Aid.getAlgorithm());
System.out.println("    info: " + bytesToHex(encodedDigestInfo));

最后,我用不同的BouncyCastle版本执行了代码。

Finally I executed the code with the different BouncyCastle versions.

有一个名为由Peter Gutmann撰写的dumpasn1 已经成为许多用于创建和显示ASN.1转储的命令行和GUI工具的核心。我目前碰巧使用 GUIdumpASN-ng

There is a well-known utility called dumpasn1 by Peter Gutmann which has become the kernel of many command line and GUI tools for creating and displaying ASN.1 dumps. I currently happen to use GUIdumpASN-ng.

在手头的情况下,我将 byte [] encodedDigestInfo 的内容保存到文件中(可以使用例如 Files.write )并在GUIdumpASN-ng中打开这些文件。

In the case at hand I saved the contents of the byte[] encodedDigestInfo to a file (which can be done using e.g. Files.write) and opened these files in GUIdumpASN-ng.

这篇关于SHA256withRSA和SHA256之间的区别然后是RSA的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆