加密与签名

简介

加密一般是用公钥进行加密,然后使用私钥进行解密(注意一般不会反向操作)。签名一般指的是,通过私钥对数据的摘要(hash值)进行签名,摘要保证了数据的完整性,签名后的数据,可以用公钥进行验证。

一般加密使用 RSA(RSA也可以用做签名),签名可以使用 ECDSA(ECDSA不具备加密能力)。

RSA 是基于质因数分解,椭圆曲线算法是依赖于椭圆曲线离散对数问题的困难性。从安全性角度看,椭圆曲线离散对数问题比整数质因数分解更困难。RSA需要更长的密钥(2048位以上)来保证安全强度,ECDSA可以使用更短的密钥。256位的 ECDSA密钥与3072位的RSA密钥强度相当。ECDSA的实现比较复杂,需要找到合适的椭圆曲线,RSA比较简单。

区块链的曲线和算法

主要的算法有:

ECDSA

secp256k1

ECDSA算法在区块链应用里,多数币种用的是 secp256k1 ,比如ETH,BTC等。该曲线和算法,第一版代码是中本聪写的,当时并没有这个算法和曲线库的开源实现。 y2=X3+7y^2 = X^3 +7

secp256r1 (aka: NIST P-256)

这个是比较老牌的曲线了,TLS用的就是这条曲线。ECDSA算法用这条secp256r1曲线的币种有:NEO和 ONT (Ontology)。其中b是一个常数,具体值比较大,这里就不写了

y2=x33x+by^2 = x^3 -3x + b

EDDSA

ED25519

在 ed25519曲线上,用 EDDSA算法的币种有: Monero(XMR), Cardano(ADA), Algorand(ALGO), Stellar(XLM), Tezos(XTZ), Zcash(ZEC), HBAR 等

Pallas

mina 这个币种,签名和密钥生成,用的曲线是 pallas,算法是 EDDSA。

ECDH

ECDH 的本质,就是通过两个公钥,算出一个共享秘密(AES key)。两个公钥相同,那么算出的共享秘密就相同。在浏览器TLS场景下,每次服务器和客户端都会临时生成一个 pub key,然后将pub key 发给对方,双方在自己的环境内,独立的算出 共享秘密 AES Key。ECDH 生成 AES key 的过程也叫 derive shared shared secret (和加密货币HD wallet的derive不一样)。可参考 AWS KMS Derive 的示例arrow-up-right

HMAC

HMAC 和非对称式密钥的 Sign 有点像,都是对原始数据,产生一个 hmac 或签名,验证方拿原始的数据以及这个 hmac 和签名,做verify,看是否verify 通过,从而保证数据在传输的时候没有被人改过。 不同的是:HMAC 是对称式密钥的功能,Sign 是非对称密钥的功能。HMAC 由于双方都有AES key,所以双方都能创造 hmac 值和验证。而Sign 只有私钥拥有方才能签名,任何公钥持有者都能验证。HMAC性能要比Sign快一些(可能几十倍差距,并非数量级差别)

签名

示例一: 直接对原始字符串进行加密

示例二:在签名的时候,可以指定签名 encode的格式

示例三,对数据摘要进行签名

加密解密

使用 rsa 算法进行加密或解密

有关 PKCS#1 与 PKCS#8

如果是 openssl 1.x,默认创建的rsa 的私钥格式,是pkcs#1的(以-BEGIN RSA PRIVATE KEY开头),但openssl 3.x,默认创建的是pkcs#8(以BEGIN PRIVATE KEY开头)

以太坊私钥创建,以及地址验证

创建ECDSA的私钥(该私钥可以作为以太坊的私钥,直接导入metamask钱包)

上述得到公钥之后,可以使用下面的python代码,获取其地址(同时将key放到metamask里,对比下,将发现地址是一致的)

上述操作完整的python代码

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 编码过的。

如果是产生的 DataKeyWithoutPlaintext,那么需要先通过 aws kms decrypt --ciphertext-blob 接口解密,获得其明文的datakey,之后使用方法和上述一样

通过kms 算地址

注意,python里的 ethernum 包已经被弃用了,所以我们用 web3的这个包来算地址

然后我们可以做一笔签名,在签名里,还原回pub key,再次通过 pub key 算地址,看与之前是否一致 (我们也可以通过 cast 命令来生成一个待签名hash,做一笔真正的转账)

使用 openssl 产生 AES密钥

上述使用 kms 产生的 datakey,如果是想使用 openssl本地产生,可以使用下面的命令,之后使用方法和上述一样

Last updated