Wiki - RSA加密演算法Wiki - 欧拉函数Wiki - 模反元素ASN.1 格式标准RSA算法原理(二)

注意:

  • RSA 加密或签名后的结果是不可读的二进制,使用时经常会转为 BASE64 码再传输。
  • RSA 加密时,对要加密数据的大小有限制,最大不大于密钥长度。例如在使用 1024 bit 的密钥时(genrsa -out rsa_private_key.pem 1024),最大可以加密 1024/8=128 Bytes 的数据。数据大于 128 Bytes 时,需要对数据进行分组加密(如果数据超限,加解密时会失败,openssl 函数会返回 false),分组加密后的加密串拼接成一个字符串后发送给客户端。
  • 为了保证每次加密的结果都不同,RSA 加密时会在待加密数据后拼接一个随机字符串,再进行加密。不同的填充方式 Padding 表示这个字符串的不同长度,在对超限数据进行分组后,会按照这个 Padding 指定的长度填入随机字符串。例如如果 Padding 填充方式使用默认的 OPENSSL_PKCS1_PADDING(需要占用 11 个字节用于填充),那么明文长度最多只能就是 128-11=117 Bytes。
  • 一般默认使用 OPENSSL_PKCS1_PADDING。PHP 支持的 Padding 有 OPENSSL_PKCS1_PADDING、OPENSSL_SSLV23_PADDING、OPENSSL_PKCS1_OAEP_PADDING 和 OPENSSL_NO_PADDING。
  • 接收方解密时也需要分组。将加密后的原始二进制数据(对于经过 BASE64 的数据,需要解码),每 128 Bytes 分为一组,然后再进行解密。解密后,根据 Padding 的长度丢弃随机字符串,把得到的原字符串拼接起来,就得到原始报文。

原理

RSA 算法的可靠性基础:对极大整数做因数分解是很困难的。

RSA 是非对称算法,加解密使用不同的密钥。

两个密钥都可以用于加密,解密时需要使用另一个密钥。但是,通常用公钥加密私钥解密,因为公钥是近乎完全公开的,对于私钥加密的数据,有太多的人可以解密了。理论上 A 和 B 之间要通过 RSA 实现保密通信,需要 A 和 B 各自生成一组密钥,同时保管好自己的私钥;用对方的公钥加密要发送的消息,用自己的私钥解密对方发送过来的消息。

在签名的场景下,用私钥签名,公钥验签。

RSA 比 DES 等对称算法慢得多。一般在实际数据传输时,用 RSA 来加密比较短的对称密码,双方交换密码后再使用 DES 等对称算法传输数据。

互质关系

如果两个正整数,除了 1 以外没有其他公因子,就称这两个数是互质关系。比如 3 和 5,13 和 31 等。

欧拉函数

Wiki - 欧拉函数

欧拉函数:求小于 RSA 私钥长度 签名长度 rsa签名长度是多少_php 的正整数中与 RSA 私钥长度 签名长度 rsa签名长度是多少_php

例如,对应 8,与 8 互质的数有 1,3,5,7,所以 RSA 私钥长度 签名长度 rsa签名长度是多少_java_03

RSA 算法使用了欧拉函数的一个特例:如果 RSA 私钥长度 签名长度 rsa签名长度是多少_php 可以分解成两个互质的整数之积:RSA 私钥长度 签名长度 rsa签名长度是多少_java_05RSA 私钥长度 签名长度 rsa签名长度是多少_java_06 比如,RSA 私钥长度 签名长度 rsa签名长度是多少_数据_07

模反元素

Wiki - 模反元素

如果两个正整数 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_08RSA 私钥长度 签名长度 rsa签名长度是多少_java_09 互质,那么一定可以找到整数 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_10,使得 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_11RSA 私钥长度 签名长度 rsa签名长度是多少_java_09 整除:RSA 私钥长度 签名长度 rsa签名长度是多少_php_13 这时,RSA 私钥长度 签名长度 rsa签名长度是多少_数据_10 就叫做 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_08

