ECC加密和签名的Java实现

一、简介

ECC是椭圆曲线算法,其加密算法叫ECIES,签名算法叫ECDSA。

JDK并不支持ECC算法,可以使用BouncyCastle库,下载地址:http://www.bouncycastle.org/latest_releases.html

选择最新的provider下载即可(bcprov-jdk15on-162.jar)。

通过以下代码可以输出BouncyCastle库支持的算法

    private static void printProvider() {
        Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
        for (Provider.Service service : provider.getServices()) {
            System.out.println(service.getType() + ": "
                    + service.getAlgorithm());
        }
    }

其中包含以下信息,可以看到有加密算法ECIES,有签名算法ECDSA(并且指定了长度)

 

二、问题

1. Invalid KeySize 的异常

这是由于JDK对keysize有最低安全要求,不过在jdk8最新版本(本人使用的是jdk8-211)已经取消了这个限制,如果有遇到这个问题,使用最新的jdk版本即可(比如你用jdk8-66,则替换为jdk8-211),或者也可以百度一下,网上有其他解决方法。

2. Unknown KeySize 的异常

这是由于BouncyCastle库对keysize有指定要求的,可以看BouncyCastle库中的类:

org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.class

其中大概在173行左右有这么一个静态代码块:

        static {
            ecParameters.put(Integers.valueOf(192), new ECGenParameterSpec("prime192v1"));
            ecParameters.put(Integers.valueOf(239), new ECGenParameterSpec("prime239v1"));
            ecParameters.put(Integers.valueOf(256), new ECGenParameterSpec("prime256v1"));
            ecParameters.put(Integers.valueOf(224), new ECGenParameterSpec("P-224"));
            ecParameters.put(Integers.valueOf(384), new ECGenParameterSpec("P-384"));
            ecParameters.put(Integers.valueOf(521), new ECGenParameterSpec("P-521"));
        }

 从中可以看出,其支持的keysize就这么几个:192, 239, 256, 224, 384, 521

 

3. Cipher的使用问题

网上有些博文贴的代码是用NullCipher,但是无法加密,使用NullCipher加密得到的数据还是原文(非密文),实际没有加密,所以无效。事实上,调用Cipher.getInstance("ECIES", "BC"); 来构造的Cipher实例时是返回new Cipher(CipherSpi);这里传入的CipherSpi其实是BouncyCastle库提供的IESCipher(org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher.class),

因此要用Cipher.getInstance("ECIES", "BC"); 来获取含IESCipher实例的Cipher对象,才能正常加解密

 

三、实现代码

package crypto;

import javax.crypto.Cipher;
import java.io.File;
import java.io.FileInputStream;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.util.Base64;

public class EccUtils {
    /**
     * @see org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.ecParameters (line #173)
     * 192, 224, 239, 256, 384, 521
     * */
    private final static int KEY_SIZE = 256;//bit
    private final static String SIGNATURE = "SHA256withECDSA";

    static {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }

    private static void printProvider() {
        Provider provider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
        for (Provider.Service service : provider.getServices()) {
            System.out.println(service.getType() + ": "
                    + service.getAlgorithm());
        }
    }

    public static void main(String[] args) {
        try {
            KeyPair keyPair = getKeyPair();
            ECPublicKey pubKey = (ECPublicKey) keyPair.getPublic();
            ECPrivateKey priKey = (ECPrivateKey) keyPair.getPrivate();
            //System.out.println("[pubKey]:\n" + getPublicKey(keyPair));
            //System.out.println("[priKey]:\n" + getPrivateKey(keyPair));

            //测试文本
            String content = "abcdefg";

            //加密
            byte[] cipherTxt = encrypt(content.getBytes(), pubKey);
            //解密
            byte[] clearTxt = decrypt(cipherTxt, priKey);
            //打印
            System.out.println("content:" + content);
            System.out.println("cipherTxt["+cipherTxt.length+"]:" + new String(cipherTxt));
            System.out.println("clearTxt:" + new String(clearTxt));

            //签名
            byte[] sign = sign(content, priKey);
            //验签
            boolean ret = verify(content, sign, pubKey);
            //打印
            System.out.println("content:" + content);
            System.out.println("sign["+sign.length+"]:" + new String(sign));
            System.out.println("verify:" + ret);

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("[main]-Exception:" + e.toString());
        }
    }

