1+1=10

扬长避短 vs 取长补短

OpenSSL小记

简介

  • 1995年,Eric Andrew Young 和 Tim J. Hudson 开发了 SSLeay。这是一个开源的SSL实现,它支持X.509v3证书、PKCS#10证书请求、SSL2和SSL3。(注:eay是Eric Andrew Young的首字母)
  • 1998年,OpenSSL项目启动,它基于SSLeay的代码进行开发。

OpenSSL可以分成三个主要部分:

  • 密码算法库:实现了目前大部分主流的密码算法和标准。主要包括对称算法、非对称算法、散列算法、数字签名和认证、X509数字证书标准、PKCS12、PKCS7等标准。
  • SSL协议库:完全实现SSL协议和TLS协议。
  • 命令行工具:基于密码算法库和SSL协议库实现的命令。包括各种算法的加密程序和各种类型密钥的产生程序、证书签发和验证程序、SSL连接测试程序等。

版本

在Qt中碰到的几个版本(注:3.0开始版本规则又发生改变,版本号不再用字母后缀):

版本 初始发布日期 备注
1.0.2 2015年1月22日 1.0.2u(2019年12月20日)
1.1.1 2018年9月11日 1.1.1w(2023年9月11日)
3.0 2021年9月7日 (2026年9月7日)

注:OpenSSL1.1和OpenSSL1.0二进制和源码都不兼容

动态库名称

OpenSSL库包含两个动态库(共享库)文件:

  • libssl
  • libcrypto

前者实现ssl的基本功能,后者是各种加密算法。

OpenSSL1.1之前的版本,在Windows下用MSVC编译的产物,名字比较怪异,是:

  • ssleay32 (等价于 libssl32)
  • libeay32

初次见到它是在Windows下,有些懵

好消息是,从OpenSSL1.1开始,在Windows下的命名也规范统一了

  • libssl-1_1(-x64).dll
  • libcrypto-1_1(-x64).dll

命令行工具

OpenSSL支持的命令列表,可以通过openssl help 查看

openssl help

它会返回内容很长,包括:

标准命令列表

Standard commands
asn1parse         ca                ciphers           cms               
crl               crl2pkcs7         dgst              dhparam           
dsa               dsaparam          ec                ecparam  
...

摘要命令

