一、ECDSA概述

      椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。ECDSA于1999年成为ANSI标准,并于2000年成为IEEE和NIST标准。它在1998年既已为ISO所接受,并且包含它的其他一些标准亦在ISO的考虑之中。与普通的离散对数问题(discrete logarithm problem  DLP)和大数分解问题(integer factorization problem  IFP)不同,椭圆曲线离散对数问题(elliptic curve discrete logarithm problem  ECDLP)没有亚指数时间的解决方法。因此椭圆曲线密码的单位比特强度要高于其他公钥体制。

二、JDK中对于ECDSA的实现

  特别注意的是:ECDSA签名算法,只是在JDK1.7之后才有实现,最常见的场景是在微软的产品的安装的产品密钥的设计

1、KeyPairGenerator

       KeyPairGenerator 类用于生成公钥和私钥对。密钥对生成器是使用 getInstance 工厂方法(返回一个给定类的实例的静态方法)构造的。
      特定算法的密钥对生成器可以创建能够与此算法一起使用的公钥/私钥对。它还可以将特定于算法的参数与每个生成的密钥关联。
      有两种生成密钥对的方式:与算法无关的方式和特定于算法的方式。
 
下面我们将按照指定ECDSA算法去生成秘钥KeyPairGenerator.getInstance("EC");

2、PublicKey

公用密钥的接口

3、PublicKey

 专用密钥的接口

4、PKCS8EncodedKeySpec

PKCS8EncodedKeySpec类继承EncodedKeySpec类,以编码格式来表示私钥。
PKCS8EncodedKeySpec类使用PKCS#8标准作为密钥规范管理的编码格式

5、Signature

Signature 类用来为应用程序提供数字签名算法功能。数字签名用于确保数字数据的验证和完整性。
 
在所有算法当中,数字签名可以是 NIST 标准的 ECDSA,它使用 ECDSA 和 SHA256。可以将使用 SHA256 消息摘要算法的 ECDSA 算法指定为SHA256withECDSA。

三、Java具体实现

其中ECDSA的实现步骤类似于我们之前学习的RSA数字签名算法

实现原则

所谓的公钥与私钥匙成对出现。 遵从的原则就是“私钥签名、公钥验证”,基于jdk1.8实现。
实现步骤
第一步:初始化化秘钥组,生成ECDSA算法的公钥和私钥
第二步:执行私钥签名, 使用私钥签名,生成私钥签名
第三步:执行公钥签名,生成公钥签名
第四步:使用公钥验证私钥签名

备注:为了还原真实场景,添加了密钥对象与十六进制字符串转换的过程,方便接口双方保存和传递密钥。

1、ECDSA加签验签工具类

package com.qt.base.client.util.test;

import javax.crypto.KeyAgreement;
import javax.xml.bind.DatatypeConverter;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * ECCDSA加签验签工具类
 * @author Administrator
 *
 */
public class ECDSAUtil {
	
	 private static final String SIGNALGORITHMS = "SHA256withECDSA";
	 private static final String ALGORITHM = "EC";
	 private static final String SECP256K1 = "secp256k1";


public static void main(String[] args) throws Exception {

//        生成公钥私钥
        KeyPair keyPair1 = getKeyPair();
        PublicKey publicKey1 = keyPair1.getPublic();
        PrivateKey privateKey1 = keyPair1.getPrivate();
        //密钥转16进制字符串
        String publicKey = HexUtil.encodeHexString(publicKey1.getEncoded());
        String privateKey = HexUtil.encodeHexString(privateKey1.getEncoded());
        System.out.println("生成公钥:"+publicKey);
        System.out.println("生成私钥:"+privateKey);
        //16进制字符串转密钥对象
        PrivateKey privateKey2 = getPrivateKey(privateKey);
        PublicKey publicKey2 = getPublicKey(publicKey);
        //加签验签
        String data="需要签名的数据";
        String signECDSA = signECDSA(privateKey2, data);
        boolean verifyECDSA = verifyECDSA(publicKey2, signECDSA, data);
        System.out.println("验签结果:"+verifyECDSA);

    }

