我正在開發一個使用標準(內置)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()方法。有任何想法嗎? –