0%

密码学 — 密码算法与协议

密码学思维导图

密码学

密码学基本认知

  1. 密码学是科学;
  2. 密码学理论是公开的;
  3. 密码学算法是相对安全的;
  4. 密码学攻击方法是多样的;
  5. 密码学应用标准很重要;
  6. 不具备很强的数学知识也能掌握密码学;
  7. 解决特点问题的密码学;

密码学的四个目标

  1. 机密性(隐私性);
  2. 完整性;
  3. 身份验证;
  4. 不可抵赖性;

Hash 算法

摘要算法 / 散列函数 / 哈希函数 / 杂凑函数:把任意长的输入消息数据转化成固定长度的输出数据的一种密码算法。

分类 算法 输出值长度 输入值最大长度 说明
MD5 MD5 128 比特 无限制 实践中已经产生了碰撞,理论上不具备弱抗碰撞性
SHA-1 SHA-1 160 比特 2^64^ -1 比特 实践中已经产生了碰撞
SHA-2 SHA-256 256 比特 2^64^ -1 比特 安全使用
SHA-512 512 比特 2^128^ -1 比特 安全使用
SHA-224 224 比特 2^64^ -1 比特 安全使用
SHA-384 384 比特 2^128^ -1 比特 安全使用
SHA-3 SHA3-256 256 比特 2^64^ -1 比特 安全使用
SHA3-512 512 比特 2^128^ -1 比特 安全使用
SHA3-224 224 比特 2^64^ -1 比特 安全使用
SHA3-384 384 比特 2^128^ -1 比特 安全使用

Hash 算法实践

OpenSSL 实现了 5 种 Hash / 信息摘要 算法,分别是 MD2、MD5、MDC2、SHA(SHA1)和 RIPEMD。SHA 算法事实上包括了 SHA 和 SHA1 两种信息摘要算法,此外,OpenSSL 还实现了 DSS 标准中规定的两种信息摘要算法 DSS 和 DSS1。

# file.txt 文件内容为:hello world
# 用 SHA-1 算法计算文件 file.txt 的 Hash 值,输出到 stdout
$ openssl dgst -sha1 file.txt
SHA1(file.txt)= 22596363b3de40b06f981fb85d82312e8c0ed511

# 用 SHA-1 算法计算文件 file.txt 的 Hash 值,输出到 digest.txt
$ openssl sha1 -out digest.txt file.txt

# 用 DSS1(SHA1) 算法为文件 file.txt 签名,输出到文件 dsasign.bin
# 签名的 private key 必须为 DSA 算法产生的,保存在文件 dsakey.pem 中
$ openssl dgst -dss1 -sign dsakey.pem -out dsasign.bin file.txt

# 用 DSS1 算法验证 file.txt 的数字签名 dsasign.bin,
# 验证的 private key 为 DSA 算法产生的文件 dsakey.pem
$ openssl dgst -dss1 -prverify dsakey.pem -signature dsasign.bin file.txt

# 用 SHA-1 算法为文件 file.txt 签名,输出到文件 rsasign.bin
# 签名的 private key 为 RSA 算法产生的文件 rsaprivate.pem
$ openssl sha1 -sign rsaprivate.pem -out rsasign.bin file.txt

# 用 SHA-1 算法验证 file.txt 的数字签名 rsasign.bin,
# 验证的 public key 为 RSA 算法生成的 rsapublic.pem
$ openssl sha1 -verify rsapublic.pem -signature rsasign.bin file.txt

对称加密算法

对称加密算法 / 密码密钥算法 / 单密钥算法:加密和解密使用同一密钥或加密和解密的密钥之间存在简单的、容易计算的互推关系。

  • 流密码算法又称序列密码算法,每次加密或解密一位或一字节的明文或密文。
  • 分组密码算法将明文(密文)分成固定长度的数据块(比特块或字节块),用同一密钥和算法对每一明文(密文)块加密(解密)后得到等长的密文(明文)块,然后将密文(文)块按照顺序组合起来最终得到密文(明文)。

流密码算法

算法 密钥长度 说明
RC4 可变密钥长度,建议 2048 比特 目前已被证明不安全
ChaCha 可变密钥长度,建议 256 比特 一种新型的流密码算法

块密码算法

算法 密钥长度 分组长度 说明
AES 128、192、256 比特 128 比特 对称加密算法的标准算法
DES 56 比特 64 比特 早期对称加密算法标准
3DES 128 比特或 168 比特 64 比特 三重 DES 算法
Blowfish 可变密钥长度,32~448 比特之间 64 比特 不推荐使用
Rijndael 128、192、256 比特 128、192、256 比特 AES 算法的原生算法
Camellia 128、192、256 比特 128 比特 不太常见的加密算法
IDEA 算法 128、192、256 比特 128 比特 不太常见的加密算法
SEED 算法 128、192、256 比特 128 比特 不太常见的加密算法

如果你对 AES 算法很感兴趣,可以参考这篇文章:AES 简介

分组密码的工作模式 / 迭代模式 / 分组模式

对称加密算法中,分组密码都有固定的分组长度,实际应用中需要加密的明文远远长于这个分组长度。如何对明文进行分组、填充?分组后的明文组和密文组之间有没有关系?这些要素形成了分组密码的不同工作模式。

