关于大数运算已经有一些可用的函数库。这里不再提供具体的实现。在数据加密头文件的示例中,定义了数据类型Huge,在安全的实现中,可以为Huge类型指定typedef别名以支持所选择的大整数抽象数据类型。其他的需求就只剩下替换整数计算中的运算符为Huge类型所支持的操作。为了达到说明的目的,在这里的实现中Huge类型用typedef定义为unsigned long,这种C语言内置的类型通常只能提供10位十进制数的支持。这意味着稍后的实现示例给出的实现只能支持最多5位整数的密钥。因此,虽然示例中的实现是可用的,但如果不把Huge重定义为大数类型,这个实现就不是安全的。
rsa_encipherrsa_encipher函数采用RSA算法将明文分组加密。
通过调用函数modexp来计算ab mod n的值,这里a代表明文分组,b和n代表公钥的e和n成员。为了提高执行效率,modexp使用称为二进制平方-乘的算法来计算模幂。
二进制平方-乘算法避免了当a和b都很大时计算ab时会出现的超大中间值。比如,假设当a、b和n都是包含200位数字的超大整数,计算ab mod n,其结果是一个40 000位的整数对200位的整数取模!因为最终得到的结果就是一个200位的整数,那么这里的目的就是要避免出现40 000位的整数的中间值。
用二进制平方-乘算法计算ab mod n,主要是多个开平方运算的结果(如下图)。首先,将b表示为二进制形式,然后从右边的位开始处理。对于b中的每一位,求出a的平方对n取模的结果,并将这个结果赋给a。每当遇到b中为1的位时,就将当前的a值乘上另一个寄存器y(初始值为1),并将结果再赋给y。一旦迭代至b的最高有效位,y的值就是ab mod n的结果。在整个计算过程中,产生的最大值是a2。因此,如果a是一个包含200位数字的整数,就不用处理大于400位的数字了。这相对于前面提到过的包含40 000位数字的整数来说已经是很大的优化了。下图中的阴影部分展示了计算511 mod 53 = 48 828 125 mod 53 = 20的过程。在这个计算过程中,相比511 = 48 828 125,所处理的最大数值只有422 = 1764。
图示:采用二进制平方-乘算法来计算模幂
rsa_encipher的时间复杂度为O(1),因为加密一个明文分组的所有步骤都能在恒定的时间内完成。由于分组的大小是固定的,因此modexp中的循环执行时间也是恒定的。
rsa_decipherrsa_decipher函数采用RSA算法将密文分组进行解密。
该操作通过调用modexp来解密。modexp计算ab mod n的结果,这里a是密文分组,b和n代表私钥成员d和n。处理过程同rsa_decipher中描述的一样。
rsa_decipher的时间复杂度为O(1),因为解密密文分组的所有步骤都可以在恒定的时间内完成。由于分组大小固定,因此,modexp中的循环执行时间也是恒定的。
示例:数据加密的头文件代码
/*encrypt.h*/ #ifndef ENCRYPT_H #define ENCRYPT_H /*在一个安全实现中,Huge 最少要400位10进制数字*/ typedef unsigned long Huge; /*为RSA公钥定义一个数据结构*/ typedef struct RsaPubKey_ { Huge e; Huge n; }RsaPubkey; /*为RSA私钥定义一个数据结构*/ typedef struct RsaPriKey_ { Huge d; Huge n; }RsaPriKey; /*函数声明*/ void des_encipher(const unsigned char *plaintext, unsigned char *ciphertext, const unsigned char *key); void des_decipher(const unsigned char *ciphertext, unsigned char *plaintext, const unsigned char *key); void rsa_encipher(Huge plaintext, Huge *ciphertext, RsaPubKey pubkey); void rsa_decipher(Huge ciphertext,Huge *plaintext, RsaPriKey prikey); #endif // ENCRYPT_H