20

Can you help me to find a simple tutorial of how sign a string using ECDSA algorithm in java. But without using any third-party libraries like bouncycastle. Just JDK 7. I found it difficult to search a simple example, I'm new to cryptography.


import java.io.*;
import java.security.*;

public class GenSig {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        /*
         * Generate a DSA signature
         */

        try {

            /*
             * Generate a key pair
             */

            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");

            keyGen.initialize(1024, random);

            KeyPair pair = keyGen.generateKeyPair();
            PrivateKey priv = pair.getPrivate();
            PublicKey pub = pair.getPublic();

            /*
             * Create a Signature object and initialize it with the private key
             */

            Signature dsa = Signature.getInstance("SHA1withDSA", "SUN");

            dsa.initSign(priv);

            String str = "This is string to sign";
            byte[] strByte = str.getBytes();
            dsa.update(strByte);

            /*
             * Now that all the data to be signed has been read in, generate a
             * signature for it
             */

            byte[] realSig = dsa.sign();
            System.out.println("Signature: " + new String(realSig));


        } catch (Exception e) {
            System.err.println("Caught exception " + e.toString());
        }
    }
}

How to modify it for ECDSA?

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
user1379574
  • 689
  • 4
  • 11
  • 23
  • Look for any example that uses DSA, but use these algorithms instead: [EC provider](http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#SunEC) – President James K. Polk Jul 05 '12 at 11:00
  • for example if I use this tutorial, what should I change here? http://www.java2s.com/Code/Java/Security/Testthesignature.htm or using this one http://docs.oracle.com/javase/tutorial/security/apisign/step1.html should I place instead of dsa ecdsa? – user1379574 Jul 05 '12 at 11:12

2 Answers2

30

Here is small example based on your example.

NOTE: this is the original code for this answer, please see the next code snippet for an updated version.

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;

public class ECDSAExample {

    public static void main(String[] args) throws Exception {
        /*
         * Generate an ECDSA signature
         */

        /*
         * Generate a key pair
         */

        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");

        keyGen.initialize(256, random);

        KeyPair pair = keyGen.generateKeyPair();
        PrivateKey priv = pair.getPrivate();
        PublicKey pub = pair.getPublic();

        /*
         * Create a Signature object and initialize it with the private key
         */

        Signature dsa = Signature.getInstance("SHA1withECDSA");

        dsa.initSign(priv);

        String str = "This is string to sign";
        byte[] strByte = str.getBytes("UTF-8");
        dsa.update(strByte);

        /*
         * Now that all the data to be signed has been read in, generate a
         * signature for it
         */

        byte[] realSig = dsa.sign();
        System.out.println("Signature: " + new BigInteger(1, realSig).toString(16));

    }
}

UPDATE: Here is slightly improved example removing deprecated algorithms. It also explicitly requests the NIST P-256 curve using the SECG notation "secp256r1" as specified in RFC 8422.

import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;

public class ECDSAExample {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws Exception {
        /*
         * Generate an ECDSA signature
         */

        /*
         * Generate a key pair
         */

        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");

        keyGen.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());

        KeyPair pair = keyGen.generateKeyPair();
        PrivateKey priv = pair.getPrivate();
        PublicKey pub = pair.getPublic();

        /*
         * Create a Signature object and initialize it with the private key
         */

        Signature ecdsa = Signature.getInstance("SHA256withECDSA");

        ecdsa.initSign(priv);

        String str = "This is string to sign";
        byte[] strByte = str.getBytes("UTF-8");
        ecdsa.update(strByte);

        /*
         * Now that all the data to be signed has been read in, generate a
         * signature for it
         */

        byte[] realSig = ecdsa.sign();
        System.out.println("Signature: " + new BigInteger(1, realSig).toString(16));

    }
}
Community
  • 1
  • 1
