简介
本篇是继 OpenSSL: 简单易上手的RSA加解密 后的补充篇,实战篇。
在实际项目中,并没有像上篇文章写的那么简单,实际情况要复杂的多。万变不离其宗,抽丝剥茧,复杂事务的背后一定是有其本质原因和原理的存在,而我们就是挖掘原理,探索本质的福尔摩斯。
今天这篇文章,带领大家参与到实际项目中运用RSA加解密,在阅读下面内容之前,期望大家可以下载 openssl
的源码,或者下载我 上篇文中 的代码示例。
我下载的是 openssl-source-1.1.0f 这个版本的源码,正好对应我从 precompiled-openssl 下载的编译版本。
项目概述
该项目的开发语言仍然采用C语言来实现,我们借助 openssl
来模拟实际项目中的案例。
服务端使用 RSA 加密原始数据,然后采用 Base64 编码该加密数据经过 HTTP 传输给到客户端;
客户端接收到该数据,先使用 Base64 解码数据,然后再使用 RSA 解密数据,最终得到原始数据。
这里特别注意,客户端收到的数据大小可能会大于 128 字节,我们知道 RSA 加密明文最大长度 117 字节,而解密的最大值是 128 字节,所以超过该大小需要分段解密数据。
大概流程图如下:
很简单的一个项目,对吧,接着往下看吧 :)-
解个小惑
也许有些朋友会问,为毛 RSA 加密的明文大小是 117 字节,而解密的最大字节数是 128 字节,两者一样不是更好吗,至少好理解呀?
得出上面结论的前提是我们RSA密钥长度是 1024 位即 128 字节(1024/8=128),同理如果是 512 位的密钥,那么最大的 RSA 解密字节长度应该是(512/8)64 字节,最大加密的明文长度是(64-11)53 字节。
在 openssl 源码中,我们可以看到如下代码:
1 |
在 rsa_sign.c
文件中可以看到 RSA_sign 函数:
1 | int RSA_sign(int type, const unsigned char *m, unsigned int m_len, |
可以看出,RSA_PKCS1_PADDING
这种填充模式是占用了 11 个字节的,那么 127+11 正好也是 128 字节。
每次RSA加密的明文的长度是受RSA填充模式限制的,如下表:
填充方式 | 输入 | 输出 | 备注 |
---|---|---|---|
RSA_PKCS1_PADDING | 必须比RSA钥模长(modulus) 短至少11个字节, 也就是RSA_size(rsa) – 11,对于1024bit的密钥,RSA_size(rsa)=128字节,即明文为128-11=117字节;如果输入的明文过长,必须切割,然后填充。 | 和modulus一样长 | 最常用的填充方式 |
RSA_PKCS1_OAEP_PADDING | RSA_size(rsa) – 41 | 和modulus一样长 | 最优非对称填充OAEP,安全性是最高的 |
RSA_NO_PADDING | 可以和RSA钥模长一样长,如果输入的明文过长,必须切割,然后填充。 | 和modulus一样长 | - |
这里注意下面结论:
- 在不同的padding模式下,使用相同长度的密钥可以加密的数据最大长度不同;
- 在不同密钥长度下,使用相同的padding模式可以加密的数据最大长度也不同;
可以阅读 rfc2313 中关于 PKCS #1: RSA Encryption Version 1.5
的部分。
开战
实战代码主要在
main.c
文件中的example_rsa3()
函数中。
原始数据是字符串 www.veryitman.com
,如下还包括了公私钥。
1 | // 原始数据为字符串:www.veryitman.com |
对数据进行私钥加密,示例如下:
1 | // 私钥加密 |
私钥加密之后,进行 Base64 编码:
1 | char *base64_content; |
至此,上面两个步骤就模拟完成了服务端加密的过程。下面我们来继续模拟客户端解密的过程。
首先,对 Base64 编码之后的数据进行 Base64 解码。
1 | char *base64DecodeOutput; |
看一下打印结果:
1 | base64 decode content's length: 160 |
很明显,长度要大于 128,需要进行分段处理。
1 | // 最大解密长度 |
输出结果:
1 | ...... |
至此整个过程简单模拟结束。
大家如果感兴趣的话,可以实现分段加密的过程。我就不再演示这个过程了,后续加入到源代码中去。
问君能有几多愁,恰似一江春水向东流。