前言

公钥和私钥都是一串字符,长得也没有什么关联性,那么当拿到一个公钥和一个私钥后怎么验证它们两个是一对呢?先说说为什么会有这个疑问,最近在对接一些SDK时经常需要做签名和验证签名的工作,双方要相互提供公钥来完成后续的身份验证,测试过程中生成了多个密钥对,当时就在想如果我用错了怎样才能发现呢?有没有什么方法可以验证公钥和私钥是一对呢?再寻找方法的过程中发现 ssh-keygenopenssl 生成的密钥对差别居然这么大,但后来又发现他们居然还有联系,所以本文将刚刚提到的问题都总结一下,方便日后查阅。

解决这个问题的思路我么可以从密钥使用的方式入手,私钥加密公钥解密可以验证身份,公钥加密私钥解密可以秘密传递数据,也就是说公钥和私钥是配合工作的,如果我们用私钥加密一个数据,公钥可以解开就说明他们两个是一对的的。

其实还有一个更简单的办法,公钥是可以从私钥中提取出来的,反之则不行。在公钥密码学中,私钥和公钥之间的关系是一种数学上的单向函数。这种函数的特性使得从私钥生成公钥是可行的,而从公钥还原私钥是非常困难的,这被称为一个单向函数或单向映射,因此被认为是安全的。

有了这个前提我们就可以这样做,从私钥中提取出公钥,然后和要比对的公钥进行比较,如果一致就说明原来的公钥和私钥是一对啦。

ssh-keygen 和 openssl

ssh-keygenopenssl 是两个不同的工具,它们分别用于生成和处理不同类型的密钥和证书。以下是它们的一些主要区别:

  1. 功能和用途:

    • ssh-keygen:主要用于生成和管理SSH密钥对,包括RSA、DSA、ECDSA和Ed25519密钥。
    • openssl:是一个通用的密码学工具,可以用于生成和管理多种密码学对象,包括SSL/TLS证书、密钥、摘要和数字签名等。
  2. 支持的算法:

    • ssh-keygen:专注于SSH密钥生成,支持多种SSH密钥算法。
    • openssl:支持广泛的密码学算法,包括RSA、DSA、ECDSA、Diffie-Hellman、AES、SHA等,用于生成和操作各种密码学对象。
  3. 密钥格式:

    • ssh-keygen:生成的SSH密钥通常以OpenSSH格式存储。
    • openssl:支持多种密钥格式,例如PEM、DER等,可以处理不同类型的密钥和证书。
  4. 具体应用场景:

    • ssh-keygen:主要用于SSH连接,生成用于身份验证的密钥对。
    • openssl:更广泛地用于TLS/SSL证书、数字签名、加密和其他与通用密码学相关的应用。
  5. 具体命令和用法:

    • ssh-keygen:用于生成SSH密钥对的常见命令包括:
      ssh-keygen -t rsa -b 2048 -f myid_rsa
      
    • openssl:用于生成RSA密钥对的常见命令包括:
      openssl genpkey -algorithm RSA -out private-key.pem
      openssl rsa -pubout -in private-key.pem -out public-key.pem
      

以上的ChatGPT提供的内容,还是对比的挺全面的,从 openssl 生成密钥对的过程可以看出,确实可以从私钥中提取出公钥。

ssh-keygen 是我接触的最多的生成密钥对的命令,不管是github还是gitlab,或者一些其他的托管平台都会提到SSH Keys,所以每次我都会用 ssh-keygen 来生成,它是用于生成SSH连接密钥对最直接的选择,如果是想要使用更广泛的密码学操作,包括证书生成、签名和加密等,那么 openssl 提供了更大的灵活性。

两种证书的内容对比

前面说了两种命令生成的证书、格式、算法和用途都有区别,我们来看看利用上面的命令生成的证书内容有什么不同,先看 ssh-keygen 生成的私钥 myid_rsa 和公钥 myid_rsa.pub

