xkcd
xkcd

Reputation: 472

How to correctly encode DH parameters using BouncyCastle in Java?

I am trying to reproduce the output of "openssl dhparam -out dh1024.pem 1024" command programatically in Java. The code snippet is following:-

            DHParametersGenerator generator = new DHParametersGenerator();
            generator.init(1024, 0, new SecureRandom());
            DHParameters params = generator.generateParameters();
            // Generator G is set as random in params, but it has to be 2 to conform to openssl
            DHParameters realParams = new DHParameters(params.getP(), BigInteger.valueOf(2));

            byte[] p = realParams.getP().toByteArray();
            byte[] g = realParams.getG().toByteArray();
            byte[] l = new byte[(byte) realParams.getL()];
            byte[] pgl = new byte[p.length+g.length+l.length];

            System.arraycopy(p, 0, pgl, 0, p.length);
            System.arraycopy(g, 0, pgl, p.length, g.length);
            System.arraycopy(l, 0, pgl, p.length+g.length, l.length);

So basically I am concatenating the values of P,G and L parameters in a byte array "pgl" and then saving it in a file using the PEMWriter class from BC. But when I try to use it via openssl, I get the following error:-

Cannot load DH parameters from /etc/openvpn/easy-rsa/keys/dh1024.pem: error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long: error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header: error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error: error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib

.... which leads me to believe that I am encoding the DH Parameters wrongly, but I cannot find anywhere the correct way to encode this. Can anyone help me in this? I've been bouncing my head against the castle wall fro many days now but to no avail .... please help :(

Upvotes: 0

Views: 4060

Answers (2)

xkcd
xkcd

Reputation: 472

Thanks GregS, your solution works but I eventually solved it using standard Java plus PemWriter from BC, although you cannot set the Generator G = 2 with this approach, but its still works both with openssl and Java, which was my initial purpose anyway :)

import java.io.FileWriter;
import java.io.IOException;
import java.security.AlgorithmParameterGenerator;
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;

public class DHCredentials {

    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException, IOException 
        {
        DHCredentials dhc = new DHCredentials();
        System.out.println("This may take a long time ...");

        dhc.saveDHParams("C:\\xxxDH.txt", dhc.genDHParams());

        System.out.println("Done");
    }

    public byte[] genDHParams() throws IOException
    {
        AlgorithmParameterGenerator paramGen = null;
        try 
        {
            paramGen = AlgorithmParameterGenerator.getInstance("DH");
            paramGen.init(1024, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        AlgorithmParameters params = paramGen.generateParameters();
        return params.getEncoded();
    }

    public void saveDHParams(String filePath, byte[] DEREncodedDHParams)
    {
        PemWriter pemWrt;

        try {
            pemWrt = new PemWriter(new FileWriter(filePath));
            pemWrt.writeObject(new PemObject("DH PARAMETERS", DEREncodedDHParams));
            pemWrt.flush();
            pemWrt.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Upvotes: 3

President James K. Polk
President James K. Polk

Reputation: 41956

Here is an example. Note that you cannot set the certainty argument to 0 in generator.init() or you won't get a prime! Most of this code I figured out just by looking at the Bouncycastle source code, for example look at the PEMWriter class.

import java.math.BigInteger;
import java.security.SecureRandom;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.generators.DHParametersGenerator;
import org.bouncycastle.crypto.params.DHParameters;
import org.bouncycastle.util.encoders.Base64;

public class OpenSSLDHParamClone
{

    public static void main(String[] args) throws Exception
    {
        DHParametersGenerator generator = new DHParametersGenerator();
        generator.init(1024, 80, new SecureRandom());
        DHParameters params = generator.generateParameters();
        // Generator G is set as random in params, but it has to be 2 to conform to openssl
        DHParameters realParams = new DHParameters(params.getP(), BigInteger.valueOf(2));
        ASN1EncodableVector seq = new ASN1EncodableVector();
        seq.add(new DERInteger(realParams.getP()));
        seq.add(new DERInteger(realParams.getG()));
        byte [] derEncoded = new DERSequence(seq).getDEREncoded();
        System.out.println("-----BEGIN DH PARAMETERS-----");
        String b64Encoded = new String(Base64.encode(derEncoded), "US-ASCII");
        while (b64Encoded.length() > 0) {
            int subStringLength = Math.min(64, b64Encoded.length());
            System.out.println(b64Encoded.substring(0, subStringLength));
            b64Encoded = b64Encoded.substring(subStringLength);
        }
        System.out.println("-----END DH PARAMETERS-----");
    }
}

Upvotes: 4

Related Questions