ECC密钥协商详解及实现与应用 ECC密钥协商原理 ECC(椭圆曲线密码学)密钥协商基于椭圆曲线离散对数问题,最常用的是ECDH(椭圆曲线Diffie-Hellman)算法。
ECDH密钥协商过程
参数选择 :双方约定使用相同的椭圆曲线参数(如secp256r1)
密钥对生成 :双方各自生成ECC密钥对(私钥d和公钥Q=d×G,其中G是基点)
公钥交换 :双方交换各自的公钥
共享密钥计算 :
Alice计算:S = d_A × Q_B(d_A是Alice的私钥,Q_B是Bob的公钥)
Bob计算:S = d_B × Q_A(d_B是Bob的私钥,Q_A是Alice的公钥)
密钥派生 :对共享密钥S应用密钥派生函数(KDF)生成最终会话密钥
由于椭圆曲线的性质,双方计算出的共享密钥S是相同的:d_A × Q_B = d_A × (d_B × G) = d_B × (d_A × G) = d_B × Q_A
ECC 密钥协商(ECDH)全过程详解 ECDH 是一种密钥协商协议,允许双方在不安全的信道上,通过交换一些公开信息,独立地计算出一个相同的共享秘密(Shared Secret)。这个共享秘密随后可以被用作对称加密的密钥(如 AES 的密钥)。其核心在于利用了椭圆曲线离散对数问题的数学特性。
参与方:
Alice :协商的发起方。
Bob :协商的响应方。
前提条件: 双方事先约定好使用同一个椭圆曲线 (例如,在 Golang 中常用的 P-256
, P-384
, P-521
)。
全过程步骤:
生成密钥对(Key Pair Generation)
Alice :随机生成一个私钥 dA
(一个保密的超大整数)。利用椭圆曲线乘法,计算其对应的公钥 QA = dA * G
,其中 G
是椭圆曲线上的一个公开的基点(Generator Point)。
Bob :同样,随机生成自己的私钥 dB
,并计算对应的公钥 QB = dB * G
。
交换公钥(Public Key Exchange)
Alice 将自己的公钥 QA
发送给 Bob 。这个通道可以是不安全的 ,即使被窃听也无妨。
Bob 将自己的公钥 QB
发送给 Alice 。同样,这个通道也可以是不安全的。
计算共享秘密(Shared Secret Calculation)
Alice 收到 Bob 的公钥 QB
后,用自己的私钥 dA
进行计算:S = dA * QB
。
因为 QB = dB * G
,所以 S = dA * (dB * G) = (dA * dB) * G
。
Bob 收到 Alice 的公钥 QA
后,用自己的私钥 dB
进行计算:S = dB * QA
。
因为 QA = dA * G
,所以 S = dB * (dA * G) = (dB * dA) * G
。
根据椭圆曲线乘法的交换律,(dA * dB) * G = (dB * dA) * G
。因此,双方计算出的点 S
是同一个点 ,即 (x, y)
坐标相同。
派生对称密钥(Symmetric Key Derivation)
这个共享秘密 S
是一个椭圆曲线上的点(包含 X, Y 坐标)。直接将其字节序列作为密钥可能不够安全或长度不合适。
双方会使用一个密钥派生函数(KDF, Key Derivation Function) 来处理这个共享秘密。通常,只会使用点的 X 坐标(或 X 和 Y 坐标的某种组合)作为 KDF 的输入。
KDF(如 HKDF)会将这个输入“拉伸”和“混合”,生成一个或多个长度固定、密码学强度高的密钥字节序列。这个最终的字节序列就是双方用于对称加密(如 AES-256)的共享密钥 。
安全性: 窃听者即使获得了公开的 G
, QA
, QB
,也无法计算出 dA
或 dB
(椭圆曲线离散对数难题),因此也无法计算出共享秘密 S
。
Java实现ECC密钥协商 以下是使用Java标准库实现ECDH密钥协商的完整示例:
import javax.crypto.KeyAgreement;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.security.*;import java.security.spec.ECGenParameterSpec;import java.security.spec.X509EncodedKeySpec;import java.util.Base64;public class ECCKeyExchange { public static KeyPair generateECCKeyPair () throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC" ); ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1" ); keyGen.initialize(ecSpec, new SecureRandom()); return keyGen.generateKeyPair(); } public static byte [] performKeyAgreement(PrivateKey privateKey, PublicKey publicKey) throws Exception { KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH" ); keyAgreement.init(privateKey); keyAgreement.doPhase(publicKey, true ); return keyAgreement.generateSecret(); } public static SecretKey deriveAESKey (byte [] sharedSecret, int keySize) throws Exception { MessageDigest digest = MessageDigest.getInstance("SHA-256" ); byte [] hashedSecret = digest.digest(sharedSecret); byte [] derivedKey = new byte [keySize / 8 ]; System.arraycopy(hashedSecret, 0 , derivedKey, 0 , derivedKey.length); return new SecretKeySpec(derivedKey, "AES" ); } public static String publicKeyToString (PublicKey publicKey) { return Base64.getEncoder().encodeToString(publicKey.getEncoded()); } public static PublicKey publicKeyFromString (String keyString) throws Exception { byte [] keyBytes = Base64.getDecoder().decode(keyString); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("EC" ); return keyFactory.generatePublic(keySpec); } public static void main (String[] args) throws Exception { System.out.println("=== ECC密钥协商演示 ===" ); KeyPair aliceKeyPair = generateECCKeyPair(); PublicKey alicePublicKey = aliceKeyPair.getPublic(); PrivateKey alicePrivateKey = aliceKeyPair.getPrivate(); KeyPair bobKeyPair = generateECCKeyPair(); PublicKey bobPublicKey = bobKeyPair.getPublic(); PrivateKey bobPrivateKey = bobKeyPair.getPrivate(); System.out.println("Alice和Bob已生成各自的ECC密钥对" ); String alicePublicKeyStr = publicKeyToString(alicePublicKey); String bobPublicKeyStr = publicKeyToString(bobPublicKey); System.out.println("公钥交换中..." ); PublicKey receivedBobPublicKey = publicKeyFromString(bobPublicKeyStr); byte [] aliceSharedSecret = performKeyAgreement(alicePrivateKey, receivedBobPublicKey); SecretKey aliceAESKey = deriveAESKey(aliceSharedSecret, 256 ); PublicKey receivedAlicePublicKey = publicKeyFromString(alicePublicKeyStr); byte [] bobSharedSecret = performKeyAgreement(bobPrivateKey, receivedAlicePublicKey); SecretKey bobAESKey = deriveAESKey(bobSharedSecret, 256 ); if (java.util.Arrays.equals(aliceAESKey.getEncoded(), bobAESKey.getEncoded())) { System.out.println("✓ 密钥协商成功!双方拥有相同的AES密钥" ); System.out.println("共享密钥哈希: " + Base64.getEncoder().encodeToString( MessageDigest.getInstance("SHA-256" ).digest(aliceSharedSecret))); System.out.println("AES密钥: " + Base64.getEncoder().encodeToString(aliceAESKey.getEncoded())); demonstrateKeyUsage(aliceAESKey, bobAESKey); } else { System.out.println("✗ 密钥协商失败" ); } } public static void demonstrateKeyUsage (SecretKey aliceKey, SecretKey bobKey) throws Exception { System.out.println("\n=== 使用协商的密钥进行加密通信 ===" ); String originalMessage = "这是一条需要加密的秘密消息" ; System.out.println("原始消息: " + originalMessage); javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding" ); byte [] iv = new byte [12 ]; SecureRandom random = new SecureRandom(); random.nextBytes(iv); cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aliceKey, new javax.crypto.spec.GCMParameterSpec(128 , iv)); byte [] encryptedMessage = cipher.doFinal(originalMessage.getBytes("UTF-8" )); byte [] messageToSend = new byte [iv.length + encryptedMessage.length]; System.arraycopy(iv, 0 , messageToSend, 0 , iv.length); System.arraycopy(encryptedMessage, 0 , messageToSend, iv.length, encryptedMessage.length); System.out.println("加密后消息: " + Base64.getEncoder().encodeToString(messageToSend)); byte [] receivedIv = new byte [12 ]; byte [] receivedEncryptedMessage = new byte [messageToSend.length - 12 ]; System.arraycopy(messageToSend, 0 , receivedIv, 0 , 12 ); System.arraycopy(messageToSend, 12 , receivedEncryptedMessage, 0 , messageToSend.length - 12 ); cipher.init(javax.crypto.Cipher.DECRYPT_MODE, bobKey, new javax.crypto.spec.GCMParameterSpec(128 , receivedIv)); byte [] decryptedMessage = cipher.doFinal(receivedEncryptedMessage); System.out.println("解密后消息: " + new String(decryptedMessage, "UTF-8" )); } }
协商密钥的使用方式 协商出的共享密钥通常用于对称加密算法(如AES),具体应用方式包括:
1. 直接作为会话密钥 将协商出的密钥直接用于加密通信,如示例中演示的AES加密。
2. 密钥派生 使用密钥派生函数(KDF)从共享密钥派生出多个子密钥:
加密密钥:用于加密数据
认证密钥:用于消息认证码(MAC)
IV生成密钥:用于生成初始化向量
3. 安全协议中的应用 在TLS、IPSec等安全协议中,ECDH协商的密钥用于:
生成预主密钥(pre-master secret)
与客户端和服务器随机数结合生成主密钥(master secret)
从主密钥派生出实际用于加密和认证的密钥
4. 前向安全性保障 ECDH的一个重要优势是提供前向安全性:即使长期私钥在未来被泄露,过去的通信会话也不会被解密,因为每次会话都使用临时密钥对。
Golang 实现ECC密钥协商 Golang 的标准库 crypto/ecdsa
和 crypto/elliptic
提供了 ECC 相关的操作,但 crypto/ecdsa
主要用于数字签名。对于密钥协商,我们使用更通用的 crypto/ecdh
包(Go 1.20+ 引入,更现代且专门用于 ECDH)。
package mainimport ( "crypto/ecdh" "crypto/rand" "encoding/hex" "fmt" "log" ) func main () { curve := ecdh.P256() alicePrivateKey, err := curve.GenerateKey(rand.Reader) if err != nil { log.Fatal("Failed to generate Alice's private key:" , err) } alicePublicKey := alicePrivateKey.PublicKey() bobPrivateKey, err := curve.GenerateKey(rand.Reader) if err != nil { log.Fatal("Failed to generate Bob's private key:" , err) } bobPublicKey := bobPrivateKey.PublicKey() fmt.Printf("Alice's Public Key: %s...\n" , hex.EncodeToString(alicePublicKey.Bytes())[:64 ]) fmt.Printf("Bob's Public Key: %s...\n" , hex.EncodeToString(bobPublicKey.Bytes())[:64 ]) fmt.Println("(Note: Public keys are being exchanged over an insecure channel)" ) aliceSharedSecret, err := alicePrivateKey.ECDH(bobPublicKey) if err != nil { log.Fatal("Alice failed to compute shared secret:" , err) } bobSharedSecret, err := bobPrivateKey.ECDH(alicePublicKey) if err != nil { log.Fatal("Bob failed to compute shared secret:" , err) } fmt.Printf("\nAlice's Shared Secret: %s\n" , hex.EncodeToString(aliceSharedSecret)) fmt.Printf("Bob's Shared Secret: %s\n" , hex.EncodeToString(bobSharedSecret)) if hex.EncodeToString(aliceSharedSecret) == hex.EncodeToString(bobSharedSecret) { fmt.Println("\n✅ Success! Shared secrets match." ) } else { fmt.Println("\n❌ Error! Shared secrets do not match." ) } aesKeyForAlice := deriveAESKey(aliceSharedSecret) aesKeyForBob := deriveAESKey(bobSharedSecret) fmt.Printf("\nDerived AES Key (Alice): %s\n" , hex.EncodeToString(aesKeyForAlice)) fmt.Printf("Derived AES Key (Bob): %s\n" , hex.EncodeToString(aesKeyForBob)) } func deriveAESKey (sharedSecret []byte ) []byte { key := make ([]byte , 32 ) copy (key, sharedSecret[:32 ]) return key }
运行结果示例: Alice's Public Key: 04b89f25d75e79f27d26d6e2a63f4e2e7c6e6e8e7e6e6e7e6e6e7e6e6e7e6e6e7e6e6e7e6e6e7e6e6e7e6e6e7e6e6e7e6e6e7e6e6e7... Bob's Public Key: 04a76f45c23e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5... (Note: Public keys are being exchanged over an insecure channel) Alice's Shared Secret: 5a1e7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b Bob's Shared Secret: 5a1e7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b ✅ Success! Shared secrets match. Derived AES Key (Alice): 5a1e7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b Derived AES Key (Bob): 5a1e7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b
Golang中协商出来的密钥是如何使用的 协商出的最终密钥(如上面代码中的 aesKeyForAlice
和 aesKeyForBob
)是一个字节序列([]byte
),它作为对称密钥 使用。最常见的用途是:
1. 用于对称加密/解密(例如 AES):
一旦 Alice 和 Bob 有了相同的 AES 密钥,他们就可以使用任何 AES 操作模式(如 GCM、CBC)来加密通信。
Alice 想给 Bob 发送一条消息:
Alice 使用 aesKeyForAlice
和 AES-GCM 算法加密明文 plainText
,得到密文 ciphertext
和一个认证标签 authTag
。
Alice 将 (ciphertext, authTag)
发送给 Bob。
Bob 收到消息后:
Bob 使用 aesKeyForBob
(与 aesKeyForAlice
相同)、收到的 ciphertext
和 authTag
,使用 AES-GCM 算法进行解密和认证。
如果认证成功(说明消息未被篡改),则得到原始的 plainText
。
Golang 代码片段示例(使用 crypto/aes
和 crypto/cipher
):
func aesGCMEncrypt (key, plaintext []byte ) ([]byte , []byte , error) { block, err := aes.NewCipher(key) if err != nil { return nil , nil , err } aesgcm, err := cipher.NewGCM(block) if err != nil { return nil , nil , err } nonce := make ([]byte , aesgcm.NonceSize()) if _, err := rand.Read(nonce); err != nil { return nil , nil , err } ciphertext := aesgcm.Seal(nil , nonce, plaintext, nil ) return ciphertext, nonce, nil } func aesGCMDecrypt (key, ciphertext, nonce []byte ) ([]byte , error) { block, err := aes.NewCipher(key) if err != nil { return nil , err } aesgcm, err := cipher.NewGCM(block) if err != nil { return nil , err } plaintext, err := aesgcm.Open(nil , nonce, ciphertext, nil ) if err != nil { return nil , err } return plaintext, nil }
2. 用于其他对称加密算法: 这个密钥同样可以用于 ChaCha20-Poly1305 等其他对称加密算法,使用方式与 AES-GCM 类似。
关键点总结:
用途 :ECDH 协商出的密钥直接用于对称加密 ,保护后续通信的机密性和完整性。
密钥派生(KDF)是必须的 :生产环境一定要使用 HKDF 等标准函数从共享秘密派生密钥,而不是简单地截取字节。这可以防止某些攻击并确保密钥质量。import "golang.org/x/crypto/hkdf" import "crypto/sha256" ... hkdf := hkdf.New(sha256.New, sharedSecret, nil , nil ) derivedKey := make ([]byte , 32 ) if _, err := io.ReadFull(hkdf, derivedKey); err != nil { log.Fatal(err) }
前向保密(Forward Secrecy) :如果每次会话都生成临时 的 ECC 密钥对(Ephemeral Key),那么一次会话的密钥泄露不会导致其他历史会话的加密通信被解密。这是现代安全通信(如 TLS)的标配。上面的示例代码使用的就是临时密钥对。
Java 中 ECC 算法参数与 P、A、B、G 的相互转换 package com.dp.gateway;import org.bouncycastle.asn1.x9.ECNamedCurveTable;import org.bouncycastle.asn1.x9.X9ECParameters;import org.bouncycastle.jce.spec.ECPrivateKeySpec;import org.bouncycastle.jce.spec.ECPublicKeySpec;import org.bouncycastle.math.ec.ECCurve;import org.bouncycastle.math.ec.ECPoint;import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve;import java.math.BigInteger;import java.security.KeyFactory;import java.security.PrivateKey;import java.security.PublicKey;import java.security.Security;import java.security.spec.ECFieldFp;import java.security.spec.EllipticCurve;public class ECCParamsConverterWithBC { static { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); } public static org.bouncycastle.jce.spec.ECParameterSpec createBCParamsFromValues ( BigInteger p, BigInteger a, BigInteger b, BigInteger gx, BigInteger gy, BigInteger n, int h) { ECCurve curve = new ECCurve.Fp(p, a, b); ECPoint g = curve.createPoint(gx, gy); return new org.bouncycastle.jce.spec.ECParameterSpec( curve, g, n, BigInteger.valueOf(h)); } public static org.bouncycastle.jce.spec.ECParameterSpec convertToBCParams ( java.security.spec.ECParameterSpec jceParams) { EllipticCurve jceCurve = jceParams.getCurve(); ECFieldFp field = (ECFieldFp) jceCurve.getField(); BigInteger p = field.getP(); BigInteger a = jceCurve.getA(); BigInteger b = jceCurve.getB(); ECCurve bcCurve = new ECCurve.Fp(p, a, b); java.security.spec.ECPoint jceG = jceParams.getGenerator(); ECPoint bcG = bcCurve.createPoint(jceG.getAffineX(), jceG.getAffineY()); return new org.bouncycastle.jce.spec.ECParameterSpec( bcCurve, bcG, jceParams.getOrder(), BigInteger.valueOf(jceParams.getCofactor())); } public static java.security.spec.ECParameterSpec convertToJCEParams ( org.bouncycastle.jce.spec.ECParameterSpec bcParams) { ECCurve bcCurve = bcParams.getCurve(); BigInteger p = ((SecP256R1Curve) bcCurve).getQ(); BigInteger a = bcCurve.getA().toBigInteger(); BigInteger b = bcCurve.getB().toBigInteger(); ECFieldFp field = new ECFieldFp(p); EllipticCurve jceCurve = new EllipticCurve(field, a, b); ECPoint bcG = bcParams.getG(); java.security.spec.ECPoint jceG = new java.security.spec.ECPoint( bcG.getAffineXCoord().toBigInteger(), bcG.getAffineYCoord().toBigInteger()); return new java.security.spec.ECParameterSpec( jceCurve, jceG, bcParams.getN(), bcParams.getH().intValue()); } public static org.bouncycastle.jce.spec.ECParameterSpec getNamedCurveParams (String curveName) { return org.bouncycastle.jce.ECNamedCurveTable.getParameterSpec("secp256r1" ); } public static void extractBCParams (org.bouncycastle.jce.spec.ECParameterSpec bcParams) { ECCurve curve = bcParams.getCurve(); if (curve instanceof ECCurve.Fp) { ECCurve.Fp fpCurve = (ECCurve.Fp) curve; BigInteger p = fpCurve.getQ(); System.out.println("素数 P: " + p.toString(16 )); } BigInteger a = curve.getA().toBigInteger(); BigInteger b = curve.getB().toBigInteger(); System.out.println("系数 A: " + a.toString(16 )); System.out.println("系数 B: " + b.toString(16 )); ECPoint g = bcParams.getG(); BigInteger gx = g.getAffineXCoord().toBigInteger(); BigInteger gy = g.getAffineYCoord().toBigInteger(); System.out.println("基点 Gx: " + gx.toString(16 )); System.out.println("基点 Gy: " + gy.toString(16 )); BigInteger n = bcParams.getN(); BigInteger h = bcParams.getH(); System.out.println("阶 N: " + n.toString(16 )); System.out.println("辅因子 H: " + h.toString()); } public static void createKeyPairFromParams (org.bouncycastle.jce.spec.ECParameterSpec bcParams) throws Exception { BigInteger privateValue = new BigInteger("1234567890ABCDEF" , 16 ); ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(privateValue, bcParams); ECPoint publicPoint = bcParams.getG().multiply(privateValue); ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(publicPoint, bcParams); KeyFactory keyFactory = KeyFactory.getInstance("EC" , "BC" ); PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); System.out.println("私钥值: " + privateValue.toString(16 )); System.out.println("私钥: " + privateKey.toString()); System.out.println("公钥: " + publicKey.toString()); } public static void demoWithSecp256r1 () throws Exception { System.out.println("=== secp256r1 曲线参数 ===" ); org.bouncycastle.jce.spec.ECParameterSpec bcParams = getNamedCurveParams("secp256r1" ); extractBCParams(bcParams); java.security.spec.ECParameterSpec jceParams = convertToJCEParams(bcParams); System.out.println("\n转换为 JCE 参数成功" ); System.out.println("\n从参数创建密钥对:" ); createKeyPairFromParams(bcParams); } public static void demoWithCustomCurve () throws Exception { System.out.println("\n=== 自定义曲线参数 ===" ); BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF" , 16 ); BigInteger a = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC" , 16 ); BigInteger b = new BigInteger("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45" , 16 ); BigInteger gx = new BigInteger("4A96B5688EF573284664698968C38BB913CBFC82" , 16 ); BigInteger gy = new BigInteger("23A628553168947D59DCC912042351377AC5FB32" , 16 ); BigInteger n = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5FB1A724" , 16 ); int h = 1 ; org.bouncycastle.jce.spec.ECParameterSpec bcParams = createBCParamsFromValues(p, a, b, gx, gy, n, h); extractBCParams(bcParams); System.out.println("\n从自定义参数创建密钥对:" ); createKeyPairFromParams(bcParams); } public static void demoWithX9Parameters () { System.out.println("\n=== 使用 X9ECParameters ===" ); X9ECParameters x9Params = ECNamedCurveTable.getByName("secp256k1" ); ECCurve curve = x9Params.getCurve(); BigInteger p = ((ECCurve.Fp) curve).getQ(); BigInteger a = curve.getA().toBigInteger(); BigInteger b = curve.getB().toBigInteger(); ECPoint g = x9Params.getG(); BigInteger gx = g.getAffineXCoord().toBigInteger(); BigInteger gy = g.getAffineYCoord().toBigInteger(); BigInteger n = x9Params.getN(); BigInteger h = x9Params.getH(); System.out.println("曲线: secp256k1 (比特币使用的曲线)" ); System.out.println("素数 P: " + p.toString(16 )); System.out.println("系数 A: " + a.toString(16 )); System.out.println("系数 B: " + b.toString(16 )); System.out.println("基点 Gx: " + gx.toString(16 )); System.out.println("基点 Gy: " + gy.toString(16 )); System.out.println("阶 N: " + n.toString(16 )); System.out.println("辅因子 H: " + h.toString()); } public static void main (String[] args) throws Exception { demoWithSecp256r1(); demoWithCustomCurve(); demoWithX9Parameters(); } }
运行结果为
=== secp256r1 曲线参数 === 系数 A: ffffffff00000001000000000000000000000000fffffffffffffffffffffffc 系数 B: 5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b 基点 Gx: 6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 基点 Gy: 4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 阶 N: ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 辅因子 H: 1 转换为 JCE 参数成功 从参数创建密钥对: 私钥值: 1234567890abcdef 私钥: EC Private Key [61:21:b0:43:d9:01:76:59:8b:9e:dd:10:f2:f0:32:4d:89:9f:d2:21] X: 9fad84aeae08bbef7f010014d82cef6a09de2b0cf871b5ce0c4f1d13a59a5934 Y: 7cb45769f1070e2c2470fe5b1bfe63133c0b0cdc64ea4bf3791a8ec2a07fd4f 公钥: EC Public Key [61:21:b0:43:d9:01:76:59:8b:9e:dd:10:f2:f0:32:4d:89:9f:d2:21] X: 9fad84aeae08bbef7f010014d82cef6a09de2b0cf871b5ce0c4f1d13a59a5934 Y: 7cb45769f1070e2c2470fe5b1bfe63133c0b0cdc64ea4bf3791a8ec2a07fd4f === 自定义曲线参数 === 素数 P: ffffffffffffffffffffffffffffffff7fffffff 系数 A: ffffffffffffffffffffffffffffffff7ffffffc 系数 B: 1c97befc54bd7a8b65acf89f81d4d4adc565fa45 基点 Gx: 4a96b5688ef573284664698968c38bb913cbfc82 基点 Gy: 23a628553168947d59dcc912042351377ac5fb32 阶 N: fffffffffffffffffffffffffffffffe5fb1a724 辅因子 H: 1 从自定义参数创建密钥对: 私钥值: 1234567890abcdef 私钥: EC Private Key [c6:d2:a8:54:5e:cd :7c:27:18:b8:28:d9:71:bb:d1:b8:d2:d2:0e:63] X: 6b364242b655e56cea164fc1455b5a5285e209ea Y: 26a63389f6bfd4e1f0c567290b5ccc246969da7 公钥: EC Public Key [c6:d2:a8:54:5e:cd :7c:27:18:b8:28:d9:71:bb:d1:b8:d2:d2:0e:63] X: 6b364242b655e56cea164fc1455b5a5285e209ea Y: 26a63389f6bfd4e1f0c567290b5ccc246969da7 === 使用 X9ECParameters === 曲线: secp256k1 (比特币使用的曲线) 素数 P: fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f 系数 A: 0 系数 B: 7 基点 Gx: 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 基点 Gy: 483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 阶 N: fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 辅因子 H: 1