# demo @ demo-ThinkPad-X390 in ~/tmp/rsa [17:58:47]
$ ssh-keygen -t rsa -b 2048 -f myid_rsa
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in myid_rsa
Your public key has been saved in myid_rsa.pub
The key fingerprint is:
SHA256:eLzG9lryO/AOdd05cDkixvBNCPFZRLRu0wp3bxZ7Vnk demo@demo-ThinkPad-X390
The key's randomart image is:
+---[RSA 2048]----+
|         +o =*   |
|          =.= . .|
|           B = + |
|       o  . o.=.+|
|      . S ...=.BE|
|       o.o .+ + B|
|        Bo.  . .*|
|       o *o    +.|
|        .o=o     |
+----[SHA256]-----+
# demo @ demo-ThinkPad-X390 in ~/tmp/rsa [19:22:03]
$ cat myid_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAr7gEjCdhA4at0tx0efpLunEOEGvK3ejJCh/dBwLfsvVJelwTeJjK
zI2nFvYGkJgndhWLuvwy6+ODnvASeNvXSvZZzXvvXjTF4JP3TRbATayFvS9mZgzOnxh/rU
z4P7ecLBfqZZkem+VBrMlv37yaIWf7co59dtrJJuAfZJFXyW//LQhNoEaqbZ/gvZls5pOX
r2foMnYg2HV0ayPlvz8cmVF8LIOqaGOc28nMQakVwO+nZhbSv1bGprIGcy4czc7UOp/m+o
t6mWJflpq3NBGiImyfaT2kxp1+pIdd6ISqoZqa9C2JzH7ZXVGjevbHuqup871yjUGapxDx
lDk9HjdSLwAAA9AlxLUDJcS1AwAAAAdzc2gtcnNhAAABAQCvuASMJ2EDhq3S3HR5+ku6cQ
4Qa8rd6MkKH90HAt+y9Ul6XBN4mMrMjacW9gaQmCd2FYu6/DLr44Oe8BJ429dK9lnNe+9e
NMXgk/dNFsBNrIW9L2ZmDM6fGH+tTPg/t5wsF+plmR6b5UGsyW/fvJohZ/tyjn122skm4B
9kkVfJb/8tCE2gRqptn+C9mWzmk5evZ+gydiDYdXRrI+W/PxyZUXwsg6poY5zbycxBqRXA
76dmFtK/VsamsgZzLhzNztQ6n+b6i3qZYl+Wmrc0EaIibJ9pPaTGnX6kh13ohKqhmpr0LY
nMftldUaN69se6q6nzvXKNQZqnEPGUOT0eN1IvAAAAAwEAAQAAAP8nH9HjYuE258XMLUnB
3vj9ii6XeWiyACLDHlUYOouUTQkPNXEgaOS+psuNUTok97vRjvqNp5mu6/liahGy10AYcM
GN/wpM73MfjdKCV455vPAKL4OdNTzML6g97eVYSol7R9foxy8JhwIYdsFyD4xB4+UYnKFS
1WP0Q/K5caMaNnazPF0X7JDSenEwBwl1bDAN5OKwZNzSzUpR/96fl1DAgnG9yp1/634WGt
IfjXLm13OSMrSHfwazNHoqAPkD0SQVJrm38ppvqArU0tBVfh/kvBkv7CCAE9mzhxusiKju
B0c/jrsRDu45AJX5wuqIWcZdfppATJjK10S20MgN5dkAAACBALYym7mpDVQJ2gedSjv/Pv
a+O/5b/20jawc3RFZdJAUWSGJTe9UqyHBHPlYj3/bHJVWIQ9S7s5AxpBtAEOsjeQf0VGFa
FALQwcUghx5r1uM5sAL06CT/5t3Ze8lcJozNW7yjbVZKjJK13GTDMrnPYTfPOc+I/6N3R+
SV2iKOwUQPAAAAgQDp9/2WvHOX2GKXan2BvMaexj6GYIQa8xEsqhtQGY9zxQ2d+9Z7YgDf
XnNanxv8XJ61komeP9KmhS+2TCt0trj8sXtQobDsDGjFcDx0jjBobU6zKvO4htoZP3lb8w
ZARgQoEANxqNOo8R61CsDQW4eM0ReAitKZ0GCcXbqNYCRTGwAAAIEAwEPdhAH4iH6V7IJn
Qs3pgf9AG9UubDe9mAwVNIircWTFO2EFaBpwP7UKgpNmmZjoD2lWEcr2Zxv6x8ILBlJqea
vCpGfLo0hwt2XZHgLRsTx8RmsbmYI2l8qnkMWyz5Lh3XAhNMZM4s9OvSJM6JPWYcedG5KY
G1/zbarjwkq+Gn0AAAAVc2h6QHNoei1UaGlua1BhZC1YMzkwAQIDBAUG
-----END OPENSSH PRIVATE KEY-----
# demo @ demo-ThinkPad-X390 in ~/tmp/rsa [19:22:10]
$ cat myid_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvuASMJ2EDhq3S3HR5+ku6cQ4Qa8rd6MkKH90HAt+y9Ul6XBN4mMrMjacW9gaQmCd2FYu6/DLr44Oe8BJ429dK9lnNe+9eNMXgk/dNFsBNrIW9L2ZmDM6fGH+tTPg/t5wsF+plmR6b5UGsyW/fvJohZ/tyjn122skm4B9kkVfJb/8tCE2gRqptn+C9mWzmk5evZ+gydiDYdXRrI+W/PxyZUXwsg6poY5zbycxBqRXA76dmFtK/VsamsgZzLhzNztQ6n+b6i3qZYl+Wmrc0EaIibJ9pPaTGnX6kh13ohKqhmpr0LYnMftldUaN69se6q6nzvXKNQZqnEPGUOT0eN1Iv demo@demo-ThinkPad-X390

