基于BC库使用Java语言学习并实现ECDH算法

ECDH算法(Elliptic Curve Diffie–Hellman Key Exchange)

1.Alice 生成一个随机ECC密钥对:{alicePrivKey,alicePubKey=alicePrivKey*G}
2.Bob生成一个随机ECC密钥对:{bobPrivKey,bobPubKey=bobpriwkey*G}
3.Alice和Bob通过不安全的渠道(例如,通过互联网)交换公钥
4.Alice 计算sharedKey=bobPubKey*alicePrivKey
5.Bob计算sharedKey=alicePubKey*bobPrivKey
6.现在,Alice 和Bob拥有相同的sharedKey==bobPubKey*alicePrivKey==alicePubKey*bobPrivKey


BC库的部分源代码

关键是处理密钥

calculateAgreement是用来计算ECDH(椭圆曲线Diffie-Hellman)协议中的共享密钥的。

代码的步骤如下:

1. 将传入的公钥参数转换为ECPublicKeyParameters类型的对象pub。

2. 检查pub的参数是否与key的参数相同,如果不相同,则抛出一个异常,表示公钥的域参数不正确。

3. 使用pub的Q点与key的D(私钥)进行乘法运算,并对结果进行归一化(normalize),得到点P。

4. 检查点P是否为无穷远点(Infinity),如果是,则抛出一个异常,表示无穷远点不是有效的协议值。

5. 返回点P的仿射X坐标的BigInteger值作为共享密钥。

public BigInteger calculateAgreement(
        CipherParameters pubKey)
    {
        ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
        if (!pub.getParameters().equals(key.getParameters()))
        {
            throw new IllegalStateException("ECDH public key has wrong domain parameters");
        }

        ECPoint P = pub.getQ().multiply(key.getD()).normalize();

        if (P.isInfinity())
        {
            throw new IllegalStateException("Infinity is not a valid agreement value for ECDH");
        }

        return P.getAffineXCoord().toBigInteger();
    }

 multiply(),ECC乘法函数(ECC原理这里就不展开讲了)


normalize 函数,返回类型是ECPoint。该方法的作用是将椭圆曲线上的点规范化。

1.判断当前点是否为无穷远点(isInfinity()方法),如果是,则直接返回当前点。

2.根据椭圆曲线的坐标系统(getCurveCoordinateSystem()方法),进行不同的处理。 如果坐标系统是AFFINE或LAMBDA_AFFINE,表示当前点已经是规范化的,直接返回当前点。

如果坐标系统不是AFFINE或LAMBDA_AFFINE,表示当前点需要进行规范化操作。 首先获取当前点的Z坐标(getZCoord(0)方法),并判断Z坐标是否为1。如果是,则表示当前点已经是规范化的,直接返回当前点。 如果Z坐标不为1,则调用normalize方法,传入Z坐标的倒数(Z1.invert()方法),进行规范化操作,并返回规范化后的点。

public ECPoint normalize()
    {
        if (this.isInfinity())
        {
            return this;
        }

        switch (this.getCurveCoordinateSystem())
        {
        case ECCurve.COORD_AFFINE:
        case ECCurve.COORD_LAMBDA_AFFINE:
        {
            return this;
        }
        default:
        {
            ECFieldElement Z1 = getZCoord(0);
            if (Z1.isOne())
            {
                return this;
            }

            return normalize(Z1.invert());
        }
        }
    }

实现ECDH

最后输出G点、双方公钥私钥和最后的共享密钥,比较共享密钥是否相同。

这里的代码只是经过学习后参考BC库的一些函数实现ECDH基本流程

import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;


