我正在开发一个使用标准(内置)DSA类的Java(JDK 1.8)应用程序来验证数字签名。如何在Java中验证给定(R,S)对的DSA签名?
我有数据文件和存储在文本文件的预期的签名,如下所示:
// Signature part R:
4226 3F05 F103 E3BE 59BF 3903 37F8 0375 8802 5D8F.
// Signature part S:
AF21 15B0 16E4 1761 75B8 C7D4 F877 5AB7 26BB AE72.
注意,签名是(R,S),如FIPS描述对形式表示186- 3 NIST标准。
为了验证我打电话的方法验证(字节[]签名)从java.security.Signature中的签名。该方法需要一个表示待验证签名的字节数组。但是,我不知道如何将(R,S)对转换为字节数组。因此我无法验证签名。
所以,我想知道:
1)是否有转换(R,S)对成预期由所述验证()方法的DSA字节数组签名的方式?;或者,
2)是否有从Java签名实例类获得R和S值,这样我就可以这些值与那些我有一个办法吗?
在此先感谢。
编辑:由@ dave_thompson_085提出的解决方案工作得很好!请参阅下面的完整源代码:
// read DSA parameters from file or other means
BigInteger r = new BigInteger(...);
BigInteger s = new BigInteger(...);
BigInteger p = new BigInteger(...);
BigInteger q = new BigInteger(...);
BigInteger g = new BigInteger(...);
BigInteger y = new BigInteger(...);
// get the public key
KeySpec publicKeySpec = new DSAPublicKeySpec(y, p, q, g);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
// read the input file to be checked and update signature
Signature signature = Signature.getInstance("SHA1withDSA");
signature.initVerify(publicKey);
File inputFile = new File(...);
try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(inputFile))) {
byte[] buffer = new byte[1024];
while (is.available() != 0) {
int len = is.read(buffer);
signature.update(buffer, 0, len);
}
}
// convert (r, s) to ASN.1 DER encoding
// assuming you have r and s as !!positive!! BigIntegers
// (if you have unsigned byte[] as shown in your Q,
// use BigInteger r = new BigInteger (1, bytes) etc.
byte[] rb = r.toByteArray();
byte[] sb = s.toByteArray(); // sign-padded if necessary
// these lines are more verbose than necessary to show the structure
// compiler will fold or you can do so yourself
int off = (2 + 2) + rb.length;
int tot = off + (2 - 2) + sb.length;
byte[] der = new byte[tot + 2];
der[0] = 0x30;
der[1] = (byte) (tot & 0xff);
der[2 + 0] = 0x02;
der[2 + 1] = (byte) (rb.length & 0xff);
System.arraycopy(rb, 0, der, 2 + 2, rb.length);
der[off + 0] = 0x02;
der[off + 1] = (byte) (sb.length & 0xff);
System.arraycopy(sb, 0, der, off + 2, sb.length);
// verifies if the signature is valid
boolean isValid = signature.verify(des);
感谢戴夫汤普森的回复。 我更喜欢使用内建的java.security类,因为我相信他们可以设法解决这个问题。 对于比较r值和s值来验证签名是不可能的。我忘记提及我知道发行人的公钥(即p,q,g和y值)。 因此,是否有可能验证签名知道p,q,g,y,r和s值? –
当然,如果您不想依赖第三方库,则需要自己构建所需的“DSAPublicKey”和签名格式。 –
当然,我这样做: //获得发行人的公钥 PublicKey pubKey = KeyFactory.getInstance(“DSA”)。generatePublic(new DSAPublicKeySpec(y,p,q,g)); 签名sig = Signature.getInstance(“SHA1withDSA”); sig.initVerify(pubKey); sig.update(...); //读取文件检查并调用update() byte [] sigToVerify = convert(r,s,y,p,q,g,...); //将(r,s,...)转换为预期格式 boolean verify = sig.verify(sigToVerify); 问题是我不知道如何实现如上所示的convert()方法。有任何想法吗? –