java sha256withrsa,SHA256withRSA和SHA256之间的区别然后是RSA

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

Compute a signature with Signature.getInstance("SHA256withRSA")

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

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

I tried the following code:

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:

method 1: 61427B2A2CF1902A4B15F80156AEB09D8096BA1271F89F1919C78B18D0BABA08AA043A0037934B5AE3FC0EB7702898AC5AE96517AFD93433DF540353BCCE72A470CFA4B765D5835E7EA77743F3C4A0ABB11414B0141EF7ECCD2D5285A69728D0D0709C2537D6A772418A928B0E168F81C99B538FD25BDA7496AE8E185AC46F39

method 2: BA9039B75CA8A40DC9A7AED51E174E2B3365B2D6A1CF94DF70A00D898074A51FDD9973672DDE95CBAC39EBE4F3BA529C538ED0FF9F0A3F9A8CE203F1DFFA907DC508643906AA86DA54DFF8A90B00F5F116D13A53731384C1C5C9C4E75A3E41DAF88F74D2F1BCCF818764A4AB144A081B641C1C488AC8B194EB14BC9D1928E4EA

Update 1:

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

}

}

method 1:

675D868546777C5A9B5E74988E0CD41A46A929C1D0890B32B1FBE34F12D68F1FDB56E623294DB903F6AC60A2ADA61976B27C66056A16F5790A78168803AD2C685F9B4CF983C939305A9819CBA9D95441CD7214D40D06A98B4DDF9692A7D300DD51E808A6722A0D7C288DBD476DF4DEEBB3DAF41CFC0978F24424960F86F0284E

method 2:

BA9039B75CA8A40DC9A7AED51E174E2B3365B2D6A1CF94DF70A00D898074A51FDD9973672DDE95CBAC39EBE4F3BA529C538ED0FF9F0A3F9A8CE203F1DFFA907DC508643906AA86DA54DFF8A90B00F5F116D13A53731384C1C5C9C4E75A3E41DAF88F74D2F1BCCF818764A4AB144A081B641C1C488AC8B194EB14BC9D1928E4EA

解决方案

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

}

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

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

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

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.

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,

The OP's code

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

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.

The environment

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).

The OP mentioned in a comment:

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

Thus I updated Java, still no difference.

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

GreenhandOriginal:

1B9557B6A076226FA4C26A9370A0E9E91B627F14204D427B03294EC4BFC346FDEEFB3A483B1E5A0593F26E9DE87F9202E1064F4D75B24B8FA355B23A560AF263361BB94B2339C3A01952C447CAC862AA9DCAB64B09ABAA0AD50232CDB299D1E4B5F7138F448A87ED32BFF4B5B66F35FFA08F13FD98DFCEC7114710282E463245311DA7A56CBEA958D88137A8B507D8601464535978EFE36EE37EF721260DB7112484F244409F0BD64C823ACFB13D06ABA84A9A0C5AB207E19231D6A71CC80F07FDA2A9654F0F609C2C3396D6DFFBBB10EF4C3D4B5ADFC72EACC044E81F252B699F095CFEF8630B284B1F6BD7201367BD5FDF2BB4C20BD07B9CC20B214D86C729

4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA

GreenhandUpdated:

method 1: 6BAAAC1060B6D0D56AD7D45A1BEECE82391088FF47A8D8179EFBBEB0925C4AC6C9DFC56F672E99F4A6E3C106A866B70513C25AE11B267286C584A136FBC20C4D1E7B10697352DF020BA5D67029A6EF890B2674F02C496CB1F1EBB0D4DBB580EB045DBB0FA0D7D73B418FF63F345658C6C73DA742FE260C9639C94967A928F74F61DACA03310B9986C32D83CAB8C7FC13E80612CCFC0B7E3E35BEA04EAEBDAA55FB8837B4661DC71499B4A0B1D36E1D23D9927CDB55C237D5AB2E5C088F29C6FAFAD9FE64DD4851CEC113560864E9923D485D0C6E092C8EBE82D29C312E5835B38EE9BD6B8B4BCC753EF4EE4D0977B2E781B391839E3EC31C36E5B1AA0CE90227

method 2: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA

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

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.

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

: }

while BC 1.53 creates

2 11: SEQUENCE {

<06 09>

4 9: OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1)

: (NIST Algorithm)

: }

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

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

to

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

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

TL;DR

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

How did I...

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

how do you inspect the intermediate object of BouncyCastle and

how do you produce the ASN.1 dumps.

So...

... inspect the intermediate object

Quite simple. First I split up the line

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

in the updated code as

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

rsaSignature.update(encodedDigestInfo);

and then added console outputs

System.out.println(" hash: " + bytesToHex(outputDigest));

System.out.println(" algo: " + sha256Aid.getAlgorithm());

System.out.println(" info: " + bytesToHex(encodedDigestInfo));

Finally I executed the code with the different BouncyCastle versions.

... produce the ASN.1 dumps

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.

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.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值