本人使用java做后台,后台使用同一公钥文件(格式为ANS.1 DER编码方式 长度为2048模长 ),加密111111(NO_PADDING)字符串得到结果 A55F50..... 开头的16进制编码,但是在ios平台使用openssl和同一公钥文件加密111111始终得不到A55F50开头的结果,代码如下,请大神帮忙看看....
#define MODULUS "CF4D10C2C011FCF1EE8F660C9B8AD7E7C8BAAB285C8168F2B355145CCFEAE4E555AC1C4926B00FFF23EC49800B159040F05BA1CC25F4D997311048292904B4015E5A419C0CFE21CC08B144575CC65BD05D5B36EECC190592BD66DACA68B49A3B0D2B1ACE4D26F50339DB834004FAABF399BD66296CA85C8F3E840E1864036A2CDDBDC5188CCF6C23928AA699C3564DB1F83561501A2C8033A34516161DF27B9C8357041291B39203C53E87027F2024A07F3DEBD41FB286CEB4824659347BF2863A6688497E56BFE4B417992F026719D2A199EC6481983636D10FBF74B8011EE5A8D27EAFE7A8E7B33FFED35167783B5753C74C2EBA7C36D134F1090EACEBC225" #define PUBLIC_EXPONENT 65537 int ret, flen; BIGNUM *bnn, *bne,*bnd; bnn = BN_new(); bne = BN_new(); //bnd = BN_new(); BN_hex2bn(&bnn, MODULUS); BN_set_word(bne, PUBLIC_EXPONENT); //BN_hex2bn(&bnd, MODULUS); RSA *r = RSA_new(); //RSA* r = RSA_generate_key( 2048 ,RSA_F4,0,0); r->e = bne; r->n = bnn; //r->d = bnd; RSA_print_fp(stdout, r, 5); flen = RSA_size(r);// - 11; /*out = (char *)malloc(flen); bzero(out, flen);*/ //memset(out, 0, flen); char *sp = (char *)malloc(flen); bzero(sp, flen); //memset(sp, 0, flen); char *message = "111111"; BIGNUM *rs; rs = BN_new(); ret = RSA_public_encrypt(flen, (unsigned char *)message , (unsigned char *)sp, r, RSA_NO_PADDING); if (ret > 0) { //BN_bin2bn((unsigned char *)sp, flen, rs); //NSLog(@"Encrypt OK, sp=%s",BN_bn2hex(rs));//转为16进制 unsigned char *tmpData; tmpData=(unsigned char *)sp; for (int i=0; i<ret; i++){ printf("%02X", *tmpData); tmpData++; } //BN_bin2bn((unsigned char *)sp, flen, rs); //NSLog(@"Encrypt OK, sp=%s",BN_bn2hex(rs));//转为16进制 } RSA_free(r);
验证的方式是使用配对的私钥能够解密,解密后的原文一致就正确,跟密文是否一致无关。
我的加密方式是NO_padding ,而且我使用rsa测试软件和我java加密的结果一样,你还能说跟密文是否一致无关吗
@TuWei: 在你的代码中:BN_num_bytes(r->n) 等于多少? flen 又等于多少?
@Launcher: 两个都是256
@TuWei: 不用你算了,我给你计算了,是 256,你的代码有 BUG(缓冲区溢出),你为了保证 256 字节长度,你的 RSA_public_encrypt 的第一个参数传入了 256,但是,你的第二个参数 message 只有 6 ,那么在使用 RSA_NO_PADDING 时,RSA_padding_add_none 方法会执行 memcpy(to,message,(unsigned int)flen) 语句,因为你的 message 只有前 6 个字节是相同的,后面的内容属于未正确分配的内存,其内容不定,这是你代码的 BUG(缓冲区溢出)。你应该将 message 扩展到 256,6 字节以后的内容用 0 或者别的值来填充。
@Launcher: 我应该将 char *message = "111111"; 改为char message[256] = "111111"; ?
我改了后貌似结果还是不对。
@TuWei: 我这样跟你说吧,你在 openssl 传入的 256 字节内容,必须同你的其它平台使用 RSA 加密时使用的 256 字节内容一致。
@Launcher: 关键我对c是真的不熟,java中我就是直接加密111111,c这里真不知怎么处理啊
@TuWei: java 中采用 no padding 后,如果你也是传入的 6 个字节的 "111111",那么根据你使用的 key 为 2048,那么 java 内部应该也有机制将你的 6 个字节补足 256 字节,最有可能的填充方式就是,在 6 字节后全部填写为 0 。
另外,我不知道你的 java 代码怎么写的,因为 java 中的 string 是 unicode 编码,要加密,首先需要将 string 转换成 byte 数组,如果采用 unicode 编码,那么转换出的就是 12 个字节,而不是 6 个字节(在 C 就是 char)。这样,不管填充的值是否相同,至少你前 12 个字节的内容就是不一样的。
从代码中你也看到了,设置 NO Padding 不代表不 Padding,这个动作只是改由你自己来完成,java 中可能做了封装,让你省去这一步。
@Launcher: java中是将字符串转为了byte数组然后加密的
@Launcher: 大神求救啊,还是搞不定啊
@Launcher:
char message[256] = {'1','1','1','1','1','1',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
得到的还是"111111"加密的结果
@TuWei: 你在 openssl 中传入相同的 message,两次加密的结果是不是一样的?
@TuWei: 你在 java 中转换出来的 byte 数组长度是多少?内容是什么?
@Launcher:
byte[] bytes = "111111".getBytes(); for(int i = 0; i < bytes.length;i++){ System.out.println(bytes[i]); }
结果:
49
49
49
49
49
49
@TuWei: byte[] bytes = "111111".getBytes(); 你这里这个 bytes 也得是 256 大小,其余的用 0 填充。
@Launcher: 这个不清楚,反正我这样加密
byte[] enBytes = cipher.doFinal("111111".getBytes());
出来的结果和网上下载的rsa计算工具得到的结果一样,而且送给加密机解密也正确,就是说java这端加密是没有问题的
@Launcher: 下面是doFinal方法的解释
The bytes in the input
buffer, and any input bytes that may have been buffered during a previous update
operation, are processed, with padding (if requested) being applied. If an AEAD mode such as GCM/CCM is being used, the authentication tag is appended in the case of encryption, or verified in the case of decryption. The result is stored in a new buffer.
Upon finishing, this method resets this cipher object to the state it was in when previously initialized via a call to init
. That is, the object is reset and available to encrypt or decrypt (depending on the operation mode that was specified in the call to init
) more data.
Note: if any exception is thrown, this cipher object may need to be reset before it can be used again.
@TuWei:RSA 在不采用 Padding 的模式下,2048 位,每次输入的明文必须是 2048 / 8 = 256 字节。某些框架在用户输入少于 256 字节时,可能采用帮助用户自动补足 256 字节的“黑箱操作”,而使用什么值来补足,由框架实现,我不知道。但是,openssl 不会这么做,它要求用户必须传入 256 字节的完整数据。
另外,.Net 中的 RSA 处于安全考虑屏蔽了 No Padding 模式,从这里也可以看到,具体实现方式依各框架而定。
经过在 openssl 1.0.1e 上的测试,上述表述正确。因此,如果你发现了差异,那么你需要从上述表述中找到限定条件(2048位,RSA_NO_PADDING,256 字节明文,PublicKey),然后比较你在不同框架下的这些限定条件是否一致。
最后,祝你好运。
@Launcher: 好吧,谢谢了