	/**
 	* 加签
 	* @param privateKey 私钥
 	* @param data 数据 
 	* @return
 	*/
    public static String signECDSA(PrivateKey privateKey, String data) {
        String result = "";
        try {
            //执行签名
            Signature signature = Signature.getInstance(SIGNALGORITHMS);
            signature.initSign(privateKey);
            signature.update(data.getBytes());
            byte[] sign = signature.sign();
            return HexUtil.encodeHexString(sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 验签
     * @param publicKey 公钥
     * @param signed 签名
     * @param data 数据
     * @return
     */
    public static boolean verifyECDSA(PublicKey publicKey, String signed, String data) {
        try {
            //验证签名
            Signature signature = Signature.getInstance(SIGNALGORITHMS);
            signature.initVerify(publicKey);
            signature.update(data.getBytes());
            byte[] hex = HexUtil.decode(signed);
            boolean bool = signature.verify(hex);
           // System.out.println("验证:" + bool);
            return bool;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

   /**
    * 从string转private key
    * @param key 私钥的字符串
    * @return
    * @throws Exception
    */
    public static PrivateKey getPrivateKey(String key) throws Exception {
    	
        byte[] bytes = DatatypeConverter.parseHexBinary(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 从string转publicKey
     * @param key 公钥的字符串
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws Exception {
    	
        byte[] bytes = DatatypeConverter.parseHexBinary(key);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }


    

    /**
     * 生成密钥对
     * @return
     * @throws Exception
     */
    public static KeyPair getKeyPair() throws Exception {

        ECGenParameterSpec ecSpec = new ECGenParameterSpec(SECP256K1);
        KeyPairGenerator kf = KeyPairGenerator.getInstance(ALGORITHM);
        kf.initialize(ecSpec, new SecureRandom());
        KeyPair keyPair = kf.generateKeyPair();
        return keyPair;
    }


}

2、16进制字符串与byte数组转换

package com.qt.base.client.util.test;

/**
 * 16进制字符串与byte数组转换
 * @author Administrator
 *
 */
public final class HexUtil {
    private static final char[] HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    public HexUtil() {
    }

    /**
     * byte数组转16进制字符串
     * @param bytes
     * @return
     */
    public static String encodeHexString(byte[] bytes) {
        int nBytes = bytes.length;
        char[] result = new char[2 * nBytes];
        int j = 0;
        byte[] var4 = bytes;
        int var5 = bytes.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            byte aByte = var4[var6];
            result[j++] = HEX[(240 & aByte) >>> 4];
            result[j++] = HEX[15 & aByte];
        }

        return new String(result);
    }

    /**
     * 16进制字符串转byte数组
     * @param s 字符串
     * @return
     */
    public static byte[] decode(CharSequence s) {
        int nChars = s.length();
        if (nChars % 2 != 0) {
            throw new IllegalArgumentException("Hex-encoded string must have an even number of characters");
        } else {
            byte[] result = new byte[nChars / 2];

            for(int i = 0; i < nChars; i += 2) {
                int msb = Character.digit(s.charAt(i), 16);
                int lsb = Character.digit(s.charAt(i + 1), 16);
                if (msb < 0 || lsb < 0) {
                    throw new IllegalArgumentException("Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position");
                }

                result[i / 2] = (byte)(msb << 4 | lsb);
            }

            return result;
        }
    }

}

3、运行结果

生成公钥:3056301006072a8648ce3d020106052b8104000a03420004f8ffbb90eb212778f429b6c3821bf1b17445a6207e28b0f03c38593f5dfa944a8a4afce6b3c07faa3b07ffc73c251b72ddceb5bbbed49e3361d15568e55fd50f
生成私钥:303e020100301006072a8648ce3d020106052b8104000a0427302502010104208acf0acf11730ac9b6553cbf26730d754ac5e0e9c219f7dc97ec51d387f1eb73
验签结果:true