使用以下两种方法计算签名有什么区别?
Signature.getInstance("SHA256withRSA")
MessageDigest.getInstance("SHA-256")
并使用摘要计算摘要Signature.getInstance("RSA");
以获得签名?如果它们不同,是否有办法修改方法2,使两个方法给出相同的输出?
我尝试了以下代码:
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);
}
}
但是,输出并不相同。
以下是带有我的测试键的示例输出:
方法1:
61427B2A2CF1902A4B15F80156AEB09D8096BA1271F89F1919C78B18D0BABA08AA043A0037934B5AE3FC0EB7702898AC5AE96517AFD93433DF540353BCCE72A470CFA4B765D5835E7EA77743F3C4A0ABB11414B0141EF7ECCD2D5285A69728D0D0709C2537D6A772418A928B0E168F81C99B538FD25BDA7496AE8E185AC46F39
方法2:
BA9039B75CA8A40DC9A7AED51E174E2B3365B2D6A1CF94DF70A00D898074A51FDD9973672DDE95CBAC39EBE4F3BA529C538ED0FF9F0A3F9A8CE203F1DFFA907DC508643906AA86DA54DFF8A90B00F5F116D13A53731384C1C5C9C4E75A3E41DAF88F74D2F1BCCF818764A4AB144A081B641C1C488AC8B194EB14BC9D1928E4EA
更新1:
根据mkl的回答,我修改了代码,但仍然无法正确执行。我还想念什么吗?
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哈希值封装在DigestInfo
结构中
DigestInfo ::= SEQUENCE {
digestAlgorithm DigestAlgorithm,
digest OCTET STRING
}
在填充之前先进行加密,然后再加密;在后一种情况下,将对裸露的SHA256哈希值进行填充和加密。
如果它们不同,是否有办法修改方法2,使两个方法给出相同的输出?
首先,您必须DigestInfo
在使用进行签名之前将哈希值封装在结构中"NONEwithRSA"
。
RFC 3447第9.2节通过在注释1中指出
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.
针对以上部分,OP使用更新的代码更新了他的问题。但是,不幸的是,它对他还没有起作用。从而,
我执行了OP的代码(SignInSteps.java)。由于他没有提供私钥,因此我使用了自己的测试密钥(demo-
rsa2048.p12)。结果:
GreenhandOriginal:
1B9557B6A076226FA4C26A9370A0E9E91B627F14204D427B03294EC4BFC346FDEEFB3A483B1E5A0593F26E9DE87F9202E1064F4D75B24B8FA355B23A560AF263361BB94B2339C3A01952C447CAC862AA9DCAB64B09ABAA0AD50232CDB299D1E4B5F7138F448A87ED32BFF4B5B66F35FFA08F13FD98DFCEC7114710282E463245311DA7A56CBEA958D88137A8B507D8601464535978EFE36EE37EF721260DB7112484F244409F0BD64C823ACFB13D06ABA84A9A0C5AB207E19231D6A71CC80F07FDA2A9654F0F609C2C3396D6DFFBBB10EF4C3D4B5ADFC72EACC044E81F252B699F095CFEF8630B284B1F6BD7201367BD5FDF2BB4C20BD07B9CC20B214D86C729
4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA
GreenhandUpdated:
method 1: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA
method 2: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA
因此,与OP的观察相反,在更新代码的情况下,签名相等。
不承担复制和粘贴错误,可能还会涉及其他差异。
我使用Java 8(1.8.0_20)添加了无限管辖权文件,并使用BouncyCastle 1.52、1.49和1.46(由于BC
API更改而对测试代码进行了少量修改)进行了测试。
OP在评论中提到:
Java是JRE 8更新66。BouncyCastle是bcprov-jdk15on-153.jar。
因此我更新了Java,仍然没有区别。
然后,我将BouncyCastle更新为1.53。实际上,突然之间结果有所不同:
GreenhandOriginal:
1B9557B6A076226FA4C26A9370A0E9E91B627F14204D427B03294EC4BFC346FDEEFB3A483B1E5A0593F26E9DE87F9202E1064F4D75B24B8FA355B23A560AF263361BB94B2339C3A01952C447CAC862AA9DCAB64B09ABAA0AD50232CDB299D1E4B5F7138F448A87ED32BFF4B5B66F35FFA08F13FD98DFCEC7114710282E463245311DA7A56CBEA958D88137A8B507D8601464535978EFE36EE37EF721260DB7112484F244409F0BD64C823ACFB13D06ABA84A9A0C5AB207E19231D6A71CC80F07FDA2A9654F0F609C2C3396D6DFFBBB10EF4C3D4B5ADFC72EACC044E81F252B699F095CFEF8630B284B1F6BD7201367BD5FDF2BB4C20BD07B9CC20B214D86C729
4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA
GreenhandUpdated:
method 1: 6BAAAC1060B6D0D56AD7D45A1BEECE82391088FF47A8D8179EFBBEB0925C4AC6C9DFC56F672E99F4A6E3C106A866B70513C25AE11B267286C584A136FBC20C4D1E7B10697352DF020BA5D67029A6EF890B2674F02C496CB1F1EBB0D4DBB580EB045DBB0FA0D7D73B418FF63F345658C6C73DA742FE260C9639C94967A928F74F61DACA03310B9986C32D83CAB8C7FC13E80612CCFC0B7E3E35BEA04EAEBDAA55FB8837B4661DC71499B4A0B1D36E1D23D9927CDB55C237D5AB2E5C088F29C6FAFAD9FE64DD4851CEC113560864E9923D485D0C6E092C8EBE82D29C312E5835B38EE9BD6B8B4BCC753EF4EE4D0977B2E781B391839E3EC31C36E5B1AA0CE90227
method 2: 4B9ECA6DD47C1B230D972E7DA026165F1CE743EC96825E4C13DFE2C6437FE673A13CA622047EE7D2F7C5280198D81550A1CBD17F8E8A3C4C2D53A746FA6464AA5194FC2782527B014F017008D89BB2C80B7FA367C74FE01369986B56BCE7DC573A11ED884511F0CB12160CA5E42D488451AA8961BF5A9F71E6A5E89F19BC8EFAC26DDE989A0369667EE74372F6E558887FE2561EA926B441AB8F0FD3DEDD608A671011313372084B059CAD7E4807AC852C0873C57F216349422771C089678BAC3021D054C4427EADE70219E251617B83E68640DD7D03C3F99E47F79EB71C124F59EDEA724496A4552F2E9E1F90DDE550745E85483D823F146982C6D2008FE9AA
有趣的是,仅更新后的代码中方法1的值有所不同。因此,在这种情况下,我研究了中介对象
[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节所期望的编码。
查看ASN.1转储,您会发现BC 1.52将AlgorithmIdentifier编码为
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中,算法参数完全丢失。这建议更改线
AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, null);
至
AlgorithmIdentifier sha256Aid = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE);
突然之间,它也可以与BouncyCastle 1.53一起使用,方法1和方法2的值一致!;)
TL; DR
null
实例化时AlgorithmIdentifier
,请勿将其用作SHA-256参数,DERNull.INSTANCE
而应使用。
OP在评论中表示他想进一步了解
- 您如何检查BouncyCastle的中间对象和
- 您如何产生ASN.1转储。
所以…
非常简单。首先我把线分开
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版本执行了代码。
彼得·古特曼(Peter
Gutmann)有一个著名的实用程序dumpasn1,它已成为许多用于创建和显示ASN.1转储的命令行和GUI工具的内核。我目前正巧使用GUIdumpASN-
ng。
在当前情况下,我将的内容保存byte[] encodedDigestInfo
到文件中(可以使用例如来完成Files.write
),然后在GUIdumpASN-ng中打开这些文件。
问题内容: 我想知道以下两个之间有区别吗? 工作流程会改变吗? ps:虽然我想更广泛地考虑这一点,但我处于有偏见的环境中。 问题答案: 您已经问过 “链接” 与 “分支” 。 假设并且表示返回诺言的异步操作,是的,两者之间存在显着差异。对于选项1: 它进行序列化,因此直到解决了返回的promise后才调用它。 适用于或中的错误 如果拒绝,则不会调用。 对于选项2: 不等待解决。 一旦返回类似于的,
似乎以下两个命令(来自LibreSSL的openssl)都会生成私钥。他们之间有区别吗?如果没有,为什么有两种方法来生成私钥?谢谢
问题内容: 查看下面的示例代码 和 当我问到代码差异时,我并不是在说执行流程,这是显而易见的。 那么这些代码之间的 真正 区别是什么?建议何时 在 通话 前 使用您的代码,何时 在 通话 后 使用您的代码?我想在某些情况下这很重要。 问题答案: 您不应在之前放置任何代码,因为此方法可以使系统执行正确暂停应用程序所需的操作。您要在回调中执行的所有代码都应放在调用之后。希望这可以帮助。 活动报价: 注
嗨,我在写一个程序,从文件,并创建一个私钥对象供以后使用。。我面临的问题是,一些文件头以 而其他人则从 通过我的搜索,我知道第一个是PKCS#8格式的,但我不知道另一个属于什么格式。
问题内容: 我错放了太多次了,我想我一直忘记,因为我不知道两者之间的区别,只是一个给了我我期望的价值,而另一个却没有。 为什么是这样? 问题答案: 是的简写形式(尽管请注意,该表达式只会被计算一次。) 是的,即指定一元的到。 例子:
问题内容: 因此,我有一段简单的代码可以打印出整数1-10: 然后,如果仅在第3行上更改一个运算符,它将打印出无限数量的1整数(我知道为什么会这样做)。为什么在运行第二个程序时没有出现语法错误?如果赋值运算符后面跟着一个加法运算符,它不会调用语法错误吗? 问题答案: 与相同, 只是意味着。