unable to verify string signed by openssl with dsa key

909 views Asked by At

Adapting the directions at Creating a DSA Signature from the Linux command line I created a DSA signed message:

echo "foobar" > foo.txt
openssl dgst -dss1 -sign dsa_priv.pem foo.txt > sigfile.bin

The directions actually used foo.sha1 instead of foo.txt, where foo.sha1 was generated by sha1sum but signing a hash seems a bit redundant since DSA is, itself, supposed to do hashing.

So, anyway, I did that. Here's the private key I used (I generated it specifically for testing purposes):

-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz
aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9
5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ
ANi+lYG7xMKQ/bE4+bS8gemNAoGBAORowvirD7AB9x2SpdiME41+O4jVR8rs6+GX
Ml3Hif6Yt1kem0CeraX9SNoyBNAzjD5TVMGIdGlgRr6GNreHeXMGWlvdDkvCACER
ZEEtMsKZicm+yl6kR8AGHTCA/PBltHfyrFQd4n9I//UDqI4RjqzvpCXGQcVEsSDY
CCBGBQJRAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX43IkE9w9FveDV1jX5mmfK7yBV
pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m
2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI
kBniZHdFBAZBTE14YJUBkw==
-----END DSA PRIVATE KEY-----

Here's the hex encoded output of sigfile.bin:

302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5

I'm now trying to verify this in Java with BouncyCastle and am unable to do so. Here's my Java code:

import java.io.StringReader;
import org.bouncycastle.openssl.PEMReader;
import java.security.interfaces.DSAPublicKey;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;

import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.DSAParameters;

public class DSA
{
    public static void main(String[] args)
    throws Exception
    {
        byte[] message = "foobar".getBytes();
        byte[] signature = hexStringToByteArray("302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5");

            String key = "-----BEGIN PUBLIC KEY-----\n" +
                "MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOwYAcAzXpuw+XCXuNp5zhAzKdhrRguI\n" +
                "uI5kLia8fhRb+1EnFPNpXt4fUS2c/0P0nvzH/TvApizzMkRYJea6rRSW5B+MDjv6\n" +
                "lvrxv+5xBM15kdug033mgSL7wEJIrTLwbe5/djz2oe+pr1KLqs/fvgyKcQyttUWb\n" +
                "5SmwZ+UVx3zfAhUAu0kA2L6VgbvEwpD9sTj5tLyB6Y0CgYEA5GjC+KsPsAH3HZKl\n" +
                "2IwTjX47iNVHyuzr4ZcyXceJ/pi3WR6bQJ6tpf1I2jIE0DOMPlNUwYh0aWBGvoY2\n" +
                "t4d5cwZaW90OS8IAIRFkQS0ywpmJyb7KXqRHwAYdMID88GW0d/KsVB3if0j/9QOo\n" +
                "jhGOrO+kJcZBxUSxINgIIEYFAlEDgYUAAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX4\n" +
                "3IkE9w9FveDV1jX5mmfK7yBVpQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadg\n" +
                "zyoFyqkmmUi1kNLyixtRqh+m2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9M\n" +
                "TzUkQjFI9UY7kZeK\n" +
                "-----END PUBLIC KEY-----";
        PEMReader reader = new PEMReader(new StringReader(key));
        DSAPublicKey decoded = (DSAPublicKey) reader.readObject();

        DSADigestSigner dsa = new DSADigestSigner(new DSASigner(), new SHA1Digest());
        DSAParameters params = new DSAParameters(
            decoded.getParams().getP(),
            decoded.getParams().getQ(),
            decoded.getParams().getG()
        );
        DSAPublicKeyParameters publickey = new DSAPublicKeyParameters(decoded.getY(), params);
        dsa.init(false, publickey);
        dsa.update(message, 0, message.length);
        boolean result = dsa.verifySignature(signature);

        System.out.println(result ? "good" : "bad");
    }

    public static byte[] hexStringToByteArray(String s)
    {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2)
        {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
}

The signature is not validating. Is there something wrong with my Java code? Maybe OpenSSL is doing something weird with dss1?

I was able to validate the signature just fine with OpenSSL:

openssl dgst -dss1 -verify dsa_pub.pem -signature sigfile.bin foo.txt
1

There are 1 answers

0
dave_thompson_085 On BEST ANSWER

(Unix) echo outputs its arguments, space-separated if more than one, PLUS A NEWLINE. Use "foobar\n" as the data to verify. Alternatively sign the result of printf '%s' foobar >foo.txt which portably omits the newline; some versions of echo support -n for this purpose, some older ones use \c, and some don't support it at all.

FYI BouncyCastle as of version 150 (2013) no longer has org.bouncycastle.openssl.PEMReader; instead you need PEMParser which returns org.bouncycastle.asn1.x509.SubjectPublicKeyInfo which can be converted to key object by org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter or KeyFactory.getInstance(alg).generatePublicKey(new X509EncodedKey(spki.getEncoded())) which is what JcaPEMKeyConverter actually does.

OTOH you can use org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil.generatePublicKeyParameter to replace that fiddling with the parameter pieces; that's what the BC provider interface (as opposed to the lightweight interface) does. Or of course you could just use JCA in the first place and you don't really need BC at all, since OpenSSL publickey formats (unlike privatekey) are consistently compatible with basic Java crypto.

Also BTW openssl dgst needed the -dss1 hack only through version 0.9.8; since version 1.0.0 released 2010 (but not immediately upgraded by many distros and products due to actual or feared incompatibility) you only need -sha1 and a DSA pubkey.