以太坊地址和公钥_使用以太坊的公钥和私钥对数据加解密

本文介绍了如何使用以太坊的私钥和公钥进行数据加解密,详细解析了以太坊密钥对的生成过程,包括通过`crypto.GenerateKey()`生成私钥,以及基于secp256k1椭圆曲线的公钥计算。示例代码展示了使用BouncyCastle库实现公钥加密和私钥解密文件的完整流程,涉及ECIES加密算法和BouncyCastle Provider。
摘要由CSDN通过智能技术生成

概述

本篇主要尝试了采用 Ethereum 生成的私钥和公钥来对数据进行加解密。在进入示例之前先简单了解一下 Ethereum 私钥和公钥的生成过程。

密钥对的生成

其实无论是 Ethereum 还是 Bitcoin,他们的私钥本质上就是一个 256 个二进制位的随机数字(2^256 ~ 10^77,目前可见宇宙中估计只含有 10^80 个原子),譬如:你可以自己选择 256 个 0 作为自己的私钥,但是这明显是不安全的,而要选择足够安全的随机数,也就是要找到足够安全的熵源,即:随机性来源,最好选择密码学安全的伪随机数生成器(CSPRNG)。下面我们看下 Ethereum(go-ethereum) 的源码中如何生成公私钥对的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43// cmd/ethkey/generate.go

var commandGenerate = cli.Command{

Name: "generate",

Usage: "generate new keyfile",

ArgsUsage: "[ ]",

...

Action: func(ctx *cli.Context) error {

...

var privateKey *ecdsa.PrivateKey

var err error

if file := ctx.String("privatekey"); file != "" {

// Load private key from file.

privateKey, err = crypto.LoadECDSA(file)

if err != nil {

utils.Fatalf("Can't load private key: %v", err)

}

} else {

// If not loaded, generate random.

// 这里生成 私钥

privateKey, err = crypto.GenerateKey()

if err != nil {

utils.Fatalf("Failed to generate random private key: %v", err)

}

}

// 下面是生成 account address 和 keystore

// Create the keyfile object with a random UUID.

id := uuid.NewRandom()

key := &keystore.Key{

Id: id,

Address: crypto.PubkeyToAddress(privateKey.PublicKey),

PrivateKey: privateKey,

}

// Encrypt key with passphrase.

passphrase := getPassPhrase(ctx, true)

keyjson, err := keystore.EncryptKey(key, passphrase, keystore.StandardScryptN, keystore.StandardScryptP)

if err != nil {

utils.Fatalf("Error encrypting key: %v", err)

}

...

}

继续看下 crypto.GenerateKey 的实现:

1

2

3

4// crypto/crypto.go

func GenerateKey() (*ecdsa.PrivateKey, error) {

return ecdsa.GenerateKey(S256(), rand.Reader)

}

这里直接调用了 go 的 ecdsa 库中的 GenerateKey 方法来生成。

ECDSA(Elliptic Curve Digital Signature Algorithm) 是使用 ECC(Elliptic Curve Cryptography) 椭圆曲线加密算法对 DSA 数字签名算法的模拟,ECDSA 是 1999 年成为 ANSI 标准,并于 2000 年成为 IEEE 和 NIST 标准。与普通的离散对数问题(Discrete Logarithm Problem DLP)和大数分解问题(Integer Factorization Problem IFP)不同,椭圆曲线离散对数问题(Elliptic Curve Discrete Logarithm Problem ECDLP)没有亚指数时间的解决方法。因此椭圆曲线密码的单位比特强度要高于其他公钥体制。

在调用 ecdsa.GenerateKey 时,第一个参数为选择的椭圆曲线,这里为 secp256k1:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18var theCurve = new(BitCurve)

func init() {

// See SEC 2 section 2.7.1

// curve parameters taken from:

// http://www.secg.org/collateral/sec2_final.pdf

theCurve.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)

theCurve.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)

theCurve.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16)

theCurve.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)

theCurve.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)

theCurve.BitSize = 256

}

// S256 returns a BitCurve which implements secp256k1.

func S256() *BitCurve {

return theCurve

}

上面的几个常亮的定义都是 secp256k1 曲线函数 Fn 中的参数。

secp256k1 标准也是由美国国家标准和技术研究院 NIST 设立,secp256k1 的一些细节参考

示例

下面示例是采用公钥对一个原始的文本文件 source.txt 进行加密生成 cipher.txt,然后再使用 私钥 对加密后的文本文件 cipher.txt 进行解密生成 decrypt.txt。

加解密库基于 BouncyCastle 库(下面简称 BC 库),BC 库是一个开源的,支持 Java 和 C# 的安全库,Java 版本的实现中提供了 JCE 和 JCA 的 provider,读写 ASN.1 编码对象的库,OCSP 的生成/处理器,OpenPGP(RFC 2440)的生成/处理器等等,提供了大量的加密算法,包括 椭圆曲线,AES 等等算法。

注:里面很多常量都是 secp256k1 中定义的。

下面为完整的使用 公钥加密文件,并采用私钥解密文件的示例源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97import org.bouncycastle.crypto.params.ECDomainParameters;

import org.bouncycastle.crypto.params.ECPublicKeyParameters;

import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;

import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bouncycastle.jce.spec.ECNamedCurveSpec;

import org.bouncycastle.jce.spec.IESParameterSpec;

import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve;

import org.bouncycastle.math.ec.custom.sec.SecP256K1FieldElement;

import org.bouncycastle.math.ec.custom.sec.SecP256K1Point;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.math.BigInteger;

import java.security.*;

import java.security.spec.*;

import javax.crypto.Cipher;