模式 名称 特点 说明
ECB Electronic Codebook,电子密码本模式 运算快速,支持并行处理,需要填充,不适用于长报文、高度结构化报文 不推荐使用
CBC Ciper Block Chaining,密文块链接模式 支持并行处理,需要填充 ⭐️ 推荐使用
CFB Ciper Feedback,密文反馈模式 分组密码转换为流密码,支持并行处理,不需要填充, 不推荐使用
OFB Output Feedback,输出反馈模式 迭代运算使用流密码模式,不需要填充,缺点:难以检测对密文的篡改。 不推荐使用
CTR Count,计数器模式 迭代运算使用流密码模式,支持并行处理,不需要填充 ⭐️ 推荐使用
XTS XEX-based tweaked-codebook 不需要填充 用于本地硬盘存储解决方案中

分组加密的操作模式

为要加密的数据分组提供保密或数据验证等功能。

分组加密的操作模式 描述 种类
加密操作模式 只保护数据的私密性,无需验证数据来源 ECB、CBC、OFB、CFB、==CTR==
验证操作模式 不在乎数据的保密性,只想确保数据的发送来源 ==CMAC==
验证加密操作模式 既想保密要传输的数据,还要确认数据的来源 CCM、==GCM==、KW/KWP/TKW

详细参考《Web 开发的身份和数据安全》第 172~174 页。

分组密码的填充标准

在分组对称加密算法中,明文长度必须是分组长度的倍数,如果不是倍数,必须有一种填充(padding)机制,填充冗余数据以保证明文长度是分组长度的倍数。

PKCS#5 填充标准

PKCS#5 处理的长度只能是 8 字节。

PKCS#7 填充标准

PKCS#7 处理的长度可以是 1 到 255 任意字节。

AES 算法中分组长度没有 8 字节,所以 AES 算法使用 PKCS#7 标准(在 OpenSSL 命令行中默认使用的填充标准即为 PKCS#7 标准)。

对称加密算法实践

OpenSSL 一共提供了 8 种对称加密算法,其中 7 种是分组加密算法,仅有的一种流加密算法是 RC4。这 7 种分组加密算法分别是 AES、DES、Blowfish、CAST、IDEA、RC2、RC5,都支持电子密码本模式(ECB)、加密分组链接模式(CBC)、加密反馈模式(CFB)和输出反馈模式(OFB)四种常用的分组密码加密模式。其中,AES 使用的加密反馈模式(CFB)和输出反馈模式(OFB)分组长度是 128 位,其它算法使用的则是 64 位。事实上,DES 算法里面不仅仅是常用的 DES 算法,还支持三个密钥和两个密钥 3DES 算法。

在 OpenSSL 命令行中,对称加密算法主要使用 enc 子命令,后面的参数可以指定具体的加密算法。也可以直接使用对称加密算法对应的子命令来操作:

# 采用 3des 算法
$ openssl enc des3

# 采用 3des 算法
$ openssl des3

显示系统支持的加密算法:

$ openssl list -cipher-algorithms

使用 OpenSSL 执行加密操作:

# 使用 aes-256-cbc 算法对 file.txt 进行加密,输出到 file.enc 文件
# 密钥是通过口令(mypassword)和 Salt 生成的
$ openssl enc -aes-256-cbc -salt -in file.txt -out file.enc -pass pass:mypassword -p
salt=4BAB66E493E8AA8E
key=6C853931F7A6BCE75756F76A99D10F473C691CDECD05B2EB066FDDC5ED49A1B2
iv =D26860583F714B4E4FF68F905D711E23

参数:

  • -in,从文件中读出明文内容。
  • -out,将加密内容保存到某个文件。
  • -aes-256-cbc,使用 AES 算法,密钥长度是 256 比特,分组模式是 CBC 模式。
  • -p,打印本次加密过程中的 salt、密钥、初始化向量的值。
  • 另外,OpenSSL 默认使用 PKCS#7 填充标准。

使用 OpenSSL 执行解密过程:

$ openssl enc -d -aes-256-cbc -in file.enc
enter aes-256-cbc decryption password:mypassword
hello world

消息验证码(MAC)

Hash 算法可以保证消息的完整性校验,对称加密算法保证消息的机密性,而消息验证码(Message Authentication Code,MAC)算法避免消息被篡改。

消息验证码一般结合加密算法一起使用,即 AE 加密模式。

MAC 算法 种类 说明
CBC-MAC 算法 CBC-MAC 从块密码算法的 CBC 分组模式演变而来
HMAC 算法 HMAC-SHA-1、HMAC-SHA-256、HMAC-SHA-512 使用 Hash 算法作为加密基元

AE 加密模式

对称加密算法保证消息的机密性,而 MAC 算法保证消息的完整性。结合这两个算法,提供机密性和完整性的模式也叫做 AE(Authenticated Encryption)加密模式

  • AE 加密模式:需要使用者单独处理加密运算和 MAC 运算,一旦使用不当,就很容易出现安全问题。

  • AEAD 加密模式:在底层组合了加密算法和 MAC 算法,能够同时保证数据机密性和完整性,减轻了使用者的负担。