欧拉定理证明当 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_16 为两个互素的正整数时,则有 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_17,其中 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_18 为欧拉函数(小于等于 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_19 且与 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_19

上述结果可分解为 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_21,其中 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_22 即为 RSA 私钥长度 签名长度 rsa签名长度是多少_php_23 关于模 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_19

举例

求整数 3 对同余 11 的模逆元素 x,RSA 私钥长度 签名长度 rsa签名长度是多少_RSA 私钥长度 签名长度_25 上述方程可变换为RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_26 在整数范围 RSA 私钥长度 签名长度 rsa签名长度是多少_php_27 内,可以找到满足该同余等式的 RSA 私钥长度 签名长度 rsa签名长度是多少_php_28 值为4,如下式所示RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_29 并且,在整数范围 RSA 私钥长度 签名长度 rsa签名长度是多少_php_27

故,整数3对同余11的模逆元素为4。

生成公钥和私钥

  • 随意选择两个大的质数 RSA 私钥长度 签名长度 rsa签名长度是多少_php_31RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_32RSA 私钥长度 签名长度 rsa签名长度是多少_php_31 不等于 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_32,计算 RSA 私钥长度 签名长度 rsa签名长度是多少_java_05
  • 根据欧拉函数,求得 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_36
  • 选择一个小于 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_37 的整数 RSA 私钥长度 签名长度 rsa签名长度是多少_java_38,使 RSA 私钥长度 签名长度 rsa签名长度是多少_java_38RSA 私钥长度 签名长度 rsa签名长度是多少_数据_37 互质。并求得 RSA 私钥长度 签名长度 rsa签名长度是多少_java_38 关于 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_37 的模反元素,命名为 RSA 私钥长度 签名长度 rsa签名长度是多少_java_43(求 RSA 私钥长度 签名长度 rsa签名长度是多少_java_43RSA 私钥长度 签名长度 rsa签名长度是多少_RSA 私钥长度 签名长度_45)。(模反元素存在,当且仅当 RSA 私钥长度 签名长度 rsa签名长度是多少_java_38RSA 私钥长度 签名长度 rsa签名长度是多少_数据_37
  • RSA 私钥长度 签名长度 rsa签名长度是多少_php_31RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_32

RSA 私钥长度 签名长度 rsa签名长度是多少_数据_50 是公钥, RSA 私钥长度 签名长度 rsa签名长度是多少_php_51

加密消息

假设客户端要向服务器发送消息 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_52,服务器的公钥是 RSA 私钥长度 签名长度 rsa签名长度是多少_phpRSA 私钥长度 签名长度 rsa签名长度是多少_java_38。客户端将消息 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_52 转换为一个小于 RSA 私钥长度 签名长度 rsa签名长度是多少_php 的非负整数 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_19,比如可以将每一个字转换为这个字的 Unicode 码,然后将这些数字连在一起组成一个数字。假如信息非常长的话,可以将这个信息分为几段,然后将每一段转换为 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_19。用下面这个公式他可以将 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_19 加密为 RSA 私钥长度 签名长度 rsa签名长度是多少_java_60RSA 私钥长度 签名长度 rsa签名长度是多少_java_61 计算 RSA 私钥长度 签名长度 rsa签名长度是多少_java_60 并不复杂。客户端算出 RSA 私钥长度 签名长度 rsa签名长度是多少_java_60

解密消息

得到消息 RSA 私钥长度 签名长度 rsa签名长度是多少_java_60 后,可以利用密钥 RSA 私钥长度 签名长度 rsa签名长度是多少_java_43 来解码。可以用以下这个公式来将 RSA 私钥长度 签名长度 rsa签名长度是多少_java_60 转换为 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_19RSA 私钥长度 签名长度 rsa签名长度是多少_RSA 私钥长度 签名长度_68 得到 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_19 后,可以将原来的信息 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_52

解码的原理是:RSA 私钥长度 签名长度 rsa签名长度是多少_RSA 私钥长度 签名长度_71 已知 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_72,即 RSA 私钥长度 签名长度 rsa签名长度是多少_java_73。由欧拉定理得:

RSA 私钥长度 签名长度 rsa签名长度是多少_RSA 私钥长度 签名长度_74

签名消息

RSA 也可以用来为一个消息签名。

对消息字符串的散列值(Message digest,用 MD5、SHA256 等算法求得的长度较短且固定的字符串)使用 RSA 的私钥计算签名(实际上仍然是加密消息)后,得到一个签名字符串,将其附加在消息字符串的合适位置后,一并发送。接收方使用对应的公钥可以从签名字符串中解密出原来的散列值,同时对原始消息再计算一次散列值。二者相比较,假如两者相符的话,则认为发信人持有正确的私钥,并且这个消息在传播路径上没有被篡改过。

密钥长度

用户应使用 1024 位密钥,证书认证机构应用 2048 位或以上。

特点

RSA 之所以叫非对称算法,是因为加密和解密的密钥不一样。任何一个密钥都可以用来加密。

公钥和私钥

通过私钥可以轻松计算出公钥,反之不行。

  1. 随机选择两个不相等的质数 RSA 私钥长度 签名长度 rsa签名长度是多少_php_31RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_32RSA 私钥长度 签名长度 rsa签名长度是多少_php_31 不等于 RSA 私钥长度 签名长度 rsa签名长度是多少_字符串_32,计算 RSA 私钥长度 签名长度 rsa签名长度是多少_java_05。 这里选择 103 和 349。RSA 私钥长度 签名长度 rsa签名长度是多少_java_05 = 35947。RSA 私钥长度 签名长度 rsa签名长度是多少_php
  2. 计算 RSA 私钥长度 签名长度 rsa签名长度是多少_php 的欧拉函数 RSA 私钥长度 签名长度 rsa签名长度是多少_java_03RSA 私钥长度 签名长度 rsa签名长度是多少_java_84
  3. 选择一个小于 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_37 的整数 RSA 私钥长度 签名长度 rsa签名长度是多少_java_38,使 RSA 私钥长度 签名长度 rsa签名长度是多少_java_38RSA 私钥长度 签名长度 rsa签名长度是多少_数据_37 互质,这里取 RSA 私钥长度 签名长度 rsa签名长度是多少_RSA 私钥长度 签名长度_89
  4. RSA 私钥长度 签名长度 rsa签名长度是多少_java_38 关于 RSA 私钥长度 签名长度 rsa签名长度是多少_数据_37 的模反元素,命名为 RSA 私钥长度 签名长度 rsa签名长度是多少_java_43(求 RSA 私钥长度 签名长度 rsa签名长度是多少_java_43RSA 私钥长度 签名长度 rsa签名长度是多少_RSA 私钥长度 签名长度_45)。最终转为 773d-1=35496k 这个二元一次方程,求得一组解(d,k)=(45,1)。
  5. RSA 私钥长度 签名长度 rsa签名长度是多少_数据_50 封装成公钥, RSA 私钥长度 签名长度 rsa签名长度是多少_php_51

加密和解密

用公钥加密时,私钥可以解密。反之亦然,私钥加密后的信息用公钥可以解密。

签名和验签

操作

如果是对接其他平台提供的标准接口,如果这个接口用到了 RSA 加密,则需要这个平台提供对应的公钥。

Linux 下通过 OpenSSL 生成 RSA 公钥和私钥

需要提前在 Linux 上安装 OpenSSL,默认生成在当前用户家目录下:

[root@VM_120_242_centos ~]# openssl 
OpenSSL> genrsa -out app_private_key.pem   1024  # 生成私钥
Generating RSA private key, 1024 bit long modulus
.++++++
........++++++
e is 65537 (0x10001)

OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem  # 生成公钥
writing RSA key
OpenSSL> exit

对于 PHP 可以直接使用上面生成的原始私钥。但是 Java 需要将私钥转换成 PKCS8 格式,然后将生成的 PKCS8 格式的私钥去除头尾、换行和空格,作为私钥字符串填入代码中:

OpenSSL> pkcs8 -topk8 -inform PEM -in app_private_key.pem -outform PEM -nocrypt -out app_private_key_pkcs8.pem # 私钥转成 PKCS8 格式

查看生成的文件:

[root@VM_120_242_centos ~]# ll
总用量 1064
-rw-r--r-- 1 root root    887 6月  14 11:25 app_private_key.pem
-rw-r--r-- 1 root root    272 6月  14 11:25 app_public_key.pem

查看公钥:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3//sR2tXw0wrC2DySx8vNGlqt
3Y7ldU9+LBLI6e1KS5lfc5jlTGF7KBTSkCHBM3ouEHWqp1ZJ85iJe59aF5gIB2kl
Bd6h4wrbbHA2XE1sq21ykja/Gqx7/IRia3zQfxGv/qEkyGOx+XALVoOlZqDwh76o
2n1vP1D+tD3amHsK7QIDAQAB
-----END PUBLIC KEY-----

转成 PKCS8 格式的公钥字符串为:

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3//sR2tXw0wrC2DySx8vNGlqt3Y7ldU9+LBLI6e1KS5lfc5jlTGF7KBTSkCHBM3ouEHWqp1ZJ85iJe59aF5gIB2klBd6h4wrbbHA2XE1sq21ykja/Gqx7/IRia3zQfxGv/qEkyGOx+XALVoOlZqDwh76o2n1vP1D+tD3amHsK7QIDAQAB

查看私钥:

-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC3//sR2tXw0wrC2DySx8vNGlqt3Y7ldU9+LBLI6e1KS5lfc5jl
TGF7KBTSkCHBM3ouEHWqp1ZJ85iJe59aF5gIB2klBd6h4wrbbHA2XE1sq21ykja/
Gqx7/IRia3zQfxGv/qEkyGOx+XALVoOlZqDwh76o2n1vP1D+tD3amHsK7QIDAQAB
AoGBAKH14bMitESqD4PYwODWmy7rrrvyFPEnJJTECLjvKB7IkrVxVDkp1XiJnGKH
2h5syHQ5qslPSGYJ1M/XkDnGINwaLVHVD3BoKKgKg1bZn7ao5pXT+herqxaVwWs6
ga63yVSIC8jcODxiuvxJnUMQRLaqoF6aUb/2VWc2T5MDmxLhAkEA3pwGpvXgLiWL
3h7QLYZLrLrbFRuRN4CYl4UYaAKokkAvZly04Glle8ycgOc2DzL4eiL4l/+x/gaq
deJU/cHLRQJBANOZY0mEoVkwhU4bScSdnfM6usQowYBEwHYYh/OTv1a3SqcCE1f+
qbAclCqeNiHajCcDmgYJ53LfIgyv0wCS54kCQAXaPkaHclRkQlAdqUV5IWYyJ25f
oiq+Y8SgCCs73qixrU1YpJy9yKA/meG9smsl4Oh9IOIGI+zUygh9YdSmEq0CQQC2
4G3IP2G3lNDRdZIm5NZ7PfnmyRabxk/UgVUWdk47IwTZHFkdhxKfC8QepUhBsAHL
QjifGXY4eJKUBm3FpDGJAkAFwUxYssiJjvrHwnHFbg0rFkvvY63OSmnRxiL4X6EY
yI9lblCsyfpl25l7l5zmJrAHn45zAiOoBrWqpM5edu7c
-----END RSA PRIVATE KEY-----