一、RSA
1. 算法原理
RSA算法是一个广泛使用的公钥算法。其密钥包括公钥和私钥。它能用于数字签名、身份认证以及密钥交换。RSA密钥长度一般使用1024位或者更高。RSA密钥信息主要包括[1]:
Ø n:模数
Ø e:公钥指数
Ø d:私钥指数
Ø p:最初的大素数
Ø q:最初的大素数
Ø dmp1:e*dmp1 = 1 (mod (p-1))
Ø dmq1:e*dmq1 = 1 (mod (q-1))
Ø iqmp:q*iqmp = 1 (mod p )
其中,公钥为n和e;私钥为n和d。在实际应用中,公钥加密一般用来协商密钥;私钥加密一般用来签名。
RSA简洁幽雅,但计算速度比较慢,通常加密中并不是直接使用RSA 来对所有的信息进行加密,最常见的情况是随机产生一个对称加密的密钥,然后使用对称加密算法对信息加密,之后用RSA对刚才的加密密钥进行加密。
使用RSA算法需要注意两个问题:
1. 加密位数。这个是在生成key时需要指定的,在调用加解密函数时会用到。
2. 填充模式。有三种
1) RSA_PKCS1_PADDING 填充模式,最常用的模式
要求:
输入 必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11
如果输入的明文过长,必须切割, 然后填充
输出 和modulus一样长
根据这个要求,对于512bit的密钥, block length = 512/8 – 11 = 53 字节
2) RSA_PKCS1_OAEP_PADDING
RSA_size(rsa) – 41
3) RSA_NO_PADDING 不填充
RSA_size(rsa)
2. 使用方法
(1) 公钥和私钥保存到文件中
第一步 生成公钥、私钥文件
生成KEY时,需要提供key的位数以及e变量因子。
通过PEM_write_RSAPublicKey,PEM_write_RSAPrivateKey这两个方法,把公钥和私钥以PEM格式写入文件。
这里有个问题,那就是内存释放问题。RSA_new与RSA_free,BN_new与BN_free配对,但即使这样还是会产生内存泄露,需要在整个程序结束的时候调用CRYPTO_cleanup_all_ex_data();
需要注意的是,CRYPTO_cleanup_all_ex_data()不能在potential race-conditions条件在调用(我理解的意思是当函数外部存在RSA结构体的时候,在函数内部执行CRYPTO_cleanup_all_ex_data()将导致函数外的RSA结构体也被清理掉),因此最好在程序结束的时候才调用。
第二步 执行加解密操作
公钥加密,首先调用PEM_read_RSAPublicKey方法,从PEM文件中把key读取到RSA的结构中,然后调用RSA_public_encrypt对数据分块加密,需要指定填充模式。
RSA_public_encrypt每次最多加密RSA_size(rsa)长度的数据,所以需要重复执行。
私钥解密,一样的道理,只不过调用的方法换成PEM_read_RSAPrivateKey和RSA_private_decrypt。
(2) 公钥和私钥保存到内存中
第一步 加密key
已经格式化的key,直接读取;如果没格式化,则需要做一些处理。
(1)公钥字符串开头要加上“-----BEGIN PUBLIC KEY-----\n”,结尾加上“\n-----END PUBLIC KEY-----\n”。否则会出现error:0906D06C:PEM routines:PEM_read_bio:no start line
(2)公钥字符串每隔64个字符要加一个换行,否则会报秘钥格式错误。
c++代码实现举例:
int nPublicKeyLen = strPublicKey.size(); //strPublicKey为base64编码的公钥字符串
for(int i = 64; i < nPublicKeyLen; i+=64)
{
if(strPublicKey[i] != '\n')
{
strPublicKey.insert(i, "\n");
}
i++;
}
strPublicKey.insert(0, "-----BEGIN PUBLIC KEY-----\n");
strPublicKey.append("\n-----END PUBLIC KEY-----\n");
第二步 执行加解密操作
公钥加密
调用PEM_read_bio_RSA_PUBKEY从内存中读取公钥到RSA中,私钥则调用PEM_read_bio_RSAPrivateKey方法。
二、AES
1. 算法原理
AES加密数据块分组长度必须为128比特,密钥长度可以是128比特、192比特、256比特中的任意一个(如果数据块及密钥长度不足时,会补齐)。AES加密有很多轮的重复和变换。大致步骤如下:
1、密钥扩展(KeyExpansion)
2、初始轮(Initial Round)
3、重复轮(Rounds),每一轮又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey
4、最终轮(Final Round),最终轮没有MixColumns。
AES中有几个参数需要注意:
1. 分块长度,固定128比特。编码时填写这个参数即可
2. 密钥长度,用户自己选择,一般使用128比特
3. ECB、CBC、CFB、OFB)
2. 使用方法
常用的模式是ECB和CBC
(1) ECB(Electronic Codebook,电码本)模式是分组密码的一种最基本的工作模式。在该模式下,待处理信息被分为大小合适的分组,然后分别对每一分组独立进行加密或解密处理。
(a)for循环使用:
(b)下面我们看下openssl中的源码实现:
(2) CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或操作后再加密,这样做的目的是增强破解难度。
openssl中提供的加密接口,是对整个输入数据加密,然后返回整个的加密结果,不需要for循环去分块加密,因为他们的前后块有关联。
(a)使用:
(b)源码:
//从上面的源码可以看出,cbc本质上和ecb差别不大,唯一区别是将前一次加密结果,与要加密的内容异或。因此,cbc的并行性较差,因为每次都要等待前一次的结果,而ecb则不用,速度较快。