![](https://blog-andy0570-1256077835.cos.ap-shanghai.myqcloud.com/site_Images/AE 加密模式.png)

公开密钥算法

非对称算法 / 公钥算法:加密密钥(公钥)和解密密钥(私钥)不相同的密码算法。

RSA 加密算法

非对称加密算法 RSA 加密算法
推荐密钥长度 2048 比特
推荐填充标准 RSAES-OAEP
用途 加密解密、密钥协商、数字签名

RSA 算法实践

OpenSSL 一共实现了 4 种非对称加密算法,包括 DH 算法、RSA 算法、DSA 算法和椭圆曲线算法(ECC)。DH 算法一般用于密钥交换。RSA 算法既可以用于密钥交换,也可以用于数字签名,当然,如果你能够忍受其缓慢的速度,那么也可以用于数据加密。DSA 算法只用于数字签名。

使用 genrsa 子命令生成密钥对,密钥对是一个文件

# ---------------------- 示例一 ----------------------------
# 使用 genrsa 命令生成密钥长度是 2048 比特的 RSA 密钥对
$ openssl genrsa -out mykey.pem 2048

# ---------------------- 示例二 ----------------------------
# 使用 genrsa 命令生成 2048 位的 RSA 密钥对,并使用 128 比特的 AES 算法保存密钥。
# **********************************************************
# 使用密码方式保护私钥只有在私钥并没有被放在生产环境服务器上的时候才有用。
# **********************************************************
$ openssl genrsa -aes128 -out fd.key 2048
Generating RSA private key, 2048 bit long modulus
....+++
..........................+++
# e 表示公用指数,默认情况下会被设置为65 537。
# 这是所谓的短公用指数(short public exponent),它可以显著提高 RSA 算法的验证性能。
e is 65537 (0x10001) 
Enter pass phrase for fd.key:mypassword # 该口令用于生成 AES 算法的密钥
Verifying - Enter pass phrase for fd.key:mypassword

# ---------------------- 示例三(生产环境不推荐 3DES 算法) ----------------------------
# 口令结合 3DES 算法保护密钥对
# 【说明】使用 genrsa 子命令生成 RSA 密钥对的时候,为了防止该密钥对被泄漏,
#        可以使用对称加密算法进行保护,而对称加密算法使用的密钥则是通过口令生成。
$ openssl genrsa -des3 -out mykey2.pem 2048
Generating RSA private key, 2048 bit long modulus
.............+++
..+++
e is 65537 (0x010001)
Enter pass phrase for mykey2.pem:mypassword # 该口令用于生成 3DES 算法的密钥
Verifying - Enter pass phrase for mykey2.pem:mypassword

查看解析私钥

#################################################
# 查看以 PEM 格式存储的私钥
$ cat mykey.pem
-----BEGIN RSA PRIVATE KEY----- # 第一行,表明它是一个私钥文件
MIIEowIBAAKCAQEAsYkE9KZXZOQ7YI1maJBM6aVRqK8GICC854FD6j2Ut914KBYX
... ()
1g0FI5lvvXjBYDUAKNiKrDbIAGcMUIgcgBy/q6X2EY5x8G/N9BVj
-----END RSA PRIVATE KEY-----

#################################################
# 解析私钥结构
$ openssl rsa -in mykey.pem -text
...()

从密钥对中分离出公钥:

$ openssl rsa -in mykey.pem -pubout -out mypubkey.pem

参数:

  • -pubout,表示输出一个公钥文件。

校验密钥对文件是否正确

$ openssl rsa -in mykey.pem -check -noout
RSA key ok

参数:

  • -noout,表示不打印密钥对信息,如果校验成功,说明密钥对文件无误。

显示公钥信息

$ openssl rsa -pubin -in mypubkey.pem -text
Public-Key: (2048 bit) ## 密钥长度
Modulus:               ## 公开密钥系数,就是 RSA 结构中的 n
    00:a7:ac:c2:c4:b1:48:24:ed:96:8f:56:53:14:a6:
    2c:8d:70:58:26:e1:11:5d:71:0a:2c:bc:8e:dd:c3:
    28:62:0e:8f:6f:ae:d0:6b:f0:6f:97:4a:63:8c:16:
    a1:e5:a8:26:c7:e9:38:89:58:79:3b:24:84:53:2d:
    d3:00:17:ce:18:45:45:a5:5c:71:6c:c5:e6:da:2e:
    86:2d:cd:75:35:7d:04:b8:cb:7b:2c:cb:fd:a5:eb:
    15:4d:64:83:2b:75:8a:48:c3:41:a2:ee:17:cf:31:
    66:e6:f9:31:eb:24:b6:8f:14:a2:ce:06:77:94:df:
    1a:5d:78:92:d0:f3:30:75:fd:9c:77:53:f6:46:a8:
    89:02:cd:8d:2a:10:f8:51:78:c9:f0:93:77:b4:36:
    c0:96:d9:72:69:2a:07:d8:2c:e2:23:8b:a1:25:1e:
    1e:d8:9a:0f:1e:04:68:b5:72:74:5a:a9:d1:bf:ca:
    7d:75:46:77:d7:f9:b1:0a:a7:01:33:ee:0e:16:29:
    22:68:1f:e9:2f:14:41:0d:a2:45:7b:dc:8b:31:a7:
    f5:15:8f:ac:e3:6c:ff:84:36:12:7c:06:6d:0f:94:
    fd:99:ae:09:12:36:b0:13:20:40:85:4f:f2:b3:1c:
    5a:b4:25:c2:4d:8b:89:20:94:69:b0:f2:a2:2a:bb:
    08:c9
Exponent: 65537 (0x10001) ## 公钥,就是 RSA 结构中的 e
writing RSA key
-----BEGIN PUBLIC KEY-----   ## 下面是公钥的具体值
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp6zCxLFIJO2Wj1ZTFKYs
jXBYJuERXXEKLLyO3cMoYg6Pb67Qa/Bvl0pjjBah5agmx+k4iVh5OySEUy3TABfO
GEVFpVxxbMXm2i6GLc11NX0EuMt7LMv9pesVTWSDK3WKSMNBou4XzzFm5vkx6yS2
jxSizgZ3lN8aXXiS0PMwdf2cd1P2RqiJAs2NKhD4UXjJ8JN3tDbAltlyaSoH2Czi
I4uhJR4e2JoPHgRotXJ0WqnRv8p9dUZ31/mxCqcBM+4OFikiaB/pLxRBDaJFe9yL
Maf1FY+s42z/hDYSfAZtD5T9ma4JEjawEyBAhU/ysxxatCXCTYuJIJRpsPKiKrsI
yQIDAQAB
-----END PUBLIC KEY-----

参数:

  • -in 表示输入文件。
  • -pubin 如果输入文件是公钥,需要带上该参数。
  • -text 打印密钥的相关信息。

RSA 加密、解密

$ echo "hello" >>plain.txt

# 使用密钥对加密
$ openssl rsautl -encrypt -inkey mykey.pem -in plain.txt -out cipher.txt

# 使用公钥加密,务必有 -pubin 参数表明 -inkey 参数输入的是公钥文件
$ openssl rsautl -encrypt -pubin -inkey mypubkey.pem -in plain.txt -out cipher.txt

# 解密
$ openssl rsautl -decrypt -inkey mykey.pem -in cipher.txt
hello

rsautl 命令默认的填充机制是 PKCS#1 v1.5,可以指定使用 PKCS#1 OAPE 机制,比如:

$ openssl rsautl -encrypt -inkey mykey.pem -in plain.txt -out cipher.txt -oape

OpenSSL 无法使用 RSA 加密大文件

RSA 算法通常用于密钥协商数字签名 / 身份认证中。

当涉及存储大量数据时,不推荐使用 2048 位的 RSA 非对称加密算法进行加密,因为 RSA 非对称加密算法的加密效率极其缓慢。

因此,请不要使用 OpenSSL 加密大文件,错误示例如下:

# 使用 genrsa 命令生成 2048 位的 RSA 密钥对(rsa_private.key)
openssl genrsa -out rsa_private.key 2048

# 解析私钥结构,查看密钥内容
openssl rsa -text -in rsa_private.key

# 生成密钥的公钥(rsa_private.key)
openssl rsa -in rsa_private.key -pubout -out rsa_public.key

# 使用生成的公钥加密文件
# -pubin 表明用纯公钥文件加密
# -inkey 指定密钥(rsa_private.key)
# -in 指定要加密的文件(test.pdf,3.2M)
# -out 加密后的文件(encrypted.en)
openssl rsautl -encrypt -pubin -inkey rsa_public.key  -in test.pdf -out encrypted.en
RSA operation error
4475450988:error:04FFF06E:rsa routines:CRYPTO_internal:data too large for key size:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.200.4/libressl-2.6/crypto/rsa/rsa_pk1.c:151:

参考:How to encrypt a large file in openssl using public key

ECDSA 算法实践

生成 ECDSA-256 密钥(.pem 文件):

# 使用 secp256r1 命名曲线创建一个256位长度的 ECDSA 密钥,并使用 AES-128 位算法保存密钥。
$ openssl ecparam -genkey -name secp256r1 | openssl ec -out mykey.pem -aes128

只是生成密钥的命令不同,加密解密环节的命令和上面是一样的。

密钥协商算法

密钥协商算法解决密钥分配、存储、传输等问题。

密钥协商算法 说明
RSA 算法 缺点:会话密钥由客户端决定、无法提供前向安全性
DH 算法 静态 DH 算法、临时 DH 算法(EDH)
ECDH 算法 ECC 椭圆曲线密码学 + DH 算法
  • 只有临时 DH 算法可以提供前向安全性。

  • ECC 椭圆曲线密码学算法的优势:密钥长度短,性能和安全性非常高。

ECC 算法中的命名曲线

NIST 命名曲线 其他标准对应的命名曲线
P-192 ansix9p192r1、prime192v1、secp192r1
P-256 ansix9p256r1、prime256v1、secp256r1
P-384 secp384r1
P-512 secp512r1

以 secp256K1 命名曲线为例,其使用的有限域大小是 256 比特,其安全性等同于 3072 比特长度的 RSA 算法。

DH 密钥协商算法实践

  1. 使用 dhparam 子命令生成参数文件:
# 生成一个 1024 比特的参数文件
$ openssl dhparam -out dhparam.pem -2 1024

# 查看参数文件内容
$ openssl dhparam -in dhparam.pem -noout -C
  1. 根据参数文件,使用 genpeky 子命令生成密钥对:
$ openssl genpkey -paramfile dhparam.pem -out dhkey.pem

# 查看密钥对文件内容
$ openssl pkey -in dhkey.pem -text -noout
DH Private-Key: (1024 bit)
    private-key: ## 私钥
        7f:1b:bc:8f:b6:ec:a7:11:00:fd:92:0d:ff:36:a5:
        73:a9:83:f1:b6:a6:8e:ed:83:13:e6:b9:39:09:69:
        3f:eb:4e:05:eb:37:ac:76:86:3e:08:65:50:7a:de:
        16:a6:cd:9e:36:c9:aa:0e:23:bb:37:a5:18:d7:e1:
        d6:64:fa:b3:c8:9f:2c:3b:35:14:d8:eb:19:5c:1b:
        33:34:5b:f9:c9:c0:35:4f:4b:b6:79:ec:fe:e0:19:
        64:e6:30:a8:14:6a:60:b8:b5:44:97:ef:cf:05:05:
        c6:70:d4:38:57:19:a2:b6:71:08:ee:cc:3f:66:b8:
        36:a1:a9:1b:65:38:56:72
    public-key: ## 公钥
        4d:0a:d1:67:bc:63:ec:16:8e:94:ee:a1:fd:8e:67:
        5e:20:fa:3c:65:b7:f6:91:ab:40:5d:34:fd:9d:0d:
        07:20:2d:a7:4b:4c:bf:4a:c3:6d:ca:9a:5f:6d:8c:
        19:2c:1d:52:5d:48:dd:03:54:b2:54:7f:2e:43:3b:
        c8:da:82:e5:c7:9f:5b:fe:81:5d:7a:e8:ff:d3:c9:
        a6:44:1a:51:95:9e:12:e7:4c:9e:c1:85:16:f8:96:
        e1:04:35:be:03:ca:6d:54:e7:3e:9d:2b:18:b2:6c:
        d4:43:8c:26:a8:a6:0d:77:6d:83:b0:ae:35:c5:61:
        73:7c:88:fa:1b:ef:e5:de
    prime: ## 大质数
        00:b7:46:0d:75:c9:34:57:f8:9c:30:1e:0d:ea:03:
        2c:a9:c3:cd:66:2a:04:9f:fb:ad:f6:4a:12:4f:80:
        9a:d0:d2:7f:f1:9b:df:69:48:a8:d8:07:ef:45:3f:
        2a:a5:da:8f:0c:16:92:ab:4e:0a:6e:09:05:64:d4:
        3d:49:87:61:4e:92:9c:1c:c8:f3:cf:c3:d1:44:2c:
        9a:05:a8:79:fd:61:3d:e7:17:24:82:34:d3:3e:71:
        91:4a:bb:e2:30:90:5a:9e:67:70:53:81:18:d0:92:
        d0:7d:62:06:7a:d1:d7:85:7b:8c:5a:11:f9:f3:e0:
        e5:e4:fb:a9:4f:ed:0e:7e:9b
    generator: 2 (0x2) ## 生成元

完整的 DH 密钥协商示例

# 通信双方的任何一方生成 DH 的参数文件,可以对外公开
$ openssl genpkey -genparam -algorithm DH -out dhp.pem
# 查看参数文件的内容,包括 p 和 g 等参数
$ openssl pkeyparam -in dhp.pem -text

# 发送方 A 基于参数文件生成一个密钥对(akey.pem)
$ openssl genpkey -paramfile dhp.pem -out akey.pem
# 查看密钥对内容
$ openssl pkey -in akey.pem -text -noout

# 发送方 B 基于参数文件生成一个密钥对 (bkey.pem)
$ openssl genpkey -paramfile dhp.pem -out bkey.pem
# 查看密钥对内容
$ openssl pkey -in bkey.pem -text -noout

# 发送方 A 拆出公钥文件 akey_pub.pem,私钥自己保存
$ openssl pkey -in akey.pem -pubout -out akey_pub.pem

# 发送方 B 拆出公钥文件 bkey_pub.pem,私钥自己保存
$ openssl pkey -in bkey.pem -pubout -out bkey_pub.pem

# 发送方 A 收到 B 发送过来的公钥,将协商出的密钥保存到 data_a.txt 文件
$ openssl pkeyutl -derive -inkey akey.pem -peerkey bkey_pub.pem -out data_a.txt

# 发送方 B 收到 A 发送过来的公钥,将协商出的密钥保存到 data_b.txt 文件
$ openssl pkeyutl -derive -inkey bkey.pem -peerkey akey_pub.pem -out data_b.txt

# 最终会发现,data_a.txt 和 data_b.txt 两个二进制文件是相同的

DH密钥协商算法示例图

ECC 算法实践

# 查看系统支持的椭圆曲线,通信双方需要选择一条都支持的命名曲线
$ openssl ecparam -list_curves

# 生成一个参数文件,通过 -name 指定命名曲线
$ openssl ecparam -name secp256k1 -out secp256k1.pem

# 默认情况下,查看参数文件只会显示曲线的名称
$ openssl ecparam -in secp256k1.pem -text -noout
ASN1 OID: secp256k1

# 显示参数文件里的具体参数
$ openssl ecparam -in secp256k1.pem -text -param_enc explicit -noout

参数文件示例:

Field Type: prime-field
## P 是大质数,ECC 所有点的大小控制在有限域中,几乎所有 ECC 操作都会对 P 进行取模运算。
Prime:
    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
    ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:fe:ff:
    ff:fc:2f
## A 和 B 是椭圆曲线公式
A:    0  
B:    7 (0x7)
## Generator,就是基点,由(gx, gy)组合
Generator (uncompressed):
    04:79:be:66:7e:f9:dc:bb:ac:55:a0:62:95:ce:87:
    0b:07:02:9b:fc:db:2d:ce:28:d9:59:f2:81:5b:16:
    f8:17:98:48:3a:da:77:26:a3:c4:65:5d:a4:fb:fc:
    0e:11:08:a8:fd:17:b4:48:a6:85:54:19:9c:47:d0:
    8f:fb:10:d4:b8
## Order,基点的打点次数
Order:
    00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
    ff:fe:ba:ae:dc:e6:af:48:a0:3b:bf:d2:5e:8c:d0:
    36:41:41
## Cofactor:该值等于椭圆曲线上所有点总数除以 n。
Cofactor:  1 (0x1)

ECDH 算法实践

# 生成参数文件
$ openssl ecparam -name secp256k1 -out secp256k1.pem

# A 和 B 各自生成一个 ECDH 私钥对文件
$ openssl genpkey -paramfile secp256k1.pem -out akey.pem
$ openssl genpkey -paramfile secp256k1.pem -out bkey.pem

# A 和 B 分解出公钥,将公钥发送给对方
$ openssl pkey -in akey.pem -pubout -out akey_pub.pem
$ openssl pkey -in bkey.pem -pubout -out bkey_pub.pem

# A 和 B 分别用自己的私钥和对方的公钥计算出同样的会话密钥
$ openssl pkeyutl -derive -inkey akey.pem -peerkey bkey_pub.pem -out data_a.txt
$ openssl pkeyutl -derive -inkey bkey.pem -peerkey akey_pub.pem -out data_b.txt

最终 data_a.txt、data_b.txt 包含的值是相同的。

数字签名

消息加密只是解决了传送消息的保密问题,而防止他人对传输文件进行破坏,以及确定发信人的身份需要使用数字签名。完善的数字签名应具备签字方不能抵赖、他人不能伪造、在公证人面前能够验证真伪的能力。

数字签名的作用:验证消息源及数据内容的真实性;签名能够被第三方验证,并确定消息内容的真实性和签名者身份。

数字签名体制包括三个算法:密钥生成算法、签名算法、验证算法。

数字签名原理

私钥拥有者使用密钥(注意不是加密操作)签署一条消息然后发送给任意的接收方,接收方只要拥有私钥对应的公钥,就能成功反解签署消息,由于只有私钥持有者才能 “签署” 消息,不能抵赖说这条签署消息不是他发送的,这就是数字签名技术的全部。

报文的发送方从报文文本中生成一个固定长度的散列值(报文摘要)。发送方用自己的私钥对这个散列值进行加密来形成发送方的数字签名。然后,这个数字签名将作为报文的附件和报文一起发送给报文的接收方。报文的接收方首先从接收到的原始报文中计算出散列值,接着再用发送方的公钥来对报文附加的数字签名进行解密。如果两个散列值相同,那么接收方就能确认该数字签名是发送方的。通过数字签名能够实现对原始报文的鉴别。

采用数字签名,能够保证:

  1. 信息是由签名者发送的;
  2. 信息自签发后到收到为止未曾有过任何修改;

数字签名算法

数字签名算法

数字签名技术的特点:

  1. 防篡改;
  2. 防抵赖;
  3. 放伪造;

数字签名与非对称加密的关系

一言以蔽之,数字签名就是将公钥密码反过来使用。

  • 都使用公钥体系,但实现的方法正好相反,使用的密钥对也不同。
  • 数字签名 使用的是发送方的密钥对,发送方用自己的私钥进行加密,接收方用发送方的公开密钥进行解密。一对多关系。
  • 非对称加密的加密解密则使用的是接收方的密钥对,这是多对一的关系:任何知道接收方公开密钥的人都可以向接收方发送加密信息,只有唯一拥有接收方私有密钥的人才能对信息解密。
  • 在实用过程中,通常一个用户拥有两个密钥对,一个密钥对用来进行数字签名,另一个密钥对用来进行非对称加密,这种方式提供了更高的安全性。

RSA 数字签名算法实践

# 生成一个 RSA 密钥对,密钥长度 1024 比特
$ openssl genrsa -out privatekey.pem 1024

# 从密钥对中分离出公钥
$ openssl rsa -in privatekey.pem -pubout -out pubilckey.pem

# 对 plain.txt 文件使用 sha256 Hash 算法和签名算法生成签名文件 signature.txt
$ echo "hello">>plain.txt
$ openssl dgst -sha256 -sign privatekey.pem -out signature.txt plain.txt

# 用相同的摘要算法和签名算法校验签名文件,需要对比签名文件和原始文件
$ openssl dgst -sha256 -verify pubilckey.pem -signature signature.txt plain.txt
Verified OK

OpenSSL 命令行进行签名的时候默认使用的是 RSAES-PKCS1-V1_5 填充标准,也可以指定 PSASSA-PSS 标准:

# 生成一个 RSA 密钥对
$ openssl genrsa -out privatekey.pem 1024
$ openssl rsa -in privatekey.pem -pubout -out pubilckey.pem

# 对 plain.txt 文件使用 sha256 Hash 算法和签名算法生成签名文件 signature.txt
# 指定 PSASSA-PSS 标准:-sigopt rsa_padding_mode:pss
$ echo "hello">>plain.txt
$ openssl dgst -sha256 -sign privatekey.pem -sigopt rsa_padding_mode:pss -out signature.txt plain.txt

#校验签名文件
$ openssl dgst -sha256 -verify pubilckey.pem -sigopt rsa_padding_mode:pss -signature signature.txt plain.txt
Verified OK

DSA 数字签名算法实践

生成参数文件和密钥对文件

# 生成参数文件,类似于 DH 参数文件
$ openssl dsaparam -out dsaparam.pem 1024

# 通过参数文件生成密钥对文件(privatekey.pem)
$ openssl gendsa -out private.pem dsaparam.pem

# 对私钥文件使用 des3 算法进行加密
$ openssl gendsa -out private2.pem -des3 dsaparam.pem

# 通过密钥对文件拆分出公钥
$ openssl dsa -in private.pem -pubout -out publickey.pem

显示参数、密钥对信息

# 显示私钥文件的信息,包括三个公共参数、公钥、私钥
$ openssl dsa -in private.pem -text

# 显示公钥文件的信息
$ openssl dsa -pubin -in publickey.pem -text

私钥文件示例:

read DSA key
Private-Key: (1024 bit)
priv: ## 密钥对私钥
    00:89:ca:36:b3:56:f4:61:91:1c:df:40:02:73:9b:
    40:cb:98:c2:e0:65
pub: ## 密钥对公钥
    35:ce:67:fa:79:aa:ce:64:18:6d:7e:fb:41:e6:ff:
    e3:59:7c:10:fc:a9:82:f7:a9:13:3b:22:96:3e:5f:
    f4:ad:09:56:bd:47:ab:2c:ca:11:81:99:ed:0c:b2:
    61:38:84:d9:d8:6b:6b:ab:67:2e:8e:01:fa:66:db:
    e6:de:3d:ac:d0:55:1c:b5:e8:20:63:56:c1:75:d8:
    11:a6:a3:2f:ed:46:ea:9c:63:d0:a3:03:ce:d0:20:
    f3:fe:0f:22:fe:9c:35:74:c3:6a:d3:f4:1f:86:e3:
    d1:ef:63:f4:8e:03:1a:3c:cb:f9:85:d1:c2:2a:b5:
    a0:26:61:53:2b:ac:0c:de
P: ## DSA 参数,大质数
    00:9e:3b:2d:3c:88:cb:3a:3e:93:74:9b:c5:85:52:
    8c:4c:03:89:ef:25:c5:f3:50:01:39:55:a0:71:18:
    3b:25:75:a6:b9:be:f7:ff:e0:b7:63:7b:4a:6f:70:
    60:b7:e7:05:e1:ad:ec:5f:17:a5:92:d1:f9:ef:72:
    33:37:53:2c:c1:30:0a:d5:c6:dc:1a:b6:d5:fc:73:
    7c:c0:d6:be:ba:73:b6:a7:ac:3a:7b:f0:46:f2:cd:
    1a:35:f3:19:d0:3d:b6:6e:7e:5a:c6:aa:2c:2a:ab:
    53:f6:7c:4b:84:e7:be:cb:3b:87:8a:ce:3f:83:cc:
    1a:44:a4:df:18:45:23:c6:45
Q: ## DSA 参数
    00:d7:3c:05:b4:6a:bd:a8:4d:25:be:3b:12:ac:c0:
    c4:50:23:c3:43:43
G: ## DSA 参数,数学表达式结果,数值来源于 p 和 q
    4b:ef:8d:33:d4:1e:65:fe:3a:25:92:2c:1b:27:b1:
    36:12:21:7d:59:80:66:a1:ec:ea:76:f2:6f:98:60:
    cd:7c:ec:e2:39:aa:4b:3a:86:8f:33:9a:ae:1d:3d:
    c6:b4:36:30:f0:e0:fe:76:ce:b5:c4:22:5c:2d:7f:
    63:b1:56:bb:b8:71:28:91:70:bc:ef:20:d8:cb:c4:
    b4:5a:e4:72:23:57:ae:fc:79:ac:f6:88:35:67:30:
    61:be:5d:e6:d7:35:72:f4:3c:b3:9a:d5:a0:75:0a:
    dd:b1:27:38:df:c5:05:ec:74:56:24:cf:99:36:cd:
    4f:6c:a2:6a:0d:25:a1:14
writing DSA key ## DSA 私钥
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQCeOy08iMs6PpN0m8WFUoxMA4nvJcXzUAE5VaBxGDsldaa5vvf/
4Ldje0pvcGC35wXhrexfF6WS0fnvcjM3UyzBMArVxtwattX8c3zA1r66c7anrDp7
8EbyzRo18xnQPbZuflrGqiwqq1P2fEuE577LO4eKzj+DzBpEpN8YRSPGRQIVANc8
BbRqvahNJb47EqzAxFAjw0NDAoGAS++NM9QeZf46JZIsGyexNhIhfVmAZqHs6nby
b5hgzXzs4jmqSzqGjzOarh09xrQ2MPDg/nbOtcQiXC1/Y7FWu7hxKJFwvO8g2MvE
tFrkciNXrvx5rPaINWcwYb5d5tc1cvQ8s5rVoHUK3bEnON/FBex0ViTPmTbNT2yi
ag0loRQCgYA1zmf6earOZBhtfvtB5v/jWXwQ/KmC96kTOyKWPl/0rQlWvUerLMoR
gZntDLJhOITZ2Gtrq2cujgH6Ztvm3j2s0FUcteggY1bBddgRpqMv7UbqnGPQowPO
0CDz/g8i/pw1dMNq0/QfhuPR72P0jgMaPMv5hdHCKrWgJmFTK6wM3gIVAInKNrNW
9GGRHN9AAnObQMuYwuBl
-----END DSA PRIVATE KEY-----

生成和验证签名

echo "hello">>plain.txt

# 签名
$ openssl dgst -sha256 -sign private.pem -out signature.txt plain.txt

# 验证签名
$ openssl dgst -sha256 -verify publickey.pem -signature signature.txt plain.txt
Verified OK

ECDSA 算法实践

# 1. 生成 ECDSA 私钥
$ openssl ecparam -name secp256k1 -genkey -out ecdsa_private_key.pem

# 显示私钥信息
$ openssl ec -in ecdsa_private_key.pem -text -noout
read EC key
Private-Key: (256 bit) ## 私钥长度
priv: ## 私钥
    00:d8:c5:c2:f0:19:d6:84:de:0f:5b:53:8d:16:98:
    5c:7b:e4:9e:c2:d9:7a:59:25:52:5a:cc:b9:78:62:
    86:2b:c2
pub: ## 公钥
    04:ed:ab:7e:93:3b:83:b3:02:ff:8d:45:2d:e4:8b:
    e0:79:6b:5a:44:77:8e:d2:72:7b:94:fb:f9:d7:7f:
    54:66:76:a3:a6:a5:f4:86:dd:7c:11:cd:26:66:d9:
    6a:62:21:08:d7:84:83:cc:9e:87:03:9c:27:93:f0:
    b5:1a:a9:50:57
ASN1 OID: secp256k1 ## 命名曲线信息

# 2. 生成私钥对应的公钥
$ openssl ec -in ecdsa_private_key.pem -pubout -out ecdsa_public_key.pem

# 显示公钥信息
$ openssl ec -pubin -in ecdsa_public_key.pem -text -noout

# 生成待签名文件
$ echo "hello">>plain.txt

# 3. 生成签名值
$ openssl dgst -sha256 -sign ecdsa_private_key.pem -out signature.txt plain.txt

# 4. 校验签名
$ openssl dgst -sha256 -verify ecdsa_public_key.pem -signature signature.txt plain.txt
Verified OK

算法安全性和性能

推荐密钥长度

密码学算法 推荐的密钥安全长度
AES 对称加密算法 128 比特
RSA 加密和签名算法 2048 比特
DSA 数字签名算法 2048 比特
ECC 椭圆曲线 256 比特

参考 ECRYPT II

密码学性能

关注密码学性能的两个途径:

  1. 参考专业的安全公司、大型互联网公司的密码学性能评测报告;
  2. 密码学性能测试;

性能评测

OpenSSL 中,可以使用 speed 子命令评测系统的能力和上限:

$ openssl speed rc4 aes rsa ecdh sha

# 让speed命令并发使用4核测试
$ openssl speed -multi 4 rsa

# 支持AES-NI指令的服务器可以加速AES计算,使用-evp开关激活硬件加速卡
$ openssl speed -evp aes-128-cbc

性能测试中的 evp 模式

evp 模式相当于调用 OpenSSL EVP 库(OpenSSL 基于底层密码库实现的一个高层次库)。

evp 模式能够基于硬件对算法运算进行调,能基于当前的 CPU 模型选择最佳的运行模式。

参考

  • 《密码学 —— 密码算法与协议(第 2 版)》
  • 《深入浅出 HTTPS:从原理到实践》
  • openssl
  • 加密通讯可以防止隐私窃取,为何我们都不用它?

欢迎关注我的其它发布渠道