关于公钥与明文长度

最近一直在使用密码算法写认证协议相关的东西,发现明文空间过长会导致加解密不成功的情况。

栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<openssl/rsa.h>
#include<openssl/pem.h>
#include<openssl/err.h>
#define OPENSSLKEY "test.key"
#define PUBLICKEY "test_pub.key"
#define BUFFSIZE 1024
/*openssl genrsa -out test.key 1024*/
/*openssl rsa -in test.key -pubout -out test_pub.key*/
/*gcc -Wall -O2 -o pk pk.c -lcrypto -lssl*/
char* my_encrypt(char *str,char *path_key);//加密
char* my_decrypt(char *str,char *path_key);//解密
int main(void){
char *source="23333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333";
char *ptr_en,*ptr_de;
printf("source is :%s\n",source);
ptr_en=my_encrypt(source,PUBLICKEY);
//printf("after encrypt:%s\n",ptr_en);
ptr_de=my_decrypt(ptr_en,OPENSSLKEY);
printf("after decrypt:%s\n",ptr_de);
if(ptr_en!=NULL){
free(ptr_en);
}
if(ptr_de!=NULL){
free(ptr_de);
}
return 0;
}
char *my_encrypt(char *str,char *path_key){
char *p_en;
RSA *p_rsa;
FILE *file;
int flen,rsa_len;
if((file=fopen(path_key,"r"))==NULL){
perror("open key file error");
return NULL;
}
if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
ERR_print_errors_fp(stdout);
return NULL;
}
flen=strlen(str);
rsa_len=RSA_size(p_rsa);
p_en=(unsigned char *)malloc(rsa_len+1);
memset(p_en,0,rsa_len+1);
if(RSA_public_encrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_en,p_rsa,RSA_NO_PADDING)<0){
return NULL;
}
RSA_free(p_rsa);
fclose(file);
return p_en;
}
char *my_decrypt(char *str,char *path_key){
char *p_de;
RSA *p_rsa;
FILE *file;
int rsa_len;
if((file=fopen(path_key,"r"))==NULL){
perror("open key file error");
return NULL;
}
if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
ERR_print_errors_fp(stdout);
return NULL;
}
rsa_len=RSA_size(p_rsa);
p_de=(unsigned char *)malloc(rsa_len+1);
memset(p_de,0,rsa_len+1);
if(RSA_private_decrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_de,p_rsa,RSA_NO_PADDING)<0){
return NULL;
}
RSA_free(p_rsa);
fclose(file);
return p_de;
}

1557752156757

关于密钥与明文长度,从网上查阅的说法如下:

一次能加密的明文长度与密钥长度成正比:

len_in_byte(raw_data) = len_in_bit(key)/8 -11,如 1024bit 的密钥,一次能加密的内容长度为 1024/8 -11 = 117 byte。

所以非对称加密一般都用于加密对称加密算法的密钥,而不是直接加密内容。

实际上,RSA 算法本身要求加密内容也就是明文长度 m 必须满足 0<m<n,也就是说内容这个大整数不能超过 n,否则就出错。

那么如果 m=0 是什么结果?

普遍 RSA 加密器会直接返回全 0 结果,如果 m>n,运算就会出错。

因此,RSA 实际可加密的明文长度最大也是 1024bits,但问题就来了:

如果小于这个长度怎么办?

就需要进行 padding,因为如果没有 padding 用户无法确分解密后内容的真实长度,字符串之类的内容问题还不大,以 0 作为结束符,便于区分。

但对二进制数据就很难理解,因为不确定后面的 0 是内容还是内容结束符。

只要用到 padding,那么就会占用实际的明文长度,我们一般使用的 padding 标准有 NoPPadding、OAEPPadding、PKCS1Padding 等。

其中 PKCS#1 建议的 padding 就占用了 11 个字节,于是才有 117 字节的说法。

如果大于这个长度怎么办?

很多算法的 padding 往往是在后边的,但 PKCS 的 padding 则是在前面的,此为有意设计,有意的把第一个字节置 0 以确保 m 的值小于 n。

这样,128字节(1024bits)- 11 字节正好是117字节,但对于 RSA 加密来讲,padding 也是参与加密的。

所以,依然按照 RSA 1024 实际的明文只有 117 字节

关于 PKCS#1 padding 规范可参考:RFC2313 chapter 8.1。

我们在把明文送给 RSA 加密器前,要确认这个值是不是大于 n,也就是如果接近 n 位长,那么需要先 padding 再分段加密。

除非我们是“定长定量自己可控可理解”的加密则不需要 padding。

这就不难理解为什么公钥不适合用来做大规模数据传输的加密了,当然如果一定要用公钥来做,也可以通过分段加密或者数字信封的方式解决。

参考:

https://www.cnblogs.com/jpfss/p/8528406.html

https://www.cnblogs.com/aLittleBitCool/archive/2011/09/22/2185418.html