我有兴趣将带有RSA签名的SHA-1哈希应用于某些数据,但我需要分两步完成 - 首先应用哈希然后对数据进行签名。 Signature.sign()函数似乎创建了一个最终签名的更复杂(ASN.1?)的数据结构(参见this question)。如何在不使用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令牌进行签名,但我要求签名数据与旧数据格式相同,以便进行验证。
答案 0 :(得分:2)
我能够通过以下方式解决这个问题:
要签名的数据需要在a中正确格式化 DigestInfo DER编码的字节数组。签名SHA1withRSA需要 为你照顾这个,但如果你想分两步完成它 过程中,您需要创建自己的DigestInfo。我最终复制了 从BouncyCastle到我的最少量的ASN.1类 尽管我不想使用第三个,但我还是想要实现这个目标 party lib。
如果您尝试使用Cipher API来加密DigestInfo,那么 PKCS1填充将是随机的,不适合数字 签名。我需要静态填充。
Signature.getInstance(" NONEwithRSA"," SunMSCAPI")拒绝 DER编码的DigestInfo格式,如果您尝试,将返回错误 签署该数据。但是,因为我最终想要使用PKCS11 API生成签名,我最终签署了DER编码 具有PKCS11 C_SignInit和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 :(得分:1)
签名的构建方式有很多种。例如,当使用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);
答案 2 :(得分:0)
根据其他同事的回答实现的示例(可能对某人有用):
class SignatureTest {
@Test
void signAndVerify() throws Exception {
byte[] message = "some text".getBytes();
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
byte[] digest = MessageDigest.getInstance("SHA1").digest(message);
// or encodeToPKCS1BySunJCE() may be called
byte[] pkcs1EncodedDigest = encodeToPKCS1ByBounceCastle(digest);
Signature signature = Signature.getInstance("NONEwithRSA");
signature.initSign(keyPair.getPrivate());
signature.update(pkcs1EncodedDigest);
byte[] digestSignature = signature.sign();
signature.initVerify(keyPair.getPublic());
signature.update(pkcs1EncodedDigest);
assertTrue(signature.verify(digestSignature));
}
@Test
void signDigestAndMessage() throws Exception {
byte[] message = "some text".getBytes();
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
byte[] digest = MessageDigest.getInstance("SHA1").digest(message);
// or encodeToPKCS1BySunJCE() may be called
byte[] pkcs1EncodedDigest = encodeToPKCS1BySunJCE(digest);
Signature signature = Signature.getInstance("NONEwithRSA");
signature.initSign(keyPair.getPrivate());
signature.update(pkcs1EncodedDigest);
byte[] signedDigest = signature.sign();
signature = Signature.getInstance("SHA1WithRSA");
signature.initSign(keyPair.getPrivate());
signature.update(message);
byte[] signedMessage = signature.sign();
assertEquals(signedDigest, signedMessage);
}
private byte[] encodeToPKCS1ByBounceCastle(byte[] digest) throws IOException {
return new DigestInfo(new AlgorithmIdentifier(X509ObjectIdentifiers.id_SHA1, DERNull.INSTANCE), digest).getEncoded();
}
private byte[] encodeToPKCS1BySunJCE(byte[] digest) throws IOException {
DerOutputStream out = new DerOutputStream();
new AlgorithmId(AlgorithmId.SHA_oid).encode(out);
out.putOctetString(digest);
return new DerValue(DerValue.tag_Sequence, out.toByteArray()).toByteArray();
}
}