接着看看 openssl 生成的私钥 private-key.pem 和公钥 public-key.pem

# demo @ demo-ThinkPad-X390 in ~/tmp/rsa [19:23:01]
$ openssl genpkey -algorithm RSA -out private-key.pem
......................................................................+++++
..................+++++

# demo @ demo-ThinkPad-X390 in ~/tmp/rsa [19:25:04]
$ openssl rsa -pubout -in private-key.pem -out public-key.pem
writing RSA key
# demo @ demo-ThinkPad-X390 in ~/tmp/rsa [19:26:18]
$ cat private-key.pem
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDjxEM9ypyLb0fT
xk7Ujdvj7efUjbLhzAcbfHNRZqzqif1rlTS4jz+tIDZBX9/HJkDcoz1dIolp/53S
CJtz+qBrkHMzsi6dcqd5jXUfT3Ts/FMTVsAKIkwafnw2Gbi2DGQx8WQ8Sn5+LlUP
FSCJa+2MTc21taiMP7CtF7iG6jFeGna3xeRTWAjMMS2xk9Zk37KMz8weU4ShdQ9q
mX7yuKHuZzUiWHmjbW/bi6abur3dfi/1jRSnFQF+r/gJoH1UXT4st/+fW425nWqO
ohSV+xw61a09fjECfjVDkMXtwiPCbbGNjD8WjLbilOJK3uBx5e7GIN1jZjVBqlH9
NqEk12HVAgMBAAECggEAIlcf8DwRGDDB+psGnaN5yMbA0lo+4y5Yz/sWg+pgHciK
TeMkHMN0TUzzAS5MVgFdfCachONFJpckm1sy3OynrTANPnkZ8CEuQPRj+UGIloaA
nATTVXPQV5pEbf+4yrHZlXIYs8JjifDAYFLGIhIEsce1QTVPYT3lDGPsLWM36ED6
XVnvJYsr00wpLlYy4uWikt4Q8ruKDnWSS8xeE7pj0bBaqYd8Gz4jfrJ94IgNIROQ
xnXb8aMG/o441ImrF6YXKn8jR/gjZXT1L4ZsD7fGTvLKBxxImBXL2eIoZ8usmTtX
eLEfYaJlzyXOcnYKKVtMQxRI+4ANJWWnMDgM8NTBTQKBgQD0hboW//hXGVcng3Pu
NfD7hUPHQKWWUHbchjRfxvxy/4itqGaWHtUrmc4PTXUxW71YaVA6qm2gvfV+jn83
o5k009vVbXYVvztNQq9wC0Fj2OYwQkxHMgAY9JdhZr6ghuEZ0+hNd5Jea9K3w62C
cY0mHqlvo55szhWmhuvzFtSUXwKBgQDudTFUjf7+qPsfYEX14udsKtfowrjepFLf
nQAbxj16PxpRBSV7S77gdlW1shKEreIPbm083bhU3ypCRs7ktR0Wki/uoQGYekHr
b8Nljh+rM9DxY83+HfLHAC/RjP8ZFrWJFgo6Whfr3jhlb8j3a4siLvqMj0d35BRv
M+HtsEhWSwKBgQC7M746aZG5LP9akvsofkeWaHpxi366S7QUNPLX+MHBUP1U5j5T
5BsTnFbzxDtapH+btlTj9SKlE4llcLbY4/khRqlhBsKG97bw+YLS5usTBDWG8Nrx
DsigUnGq8jwzLdAcM+3/kPGQNEBIzBiEa2bCrmrNu5hIeKZ+rYDqxKnPRQKBgQCh
MZuNOwfbAkPvq9bfJuml1zho4eiyqIZVHv6Jx/KbBrk1d7Mkq8l2VsgNsTiPdDPS
lqVFdeQ7izg34UHOnRfywBOizjgT6OFS1BRUPETwKHDgHMWNOJV7LIcmspdnUFCb
QMjdNTXifi/FclF537sH+pXoGpsHUqj1my7ozDe6DQKBgQDY9tJvXx5DRPVH2t60
0rl1df3PogoAhwjvK+Q/Y5vLyrL470JxMNPtW546aWXRL6INTg+OvsUqLJXEMlxl
HdHIe16orIGBrGcqHNGfkQPfddJVOo0eQCCk4jaLL9EP7+euA83vp6cP38rqgsL7
0+gt34iMPCP9dEKaJyjZPv+vFQ==
-----END PRIVATE KEY-----
# demo @ demo-ThinkPad-X390 in ~/tmp/rsa [19:26:21]
$ cat public-key.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA48RDPcqci29H08ZO1I3b
4+3n1I2y4cwHG3xzUWas6on9a5U0uI8/rSA2QV/fxyZA3KM9XSKJaf+d0gibc/qg
a5BzM7IunXKneY11H0907PxTE1bACiJMGn58Nhm4tgxkMfFkPEp+fi5VDxUgiWvt
jE3NtbWojD+wrRe4huoxXhp2t8XkU1gIzDEtsZPWZN+yjM/MHlOEoXUPapl+8rih
7mc1Ilh5o21v24umm7q93X4v9Y0UpxUBfq/4CaB9VF0+LLf/n1uNuZ1qjqIUlfsc
OtWtPX4xAn41Q5DF7cIjwm2xjYw/Foy24pTiSt7gceXuxiDdY2Y1QapR/TahJNdh
1QIDAQAB
-----END PUBLIC KEY-----