    //生成秘钥对
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");//BouncyCastle
        keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        return keyPair;
    }

    //获取公钥(Base64编码)
    public static String getPublicKey(KeyPair keyPair) {
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
        byte[] bytes = publicKey.getEncoded();
        return Base64.getEncoder().encodeToString(bytes);
    }

    //获取私钥(Base64编码)
    public static String getPrivateKey(KeyPair keyPair) {
        ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
        byte[] bytes = privateKey.getEncoded();
        return Base64.getEncoder().encodeToString(bytes);
    }

    //公钥加密
    public static byte[] encrypt(byte[] content, ECPublicKey pubKey) throws Exception {
        Cipher cipher = Cipher.getInstance("ECIES", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        return cipher.doFinal(content);
    }

    //私钥解密
    public static byte[] decrypt(byte[] content, ECPrivateKey priKey) throws Exception {
        Cipher cipher = Cipher.getInstance("ECIES", "BC");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        return cipher.doFinal(content);
    }

    //私钥签名
    public static byte[] sign(String content, ECPrivateKey priKey) throws Exception {
        //这里可以从证书中解析出签名算法名称
        //Signature signature = Signature.getInstance(getSigAlgName(pubCert));
        Signature signature = Signature.getInstance(SIGNATURE);//"SHA256withECDSA"
        signature.initSign(priKey);
        signature.update(content.getBytes());
        return signature.sign();
    }

    //公钥验签
    public static boolean verify(String content, byte[] sign, ECPublicKey pubKey) throws Exception {
        //这里可以从证书中解析出签名算法名称
        //Signature signature = Signature.getInstance(getSigAlgName(priCert));
        Signature signature = Signature.getInstance(SIGNATURE);//"SHA256withECDSA"
        signature.initVerify(pubKey);
        signature.update(content.getBytes());
        return signature.verify(sign);
    }

    /**
     * 解析证书的签名算法,单独一本公钥或者私钥是无法解析的,证书的内容远不止公钥或者私钥
     * */
    private static String getSigAlgName(File certFile) throws Exception {
        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
        X509Certificate x509Certificate = (X509Certificate) cf.generateCertificate(new FileInputStream(certFile));
        return x509Certificate.getSigAlgName();
    }
}

 

 

 

 

 

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
ECC(Elliptic Curve Cryptography)椭圆曲线加密算法是一种公钥加密算法,它基于椭圆曲线上的离散对数问题。在Java中,可以使用Bouncy Castle库来实现ECC加密算法。下面是一个简单的示例代码: ```java import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.Security; import java.security.Signature; public class ECCExample { public static void main(String[] args) throws Exception { // 添加Bouncy Castle作为Provider Security.addProvider(new BouncyCastleProvider()); // 生成密钥对 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC"); keyPairGenerator.initialize(256); // 设置密钥长度 KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 获取私钥和公钥 byte[] privateKey = keyPair.getPrivate().getEncoded(); byte[] publicKey = keyPair.getPublic().getEncoded(); // 签名 Signature signature = Signature.getInstance("SHA256withECDSA", "BC"); signature.initSign(keyPair.getPrivate()); byte[] message = "Hello, ECC!".getBytes(); signature.update(message); byte[] signatureBytes = signature.sign(); // 验证签名 Signature verifier = Signature.getInstance("SHA256withECDSA", "BC"); verifier.initVerify(keyPair.getPublic()); verifier.update(message); boolean verified = verifier.verify(signatureBytes); System.out.println("Private Key: " + bytesToHex(privateKey)); System.out.println("Public Key: " + bytesToHex(publicKey)); System.out.println("Signature Verified: " + verified); } // 将字节数组转换为十六进制字符串 private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } } ``` 这段代码使用Bouncy Castle库生成了一个256位的ECC密钥对,并进行了签名和验证。你可以根据自己的需求修改密钥长度和签名算法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值