public class ECC_DH {
    public static void main(String[] args) throws Exception {
        // 添加 Bouncy Castle 作为安全提供程序
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        // 选择椭圆曲线参数
        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");


        // 生成密钥对
        //创建一个KeyPairGenerator对象,使用参数"ECDH"和"BC"来指定生成ECDH密钥对的算法和提供者
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC");
        //初始化密钥对生成器
        keyPairGenerator.initialize(ecSpec);

        ECPoint G = ecSpec.getG();
        BigInteger n = ecSpec.getN();

        // Alice 和 Bob 分别生成私钥和公钥
        BigInteger dA = new BigInteger(n.bitLength(), new SecureRandom()).mod(n.subtract(BigInteger.ONE)).add(BigInteger.ONE); // 1 <= dA < n
        BigInteger dB = new BigInteger(n.bitLength(), new SecureRandom()).mod(n.subtract(BigInteger.ONE)).add(BigInteger.ONE); // 1 <= dB < n

        ECPoint Qa = G.multiply(dA).normalize(); // 公钥 QA = dA * G
        ECPoint Qb = G.multiply(dB).normalize(); // 公钥 QB = dB * G

        // Alice 使用自己的私钥和 Bob 的公钥计算共享密钥
        ECPoint sharedKeyA = Qb.multiply(dA).normalize(); // 共享密钥 = dB * QA

        // Bob 使用自己的私钥和 Alice 的公钥计算共享密钥
        ECPoint sharedKeyB = Qa.multiply(dB).normalize(); // 共享密钥 = dA * QB

        System.out.println("Alice's key pair: " + dA);
        System.out.println("Bob's key pair:"+ dB);
        System.out.println("G="+G);
        System.out.println("\n");
        System.out.println("A的公钥:"+ Qa);
        System.out.println("B的公钥:"+ Qb);

        BigInteger a=sharedKeyA.getAffineXCoord().toBigInteger();
        BigInteger b=sharedKeyB.getAffineXCoord().toBigInteger();
        //输出共享密钥
        System.out.println("Shared key A: " + a);
        System.out.println("Shared key B: " + b);
        System.out.println("比较结果:" + sharedKeyA.equals(sharedKeyB));
    }
}

 其他

写完后出现了最后共享密钥不同的结果,经过分析BC库源代码发现计算时没有规范化,在计算时加normalize()

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是 Java 实现 ECDH 算法的示例代码: ```java import java.security.*; import java.security.spec.*; import javax.crypto.*; import javax.crypto.spec.*; public class ECDHDemo { public static void main(String[] args) throws Exception { // 创建椭圆曲线密钥对生成器 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); // 使用 secp256r1 椭圆曲线 kpg.initialize(new ECGenParameterSpec("secp256r1")); // 生成密钥对 KeyPair kp1 = kpg.generateKeyPair(); KeyPair kp2 = kpg.generateKeyPair(); // 获取公钥和私钥 PublicKey pubKey1 = kp1.getPublic(); PrivateKey priKey1 = kp1.getPrivate(); PublicKey pubKey2 = kp2.getPublic(); PrivateKey priKey2 = kp2.getPrivate(); // ECDH 密钥协商 KeyAgreement ka1 = KeyAgreement.getInstance("ECDH"); ka1.init(priKey1); ka1.doPhase(pubKey2, true); byte[] secret1 = ka1.generateSecret(); KeyAgreement ka2 = KeyAgreement.getInstance("ECDH"); ka2.init(priKey2); ka2.doPhase(pubKey1, true); byte[] secret2 = ka2.generateSecret(); // 验证协商的密钥是否相同 if (MessageDigest.isEqual(secret1, secret2)) { System.out.println("ECDH 密钥协商成功!"); } else { System.out.println("ECDH 密钥协商失败!"); } } } ``` 在上面的示例代码中,我们使用 `secp256r1` 椭圆曲线生成密钥对,并使用 ECDH 算法进行密钥协商。具体步骤如下: 1. 通过 `KeyPairGenerator` 生成密钥对。 2. 获取公钥和私钥。 3. 创建 `KeyAgreement` 实例,初始化为自己的私钥。 4. 使用 `doPhase` 方法将对方的公钥作为输入,并执行密钥协商。 5. 使用 `generateSecret` 方法生成协商的密钥。 6. 验证双方协商的密钥是否相同,如果相同则密钥协商成功。 值得注意的是,ECDH 密钥协商只能协商出一个共享密钥,而且这个共享密钥不是公钥或私钥,它只能用于对称加密。因此,在实际应用中,我们通常需要使用协商出的共享密钥来加密通信内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值