从私钥提取公钥

使用 openssl 命令从私钥提取公钥,上面已经展示过程了,它的公钥就是这样生成的

openssl rsa -pubout -in private-key.pem -out public-key.pem

使用 ssh-keygen 命令从私钥提取公钥可以这样做

ssh-keygen -y -f myid_rsa > compare_myid_rsa.pub

结果文件 compare_myid_rsa.pub 和 myid_rsa.pub 的内容一模一样,所以从私钥提取公钥可以作为检验密钥是否匹配的方法

OPENSSH和PEM两种密钥格式相互转化

之前说了ssh-keygenopenssl两个命令及生成的密钥区别,现在讲讲两种密钥的联系,OpenSSH 和 OpenSSL 使用的密钥对虽然有一些格式上的差异,但可以相互转换。OpenSSH 使用自己的私钥和公钥格式, 而 OpenSSL 通常使用 PEM 格式,可以通过下列命令转化

OpenSSH 格式私钥转换成 OpenSSL PEM 格式私钥

ssh-keygen -p -N "" -f myid_rsa -m PEM

OpenSSL PEM 格式私钥转换成 OpenSSH 格式私钥

ssh-keygen -p -N "" -f myid_rsa

OpenSSH 格式公钥生成 OpenSSL PEM 格式公钥

ssh-keygen -e -m PEM -f myid_rsa.pub > new-public-key.pem

OpenSSL PEM 格式公钥生成 OpenSSH 格式公钥

ssh-keygen -i -m PKCS8 -f new-public-key.pem > compare_myid_rsa.pub

公钥和私钥的匹配使用

匹配使用公钥和私钥之前先准备一个测试文件

echo "Hello World" > data.txt

加密和解密

使用公钥加密

openssl rsautl -in data.txt -out data_enc.txt -inkey public-key.pem -pubin -encrypt

使用私钥解密

openssl rsautl -in data_enc.txt -out data_dec.txt -inkey private-key.pem -decrypt

两个命令执行后都没有任何控制台输出,文件 data_enc.txt 中是加密后的内容,文件 data_dec.txt 是解密后的内容,成功解密后内容与 data.txt 文件内容一样

签名和验证

使用私钥签名

openssl dgst -sha256 -sign private-key.pem -out signature.bin data.txt

使用公钥验证签名

openssl dgst -sha256 -verify public-key.pem -signature signature.bin data.txt

验证通过后,会在命令行输出 “Verified OK” 字样,若不通过则输出 “Verification Failure” 字样

总结

  • 验证公钥私钥是否匹配的最便捷的方法是通过私钥提取公钥来和原公钥进行对比
  • 公钥加密私钥解密,私钥签名公钥验证签名,需要把公钥发给别人,私钥自己要保留好
  • 公钥和私钥常用的有OpenSSH和PEM两种格式,这两种格式的密钥可以相互转化
  • ssh-keygenopenssl 是两个不同的工具,前者用于管理SSH密钥更具体,后者使用范围更广更灵活


12-07 11:06