3

I am developing a Java (JDK 1.8) application using standard (built in) DSA classes in order to verify digital signatures.

I have data files and the expected signatures stored in text files as shown below:

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

Note that the signature is expressed in the form of (R,S) pair as described in the FIPS 186-3 NIST Standard.

In order to verify the signature I am calling the method verify(byte[] signature) from java.security.Signature. This method expects a byte array that represents the signature to be verified. However, I do not know how to convert the (R,S) pair into a byte array. Therefore I am not able to verify the signature.

So, I would like to know:

1) Is there a way of converting (R, S) pair into a DSA byte array signature as expected by the verify() method?; Or,

2) Is there a way of getting the R and S values from the Java Signature instance class so that I can compare these values to the ones I have?

Thanks in advance.

EDIT: the solution proposed by @dave_thompson_085 worked pretty well! See below the complete source code:

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

1 Answers1

2

1A. The form expected by normal Java crypto (and most but not all other uses of DSA, and also ECDSA) is an ASN.1 DER encoding. https://crypto.stackexchange.com/a/1797/12642 explains what the difficulties are but doesn't quite tell you how to do it.

1B. If you have or can install the BouncyCastle cryptoprovider jar, it contains a whole set of ASN.1 routines. Or easier it also contains a low-level primitive org.bouncycastle.crypto.signers.DSASigner with init and verifySignature(byte[], BigInteger, BigInteger) accessible. (But this primitive does not do the hash, so do that yourself first.)

1C. If you must do it yourself with standard crypto:

// 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(), 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, tot = off+(2-2)+sb.length;
byte[] der = new byte[tot+2];
der[0] = 0x30; der[1] = tot;
der[2+0] = 0x02; der[2+1] = rb.length; System.arraycopy(rb,0, der,2+2, rb.length);
der[off+0] = 0x02; der[off+1] = sb.length; System.arraycopy(sb,0, der,off+2, sb.length);

 2. You cannot verify a standard DSA signature by comparing r and s. As you should know from reading FIPS186-3 sections 4.5 and 4.6 signatures are randomized; computing two (or more) signatures for the same message gives a different (r,s) pair every time -- unless you repeat enough times to hit the same k, which on average is 2^159 tries for older 1024/160 groups/keys, more for newer/larger ones. If you have a million computers each of which can do a million tries per second, this will still take about 16,000,000,000,000,000,000,000,000,000 years.

Community
  • 1
  • 1
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • Thanks Dave Thompson for your reply. I do prefer using the built in java.security classes just because I do believe they can manage to solve this issue. And you are quite right about the impossibility of comparing r and s values to verify the signature. I forgot to mention that I know the issuer´s public key (that is, the p, q, g and y values). Therefore, is it possible to verify the signature knowing p, q, g, y, r and s values? – Marcelo Medeiros Nov 14 '16 at 19:39
  • Of course, but you need to build the required `DSAPublicKey` and signature format yourself if you don't want to rely on third party libs. – Maarten Bodewes Nov 15 '16 at 00:34
  • Sure, I am doing this: // get issuer´s public key PublicKey pubKey = KeyFactory.getInstance("DSA").generatePublic(new DSAPublicKeySpec(y, p, q, g)); Signature sig = Signature.getInstance("SHA1withDSA"); sig.initVerify(pubKey); sig.update(...); // read file to check and call update() byte[] sigToVerify = convert(r, s, y, p, q, g,...); // convert (r,s,...) to the expected format boolean verify = sig.verify(sigToVerify); The problem is that I do not know how to implement the convert() method as shown above. Any ideas? – Marcelo Medeiros Nov 15 '16 at 01:14
  • Sorry, this is my first post. I have not discovered how to format the code yet! – Marcelo Medeiros Nov 15 '16 at 01:22
  • @MarceloMedeiros META: formatting in comments is very limited, and SX is designed to be Q&A not discussion; excessive comments often get deleted. Add substantial additional details, data, and especially code _to your question_ using the edit button. It's usually nice to mark such changes as 'added' or 'edited', although the system maintains an edit history automatically and can display it on request. – dave_thompson_085 Nov 15 '16 at 03:34
  • @MarceloMedeiros: but to the point, you provide p,q,g,y in the DSAPublicKey (or equivalent cert) to `.initVerify`; you provide signature containing r,s and ONLY r.s to `.verify` in the format I showed. – dave_thompson_085 Nov 15 '16 at 03:40
  • Thank you @dave_thompson_085! Everything is working like a charm! – Marcelo Medeiros Nov 15 '16 at 16:39