Message Digest commands (see the `dgst' command for more details)
blake2b512        blake2s256        gost              md4               
md5               rmd160            sha1              sha224   
...

以及

Cipher commands (see the `enc' command for more details)
aes-128-cbc       aes-128-ecb       aes-192-cbc       aes-192-ecb       
aes-256-cbc       aes-256-ecb       aria-128-cbc      aria-128-cfb
...

算法

对称加密算法(Cipher)

特点是文件加密和解密使用相同的密钥,即加密密钥也用作解密密钥。特点:加解密速度很快,但密钥丢失,会造成安全隐患。

  • https://www.openssl.org/docs/man1.1.1/man1/enc.html

通过openssl enc -list命令可以获取算法列表

  • AES
  • Blowfish
  • Camelia
  • Poly1305
  • SEED
  • CAST-128
  • DES
  • IDEA
  • RC2
  • RC4
  • RC5
  • Triple DES
  • GOST 28147-89
  • SM4

加解密使用

使用AES加密:

openssl enc -aes-256-cbc -salt -in input.txt -out encrypted.enc

解密

openssl enc -aes-256-cbc -d -in encrypted.enc -out decrypted.txt

加解密过程中会提示输入密码,类似下面这样:

enter AES-256-CBC decryption password:

非对称加密算法(Public-key cryptography)

非对称加密算法需要两个密钥:公开密钥(public-key)和私有密钥 (private-key)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密(公私钥互为加解密关系)。

相对于对称加密算法来说,非对称加密算法速度很慢。

  • RSA
  • DSA
  • Diffie-Heliman key exchange(迪菲-赫尔曼密钥交换)
  • Elliptic curve(椭圆曲线)
  • X25519
  • Ed25519
  • X448
  • Ed448
  • GOST R34.10-2001
  • SM2

对非对称加密算法,一些知识点:

  • 使用公钥操作数据(公钥加密,私钥解密) -> 加密
  • 使用私钥对原文的摘要操作(私钥加密,公钥解密) -> 签名
  • 公钥和私钥可以互相加密解密(对应上两条)
  • 公钥对外公开,但私钥一定不能泄露。

密钥操作

OpenSSL3 默认生成密钥格式(PKCS#8)和之前(PKCS#1)不同。

直观差异体现在密钥文件的开头:

PKCS#1(只用于RSA)

-----BEGIN RSA PRIVATE KEY-----

和PKCS#8(也适用于其他算法的私钥,使用OID进行区分)

-----BEGIN PRIVATE KEY-----

在OpenSSL3中,可以使用-traditional选项来生成老的格式。

两个格式具体差异,详见:DER格式小结

OpenSSL 1.1下

生成密钥(PKCS#1格式)

$ openssl genrsa -out rsa.key 1024
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDBqdNEz15gMCAH6FQTsqxFFExolLg2VUAdBxks7pl7aIIi4qE
....
/Wo83kTObo8lOMq7MPwFlCWoXNxYZpVdb3WU6eNNLYY=
-----END RSA PRIVATE KEY-----

查看私钥内容

$ openssl rsa -in rsa.key -text
RSA Private-Key: (1024 bit, 2 primes)
modulus:
    00:c1:a9:d3:44:cf:5e:60:30:20:07:e8:54:13:b2:
    ac:45:14:4c:68:94:b8:36:55:40:1d:07:19:2c:ee:
    99:7b:68:82:22:e2:a1:33:b0:db:92:94:be:f0:13:
    2b:03:ee:37:09:82:f1:3b:3d:cd:2b:2f:df:5e:8a:
....
  • https://www.openssl.org/docs/man1.1.1/man1/openssl-pkcs8.html

PKCS#1到PKCS#8转换(-nocrypt生成不带密码的私钥)

$ openssl.exe pkcs8 -in 1.pem -topk8 -out 2.pem -nocrypt

PKCS#8 到PKCS#1转换

$ openssl.exe rsa -in 2.pem  -out 3.pem
OpenSSL 3.0下

生成密钥(PKCS8格式):

$ openssl genrsa -out rsa.key  1024
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMXQYsn+DeflrheT
...
Lm9g7JNkTzCgiwQ=
-----END PRIVATE KEY-----

查看密钥:

$ openssl rsa -in rsa.key -text
Private-Key: (1024 bit, 2 primes)
modulus:
    00:c4:3d:a2:07:14:01:d2:68:20:f5:8b:72:89:4f:
    42:58:a9:70:0f:94:1e:76:47:d0:ea:be:93:c5:ae:
...

导出公钥:

$ openssl rsa -in rsa.key -pubout -out rsa.pub
$ openssl rsa -in rsa.key -pubout
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBqdNEz15gMCAH6FQTsqxFFEx
...
aW+wLBQ5O4mCIQyALQIDAQAB
-----END PUBLIC KEY-----
writing RSA key

注:-in -out 默认都是私钥,通过-pubout指定输出公钥;同样通过-pubin指定输入公钥。

PEM 与 DER转换

输出格式默认都是pem格式,如果要使用der格式,可以使用 -outform DER指定:

$ openssl rsa -in rsa.pem -outform DER -out rsa.der

加解密操作

使用公钥加密文件(openssl命令行只能使用公钥加密,其C API支持私钥加解密):

$ openssl pkeyutl -encrypt -in a.txt -pubin -inkey rsa.pub -out rsa_chiper.enc

即使是如下这样写,还是用的私钥文件中的公钥进行的加密:

openssl pkeyutl -encrypt -in a.txt -inkey rsa.key -out rsa_chiper.enc

使用私钥解密文件:

$ openssl pkeyutl -decrypt -in rsa_chiper.enc -inkey rsa.key -out a.txt.2

注意:

  • 填充方式可通过选项指定:-pkeyopt rsa_padding_mode:oaep

签名与验证

签名(输入必须是哈希值,该命令不会对输入内容执行哈希操作)

openssl pkeyutl -sign -in sha384.txt -inkey rsa.key  -out sha384.sig

验证

openssl pkeyutl -verify -in sha384.txt -pubin -inkey rsa.pub -sigfile sha384.sig

OpenSSH

注意,OpenSSH生成公钥私钥格式与OpenSSL不同!!不要混淆

它的私钥头部如下:

-----BEGIN OPENSSH PRIVATE KEY-----

它生成私钥的命令

ssh-keygen -t rsa -b 4096

散列函数(Cryptographic hash Functions)

散列函数 又称 哈希函数、摘要算法。它是一种单向算法,用户可以通过散列函数对目标信息生成一段特定长度的唯一的Hash值,却不能通过这个Hash值逆向获得目标信息。

特点:不可逆,加密后值很小(128位,256位,512位等)。

散列函数有两大家族:

  • MD:Message Digest Algorithm

  • MD4:1990年发布。早已被攻破。

  • MD5:Message Digest 5,1992年发布。早已被攻破,不适用于安全认证或数字签名。

  • SHA:安全散列算法(Secure Hash Algorithm),由美国国家安全局(NSA)设计,美国国家标准与技术研究所发布。

  • SHA-0:1993年发布后,但很快被撤回。

  • SHA-1:1995年发布,使用很广泛(用于 TLS, GnuPG, SSH, IPsec等)。2017年已经被荷兰CWI和Google攻破。
  • SHA-2:2001年发布,包括SHA-224, SHA-256, SHA-384, SHA-512, SHA512/224, SHA512/256。
  • SHA-3:2015年发布。

算法列表

  • https://www.openssl.org/docs/manmaster/man1/openssl-dgst.html

OpenSSL支持的算法列表,可以通过openssl dgst -list命令获得:

$ openssl dgst -list
Supported digests:
-blake2b512                -blake2s256                -md4
-md5                       -md5-sha1                  -mdc2
-ripemd                    -ripemd160                 -rmd160
-sha1                      -sha224                    -sha256
-sha3-224                  -sha3-256                  -sha3-384
-sha3-512                  -sha384                    -sha512
-sha512-224                -sha512-256                -shake128
-shake256                  -sm3                       -ssl3-md5
-ssl3-sha1                 -whirlpool

也可以通过openssl list -digest-algorithms命令(这个返回的列表比较长,多出来的别名?):

$ openssl list -digest-algorithmsRSA-MD4 => MD4
RSA-MD5 => MD5
RSA-MDC2 => MDC2
RSA-RIPEMD160 => RIPEMD160
RSA-SHA1 => SHA1
RSA-SHA1-2 => RSA-SHA1
RSA-SHA224 => SHA224
RSA-SHA256 => SHA256
RSA-SHA3-224 => SHA3-224
RSA-SHA3-256 => SHA3-256
RSA-SHA3-384 => SHA3-384
RSA-SHA3-512 => SHA3-512
RSA-SHA384 => SHA384
RSA-SHA512 => SHA512
RSA-SHA512/224 => SHA512-224
RSA-SHA512/256 => SHA512-256
RSA-SM3 => SM3
BLAKE2b512
BLAKE2s256
id-rsassa-pkcs1-v1_5-with-sha3-224 => SHA3-224
id-rsassa-pkcs1-v1_5-with-sha3-256 => SHA3-256
id-rsassa-pkcs1-v1_5-with-sha3-384 => SHA3-384
id-rsassa-pkcs1-v1_5-with-sha3-512 => SHA3-512
MD4
md4WithRSAEncryption => MD4
MD5
MD5-SHA1
md5WithRSAEncryption => MD5
...

生成摘要

要获得一个文件的md5值,可以使用dgst命令:

$openssl dgst -md5 dbzhang800.txt
MD5(dbzhang800.txt)= 520e37bcab36e7f6c63a9fe8571fef5c

或者直接用md5命令

$openssl md5 dbzhang800.txt

将结果以二进制输出到文件

$openssl dgst -md5 -binary -out dgst.out dbzhang800.txt

使用16进制编辑器查看dgst.out结果

00000000: 520e 373f 3f36 3f3f 3f3a 3f3f 571f 3f5c
00000010: 0a

默认的dgst是SHA256:

$ openssl dgst dbzhang800.txt
SHA256(dbzhang800.txt)= a327231c982850cb61eecfb3da89aa6831d09d2c9e209acb58658faa78ccfeeb

加密后的值被称为hash值,hash值用于确保数据的完整性,但是不能保证数据传输存在身份认证机制。

数字签名

数字签名它是基于非对称密钥加密技术与数字摘要算法技术的应用,只有私钥拥有者才能产生别人无法伪造的一段数字串。

发送方通过私钥加密后数字签名发送给接收方,接收方使用公钥解密,通过对比解密后的摘要和使用Hash函数对数据电文进行计算的摘要 判定其是否被篡改。

生成签名

$ openssl dgst -sha256 -sign rsa.key -out signature.sign a.txt

验证签名

$ openssl dgst -sha256 -verify rsa.pub -signature signature.sign a.txt
Verified OK

结果为 Verification failure 或 Verified OK

OpenSSL 作为CA

配置文件

在Ubuntu下,配置文件在 /etc/ssl/openssh.cnf

在Windows下安装OpenSSL-Win32,安装目录下有一个配置文件openssh.cnf

如果报错,可通过环境变量来指定它。

set OPENSSL_CONF=[path-to-OpenSSL-install-dir]\openssl.cnf

[ ca ]
default_ca  = CA_default        # The default ca section

####################################################################
[ CA_default ]

dir     = ./demoCA      # Where everything is kept
certs       = $dir/certs        # Where the issued certs are kept
crl_dir     = $dir/crl      # Where the issued crl are kept
database    = $dir/index.txt    # database index file.
#unique_subject = no            # Set to 'no' to allow creation of
                    # several certs with same subject.
new_certs_dir   = $dir/newcerts     # default place for new certs.

certificate = $dir/cacert.pem   # The CA certificate
serial      = $dir/serial       # The current serial number
crlnumber   = $dir/crlnumber    # the current crl number
                    # must be commented out to leave a V1 CRL
crl     = $dir/crl.pem      # The current CRL
private_key = $dir/private/cakey.pem# The private key
...

建立CA目录

.
`-- myCA/
    |-- index.txt
    |-- newcerts/
    |-- private/
    `-- serial
  • private/ 存放证书私钥
  • serial 里面写入序列号,比如可以写入“8101”或其他数字,后期递增 echo 8101 > serial

CA自签名证书

生成私钥,并放入我们在配置文件中指定的位置

openssl genrsa -des3 -out myCA\private\cakey.pem 2048

生成CA证书请求

openssl.exe req -new -days 7300 -key myCA\private\cakey.pem -out myCA\careq.pem

签名证书,将其放置到配置文件期望的位置

openssl.exe ca -selfsign -in myCA\careq.pem -out myCA\cacert.pem

在该步骤中,配置文件开始起作用了,当前目录结构:

.
`-- myCA
    |-- cacert.pem
    |-- careq.pem
    |-- index.txt
    |-- index.txt.attr
    |-- index.txt.old
    |-- newcerts
    |   `-- 8101.pem
    |-- private
    |   `-- cakey.pem
    |-- serial
    `-- serial.old

接下来,该CA就可以为其他csr生成证书了。

OpenSSL命令行参数备忘

  • 非对称算法:-in -out 默认都是私钥,通过-pubout指定输出公钥;同样通过-pubin指定输入公钥。

  • 通过s client可以读取网站的证书

    $ openssl s_client -connect blog.debao.me:443

  • x509和pkcs12这两个命令可用于操作证书,比如证书格式之间的转换:

    openssl pkcs12 -export -out certificate.pfx -inkey privatekey.key -in cert.crt

  • 生成 证书请求文件 (回答它的提问,完成过程),而后用csr去找CA机构申请正式的证书。

openssl req -new -key x.key -out x.csr

Qt 与 OpenSSL

Qt官方提供的Windows下的安装包,默认不打包OpenSSL。在Qt5.13之前,如果需要QSslScocket功能,需要手动安装OpenSSL。安装时需要注意版本!!!

这块有点乱,先简单记录。要验证的话,可能需要结合Qt源码,以及Qt的bug来看才行

  • Qt 从 5.2.0 起,不再官方支持 OpenSSL 低于 1.0.0 的版本。
  • Qt 从 5.10起,官方二进制包开始使用OpenSSL 1.1
  • Qt 从 5.12.4起,官方二进制包开始使用 OpenSSL1.1.1 的。
  • Qt 从 6.2.0 起,开始支持 OpenSSL3.0(自己编译Qt)
  • Qt 从 6.5.0 起,官方二进制包开始使用 OpenSSL 3.0。

简单说:

OpenSSL 3.0(Qt >= 6.5.0)
OpenSSL 1.1.x(Qt >= 5.10 Qt < 6.5.0)
OpenSSL 1.0.2(Qt < 5.10)

注: Qt 5.13起,在Windows下开始增加schannel作为ssl后端,可作为 openssl 的替代。Qt6.3起,编译Qt时,schannel插件已默认启用。如果不需要OpenSSL的扩展功能的话,新版本的Qt程序可以不用纠结OpenSSL的问题,使用Schannel就行。

其他

Git

Windows下安装的Git,会自带 openssl,这个东西功能不全,要避免使用。

钥yào yuè

密钥中的“钥”,读音争议很大 。

《现代汉语词典(第7版)》:【密钥】mìyuè (口语中多读mìyào)。

参考

  • https://www.openssl.org/docs/man3.0/
  • https://en.wikipedia.org/wiki/OpenSSL
  • https://www.feistyduck.com/library/openssl-cookbook/online/
  • https://blog.csdn.net/zhihao_li/article/details/131068038
  • https://unix.stackexchange.com/questions/296697/how-to-encrypt-a-file-with-private-key

Comments