President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
  • 1
    What curve would that be? P-256? – Maarten Bodewes Jul 05 '12 at 23:46
  • 2
    I was afraid someone would ask that. I don't know, P-256 would be my guess though. – President James K. Polk Jul 05 '12 at 23:47
  • 3
    Running it in the debugger and cheating a little reveals: secp256r1 [NIST P-256, X9.62 prime256v1] – President James K. Polk Jul 05 '12 at 23:57
  • 1
    Oh, I just cast it to the ECPrivateKey and got the curve :) I like to explicitly name the curve though. – Maarten Bodewes Jul 05 '12 at 23:58
  • thanks a lot for analysis. I will try and write a bit later=) – user1379574 Jul 06 '12 at 08:05
  • @GregS you know the dangers of calling String.getBytes() without an explicit character set... – Jumbogram Jul 07 '12 at 02:48
  • @Jumbogram I just did a copy and paste of the OPs example and missed it. Fixed now. – President James K. Polk Jul 07 '12 at 11:15
  • 1
    Does it support SHA-256 too? The security margin of SHA-1 against collisions is a bit on the low side, so I'd recommend SHA-256 where possible. P256 on the other hand is a fine curve, so no complaints about that part. – CodesInChaos Jul 09 '12 at 21:04
  • 1
    @CodeInChaos: It does. I didn't actually verify the signatures, but the example runs if you use instead `Signature dsa = Signature.getInstance("SHA256withECDSA");` or any of the SHA2 algorithms in place of SHA256. – President James K. Polk Jul 09 '12 at 23:45
  • 1
    I do want to thank you for this answer. I have been banging my head on a problem for the past several hours with using ECDSA and then the keystore camplaining that the algorithms dont match...Now it works, just wanted to share my gratitude. – sparticvs Oct 02 '14 at 14:04
  • @MaartenBodewes `System.err.println("Public Key: " + pub.toString()); ` shows Public Key: Sun EC public key, 256 bits public x coord: 19501778814434347530334294264752414149486648444804770040426369360038920483177 public y coord: 114925210518466507390625110183293395999411704799479237295270124024949219180290 parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7) – Akintayo Olusegun Sep 13 '18 at 16:38
7
class ECCCipher {
    @Override
    public byte[] sign(PrivateKey privateKey, String message) throws Exception {
        Signature signature = Signature.getInstance("SHA1withECDSA");
        signature.initSign(privateKey);

        signature.update(message.getBytes());

        return signature.sign();
    }

    @Override
    public boolean verify(PublicKey publicKey, byte[] signed, String message) throws Exception {
        Signature signature = Signature.getInstance("SHA1withECDSA");
        signature.initVerify(publicKey);

        signature.update(message.getBytes());

        return signature.verify(signed);
    }
}

========================

public class ECCCipherTest {

private final KeyPairGenerator keygen;

public ECCCipherTest() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
    Security.addProvider(new BouncyCastleProvider());
    this.keygen = KeyPairGenerator.getInstance("ECDSA", "BC");
    keygen.initialize(new ECGenParameterSpec("brainpoolP384r1"));
}

@Test
public void ECC_CipherTest_1() throws Exception {
    String message = "hello world";

    ICipher<PrivateKey, PublicKey> cipher = new ECCCipher();
    KeyPair keyPair = keygen.generateKeyPair();

    byte[] encrypted = cipher.sign(keyPair.getPrivate(), message);

    Assert.assertTrue(cipher.verify(keyPair.getPublic(), encrypted, message));
}

}

this is a small code snippet from my project. it works for me. I have included one junit test as well; hopefully this helps.

just in case anyone wonders how we load the private key and pubkey: (note: privKey is the byte array representing the BigInteger in java, and the pubKey is the curve point in binary format)

    @Override
public PrivateKey generatePrivateKey(byte[] keyBin) throws InvalidKeySpecException, NoSuchAlgorithmException {
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
    KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
    ECNamedCurveSpec params = new ECNamedCurveSpec("secp256k1", spec.getCurve(), spec.getG(), spec.getN());
    ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger(keyBin), params);
    return kf.generatePrivate(privKeySpec);
}

@Override
public PublicKey generatePublicKey(byte[] keyBin) throws InvalidKeySpecException, NoSuchAlgorithmException {
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1");
    KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
    ECNamedCurveSpec params = new ECNamedCurveSpec("secp256k1", spec.getCurve(), spec.getG(), spec.getN());
    ECPoint point =  ECPointUtil.decodePoint(params.getCurve(), keyBin);
    ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
    return kf.generatePublic(pubKeySpec);
}
linehrr
  • 1,668
  • 19
  • 24