import javax.crypto.CipherInputStream;

import javax.crypto.CipherOutputStream;

import javax.crypto.NoSuchPaddingException;

public class EncryptWithPubKey{

public static void main(String [] args) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, InvalidKeyException, IOException{

// ECDSA secp256k1 algorithm constants

BigInteger pointGPre = new BigInteger("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16);

BigInteger pointGPost = new BigInteger("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16);

BigInteger factorN = new BigInteger("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16);

BigInteger fieldP = new BigInteger("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 16);

Security.addProvider(new BouncyCastleProvider());

Cipher cipher = Cipher.getInstance("ECIES", "BC");

IESParameterSpec iesParams = new IESParameterSpec(null, null, 64);

//----------------------------

// Encrypt with public key

//----------------------------

// public key for test

String publicKeyValue = "30d67dc730d0d253df841d82baac12357430de8b8f5ce8a35e254e1982c304554fdea88c9cebdda72bf6be9b14aa684288eb3ba6f9cb7b7872b6e41d2b9706fc";

String prePublicKeyStr = publicKeyValue.substring(0, 64);

String postPublicKeyStr = publicKeyValue.substring(64);

EllipticCurve ellipticCurve = new EllipticCurve(new ECFieldFp(fieldP), new BigInteger("0"), new BigInteger("7"));

ECPoint pointG = new ECPoint(pointGPre, pointGPost);

ECNamedCurveSpec namedCurveSpec = new ECNamedCurveSpec("secp256k1", ellipticCurve, pointG, factorN);

// public key

SecP256K1Curve secP256K1Curve = new SecP256K1Curve();

SecP256K1Point secP256K1Point = new SecP256K1Point(secP256K1Curve, new SecP256K1FieldElement(new BigInteger(prePublicKeyStr, 16)), new SecP256K1FieldElement(new BigInteger(postPublicKeyStr, 16)));

SecP256K1Point secP256K1PointG = new SecP256K1Point(secP256K1Curve, new SecP256K1FieldElement(pointGPre), new SecP256K1FieldElement(pointGPost));

ECDomainParameters domainParameters = new ECDomainParameters(secP256K1Curve, secP256K1PointG, factorN);

ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(secP256K1Point, domainParameters);

BCECPublicKey publicKeySelf = new BCECPublicKey("ECDSA", publicKeyParameters, namedCurveSpec, BouncyCastleProvider.CONFIGURATION);

// begin encrypt

cipher.init(Cipher.ENCRYPT_MODE, publicKeySelf, iesParams);

String cleartextFile = "test/source.txt";

String ciphertextFile = "test/cipher.txt";

byte[] block = new byte[64];

FileInputStream fis = new FileInputStream(cleartextFile);

FileOutputStream fos = new FileOutputStream(ciphertextFile);

CipherOutputStream cos = new CipherOutputStream(fos, cipher);

int i;

while ((i = fis.read(block)) != -1) {

cos.write(block, 0, i);

}

cos.close();

//----------------------------

// Decrypt with private key

//----------------------------

// private key for test, match with public key above

BigInteger privateKeyValue = new BigInteger("eb06bde0e1e9427b3e23ab010a124e8cea0d9242b5406eff295f0a501b49db3", 16);

ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(privateKeyValue, namedCurveSpec);

BCECPrivateKey privateKeySelf = new BCECPrivateKey("ECDSA", privateKeySpec, BouncyCastleProvider.CONFIGURATION);

// begin decrypt

String cleartextAgainFile = "test/decrypt.txt";

cipher.init(Cipher.DECRYPT_MODE, privateKeySelf, iesParams);

fis = new FileInputStream(ciphertextFile);

CipherInputStream cis = new CipherInputStream(fis, cipher);

fos = new FileOutputStream(cleartextAgainFile);

while ((i = cis.read(block)) != -1) {

fos.write(block, 0, i);

}

fos.close();

}

}

在 Cipher.getInstance 方法中第一个参数指定加密算法(Cipher Algorithm Name),这里使用了 ECIES 是一种集成了 KEM(Key Encapsulation Mechanism,密钥封装机制) 和 DEM(Data Encapsulation Mechanism,数据封装机制) 的混合加密系统规范(参考:ANSI X9.63, IEEE 1363a, ISO/IEC 18033-2, SECG SEC-1);第二个参数指定 Provider,这里的 “BC” 就是 BouncyCastle Provider。

IESParameterSpec 为 IES 的算法引擎参数设置,这里的三个参数分别为:

derivation: 可选,用于 KDF 的推导向量;

encoding: 可选,用于 KDF 的编码向量;

macKeySize: 设置 MAC 的 key size,这里设置为 64 个 bits;

下图为加密之前的 source.txt 文本文件:

下图为通过 public key 加密之后的 encrypt.txt 文本文件,非文本格式,无法识别:

下图为通过 private key 对 encrypt.txt 解密之后的 decrypt.txt 文本文件,与原始的 source.txt 一致:

常见术语

EC, Elliptic Curve, 椭圆曲线

ECC, Elliptic Curve Cryptography, 椭圆曲线密码学

ECDSA, Elliptic Curve Digital Signature Algorithm, 椭圆曲线数字签名算法

DH, Diffie-Hellman Key Exchange, Diffie-Hellman 密钥交换

ECDH, Elliptic Curve Diffie-Hellman Key Exchange, 椭圆曲线 Diffie-Hellman 密钥交换

IES, Integrated Encryption Schema, 集成加密框架

ECIES, Elliptic Curve Integrated Encryption Schema, 椭圆曲线集成加密框架

KDF, Key Derivation Function, 密钥(私钥)生成函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值