简介
加密一般是用公钥进行加密,然后使用私钥进行解密(注意一般不会反向操作)。签名一般指的是,通过私钥对数据的摘要(hash值)进行签名,摘要保证了数据的完整性,签名后的数据,可以用公钥进行验证。
一般加密使用 RSA(RSA也可以用做签名),签名可以使用 ECDSA(ECDSA不具备加密能力)。
RSA 是基于质因数分解,椭圆曲线算法是依赖于椭圆曲线离散对数问题的困难性。从安全性角度看,椭圆曲线离散对数问题比整数质因数分解更困难。RSA需要更长的密钥(2048位以上)来保证安全强度,ECDSA可以使用更短的密钥。256位的 ECDSA密钥与3072位的RSA密钥强度相当。ECDSA的实现比较复杂,需要找到合适的椭圆曲线,RSA比较简单。
区块链的曲线和算法
主要的算法有:
ECDSA
secp256k1
ECDSA算法在区块链应用里,多数币种用的是 secp256k1 ,比如ETH,BTC等。该曲线和算法,第一版代码是中本聪写的,当时并没有这个算法和曲线库的开源实现。 y 2 = X 3 + 7 y^2 = X^3 +7 y 2 = X 3 + 7
secp256r1 (aka: NIST P-256)
这个是比较老牌的曲线了,TLS用的就是这条曲线。ECDSA算法用这条secp256r1曲线的币种有:NEO和 ONT (Ontology)。其中b是一个常数,具体值比较大,这里就不写了
y 2 = x 3 − 3 x + b y^2 = x^3 -3x + b y 2 = x 3 − 3 x + b EDDSA
ED25519
在 ed25519曲线上,用 EDDSA算法的币种有: Monero(XMR), Cardano(ADA), Algorand(ALGO), Stellar(XLM), Tezos(XTZ), Zcash(ZEC), HBAR 等
Pallas
mina 这个币种,签名和密钥生成,用的曲线是 pallas,算法是 EDDSA。
有关椭圆曲线
ECC、ECDSA、EDDSA、ECDH、ECDHE都是基于椭圆曲线 cryptography (ECC) 的算法,它们之间的区别如下:
ECC 是椭圆曲线加密算法的总称,提供了基于椭圆曲线数学原理的公钥密码学框架。
ECDSA 是基于ECC的数字签名算法,用于生成签名和验证签名。
EDDSA 是新一代的ECC签名算法,相比ECDSA有各种改进。
ECDH 是基于ECC的密钥交换/协商算法,用于在双方生成共享密钥。
ECDHE 是使用ECDHE执行密钥交换的协议,常用在TLS等加密通信中。
总结一下:
签名
示例一: 直接对原始字符串进行加密
复制 ## ecdsa 0.18.0
import ecdsa.util
from ecdsa import SigningKey, SECP256k1
# 生成签名秘钥
sk = SigningKey.generate(curve=SECP256k1)
# 待签名信息
message = "abc"
message_bytes = message.encode()
# 计算签名
signature = sk.sign(message_bytes)
r, s = ecdsa.util.sigdecode_string(signature, order=SECP256k1.generator.order())
print(r,s)
# 验证签名
vk = sk.verifying_key
print(vk.verify(signature, message_bytes)) # True
示例二:在签名的时候,可以指定签名 encode的格式
复制
import ecdsa
import hashlib
# 生成签名秘钥
sk = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)
# 消息
message = b"abc"
# 签名,下面sign的时候,也可以指定 entropy 为任意正整数,这个代表了椭圆曲线算法的 k,默认用的是 os.urandom()
sig = sk.sign(message, hashfunc= hashlib.sha256, sigencode=ecdsa.util.sigencode_der)
from ecdsa.util import sigdecode_der
curve = ecdsa.SECP256k1
order = curve.generator.order()
r, s = sigdecode_der(sig,order)
print(r, s)
# 验证签名
vk = sk.verifying_key
print(vk.verify(sig, message, hashfunc=hashlib.sha256, sigdecode=sigdecode_der))
示例三,对数据摘要进行签名
复制 ## ecdsa 0.18.0
import ecdsa.util
from ecdsa import SigningKey, SECP256k1
import hashlib
# 生成签名秘钥
sk = SigningKey.generate(curve=SECP256k1)
# 待签名信息,获取其摘要
message = "abc"
message_bytes = message.encode()
hash_func = hashlib.sha256()
hash_func.update(message_bytes)
msg_digest = hash_func.digest()
# 计算签名
signature = sk.sign(msg_digest)
r, s = ecdsa.util.sigdecode_string(signature, order=SECP256k1.generator.order())
print(r,s)
# 可选步骤:将签名转为16进制字符串
import codecs
signature_hex = codecs.encode(signature, 'hex')
print(signature_hex)
# 验证签名
vk = sk.verifying_key
print(vk.verify(signature, msg_digest)) # True
加密解密
使用 rsa 算法进行加密或解密
复制 # 默认使用 PKCS#1v1.5进行RSA填充,也可以指定使用TLS填充
import rsa
# 生成RSA密钥对
(pubkey, privkey) = rsa.newkeys(1024)
# 待加密信息
message = 'hello world'
# RSA加密
crypto = rsa.encrypt(message.encode(), pubkey)
# RSA解密
message = rsa.decrypt(crypto, privkey).decode()
print(message)
有关 PKCS#1 与 PKCS#8
如果是 openssl 1.x,默认创建的rsa 的私钥格式,是pkcs#1的(以-BEGIN RSA PRIVATE KEY开头),但openssl 3.x,默认创建的是pkcs#8(以BEGIN PRIVATE KEY开头)
复制 # openssl 1.x
openssl genrsa -out private-key.pem
openssl rsa -pubout -in private-key.pem -out public-key.pem
# openssl 3.x
openssl genrsa -traditional -out private-key.pem
openssl rsa -traditional -pubout -in private-key.pem -out public-key.pem
以太坊私钥创建,以及地址验证
创建ECDSA的私钥(该私钥可以作为以太坊的私钥,直接导入metamask钱包)
复制 openssl ecparam -name secp256k1 -genkey -noout -out ec-secp256k1-priv-key.pem
#获得公钥和私钥,保存到key文件
cat ec-secp256k1-priv-key.pem | openssl ec -text -noout > key
# 私钥
cat key | grep priv -A 3 | tail -n +2 | tr -d '\n[:space:]:' | sed 's/^00//' > priv
# 公钥
cat key | grep pub -A 5 | tail -n +2 | tr -d '\n[:space:]:' | sed 's/^04//' > pub
上述得到公钥之后,可以使用下面的python代码,获取其地址(同时将key放到metamask里,对比下,将发现地址是一致的)
复制 from ethereum import utils
def generate_ethereum_address(public_key):
address = utils.sha3(public_key)[-20:]
return '0x' + address.hex()
# Example public key (replace this with an actual public key)
public_key = bytes.fromhex("74f96b6971d2cdc996e1ac4886cb45ed7c995b3bbba513da1b8822d3162da2b262909754c0b5c98f1dd26943672b6e1f319e7220aa39fef220a2900423b573b8")
ethereum_address = generate_ethereum_address(public_key)
print("Ethereum Address:", ethereum_address)
上述操作完整的python代码
复制 from ecdsa import SigningKey, SECP256k1
sk = SigningKey.generate(curve=SECP256k1)
prv_key_hex = sk.to_string().hex()
print(prv_key_hex)
pub_key = sk.get_verifying_key()
pub_key_hex = pub_key.to_string().hex()
print(pub_key_hex)
# 也可以将上述信息,保存到文件中
#生成私钥pem文件
# with open("private.pem", "wb") as f:
# f.write(sk.to_pem())
#
#
# # 从私钥pem文件中,读取pk信息
# with open("private.pem", "r") as f:
# private_key_pem = f.read()
# sk = SigningKey.from_pem(private_key_pem)
#
#
# prv_key_hex = sk.to_string().hex()
# print(prv_key_hex)
#
# pub_key = sk.get_verifying_key()
# pub_key_hex = pub_key.to_string().hex()
# print(pub_key_hex)
## 获得以太坊地址
from ethereum import utils
def generate_ethereum_address(public_key):
address = utils.sha3(public_key)[-20:]
return '0x' + address.hex()
public_key = bytes.fromhex(pub_key_hex)
ethereum_address = generate_ethereum_address(public_key)
print("Ethereum Address:", ethereum_address)
AWS KMS 基础
对称式密钥
使用 AWS KMS创建的对称式密钥,如果直接使用 encrypt 或者 decrypt 接口对数据进行加密解密,那么加密的数据要求是4096字节以内,因此一般只适合加密数据库密码等数据,或者加密 data key(data key有时候也被称为 envelop key)。对于大量数据加密,一般是通过 GenerateDataKey的方式,产生一对对称式数据密钥,然后使用数据密钥对数据进行加密。也可以GenerateDataKeyPair 产生非对称式数据密钥,然后对数据做验签。
AWS KMS GenerateDataKey
只有对称式加密才能产生data key,非对称式加密无法产生data key。产生数据密钥的时候,会返回 Data Key 的 CiphertextBlob 以及 Plaintext,然后可以拿 Plaintext 直接进行加密。但如果此时只是想产生,并不需要立即使用,那么可以通过generate-data-key-without-plaintext 接口,只产生CiphertextBlob,不产生Plaintext。另外需要注意,无论是 CiphertextBlob 还是 Plaintext,都是 base64 编码过的。
复制 export KEY_ID=$AWS_KEY_ID
# 获得明文的datakey,并将其保存到文件中
aws kms generate-data-key --key-id $KEY_ID --key-spec AES_256 --query Plaintext --output text | base64 -d > plaintext_datakey
# 使用上述产生的datakey,对 my_secret_message.txt 文件进行加密,并将加密后的数据保存成一个 .enc 的新文件
openssl enc -e -aes256 -kfile plaintext_datakey \
-in my_secret_message.txt \
-out my_secret_message.enc
# 解密,获得新的文件
openssl enc -d -aes256 -kfile plaintext_datakey \
-in my_secret_message.enc \
-out new_plaintext_message.txt
如果是产生的 DataKeyWithoutPlaintext,那么需要先通过 aws kms decrypt --ciphertext-blob 接口解密,获得其明文的datakey,之后使用方法和上述一样
复制 aws kms generate-data-key-without-plaintext --key-id $KEY_ID --key-spec AES_256 --query CiphertextBlob --output text | xargs -I {} aws kms decrypt --ciphertext-blob {} --query Plaintext --output text | base64 -d > plaintext_datakey
使用 openssl 产生 AES密钥
上述使用 kms 产生的 datakey,如果是想使用 openssl本地产生,可以使用下面的命令,之后使用方法和上述一样
复制 # AES 256密钥:该命令其实是生成32字节的随机数,每个字节8位,因此是256位,生成的密钥保存在 aes256key.bin文件中
openssl rand -out aes256key.bin 32
# 生成 AES 128
openssl rand -out aes256key.bin 16
# 生成 AES 192
openssl rand -out aes192key.bin 24