我正在尝试使用OpenSSL提供的高级API进行加密工作。
通常这里效果很好。
/**
* Instructions for generating private key file and self signed certificate file.
*
* openssl genrsa -des3 -out keys.pem 2048
* openssl rsa -in keys.pem -out rsa.pem
* openssl req -new -x509 -key rsa.pem -out rsa-cert.pem -days 9999
*
* Compile
* g++ -ggdb -o exe.bex src.cpp -lcrypto
*/
//c standard library
#include <stdio.h>
#include <string.h>
//openssl library
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/x509.h>
//c++ standard library
#include <iostream>
#include <string>
int main(int argc, char* argv[]) {
FILE* fd = fopen("rsa.pem", "r");
X509* x509;
STACK_OF(X509)* x509_stack = sk_X509_new_null();
EVP_PKEY* key;
if (NULL != fd)
{
key = PEM_read_PrivateKey(fd, NULL, NULL, NULL);
}
fd = fopen("rsa-cert.pem", "r");
if (NULL != fd)
{
while (NULL != (x509 = PEM_read_X509(fd, NULL, NULL, NULL)))
{
sk_X509_push(x509_stack, x509);
}
fclose(fd);
}
OpenSSL_add_all_algorithms();
while (!std::cin.eof()) {
std::string msg;
//get input text
printf("Message to PKCS7 encrypt: ");
fflush(stdout);
std::getline(std::cin, msg);
if (223 < msg.length())
{
// SMIME_read_PKCS7 bug, need to be fixed.
//
// bt:
//#0 asn1_d2i_read_bio (in=0x60bc40, pb=0x7fffffffe238) at a_d2i_fp.c:286
//#1 0x00007ffff7aeb762 in ASN1_item_d2i_bio (it=0x7ffff7dc2da0, in=0x60bc40, x=0x0) at a_d2i_fp.c:113
//#2 0x00007ffff7b0201c in b64_read_asn1 (bio=0x60bc40, it=0x7ffff7dc2da0) at asn_mime.c:191
//#3 0x00007ffff7b02dd0 in SMIME_read_ASN1 (bio=0x60af00, bcont=0x7fffffffe350, it=0x7ffff7dc2da0) at asn_mime.c:527
//#4 0x00007ffff7b39db2 in SMIME_read_PKCS7 (bio=0x60af00, bcont=0x7fffffffe350) at pk7_mime.c:96
//#5 0x0000000000401668 in main (argc=1, argv=0x7fffffffe4b8) at ./crypto-pkcs.cpp:99
//
std::cout << "string too long " << msg.length() << std::endl;
continue;
}
if (msg.empty())
{
std::cout << "string too short" << std::endl;
continue;
}
//save input
BIO* bio_input_plain_text = BIO_new(BIO_s_mem());
BIO_write(bio_input_plain_text, msg.c_str(), msg.length());
BIO_flush(bio_input_plain_text);
//create PKCS7 object in the way of PKCS7_encrypt.
PKCS7* pkcs7_encrypt = PKCS7_encrypt(x509_stack, bio_input_plain_text, EVP_aes_256_cbc(), 0);
if (NULL == pkcs7_encrypt) {
std::cout << "PKCS7_encrypt returns NULL" << std::endl; return -1;}
//dump encryped info.
BIO* bio_encrypted_smime = BIO_new(BIO_s_mem());
if (SMIME_write_PKCS7(bio_encrypted_smime, pkcs7_encrypt, bio_input_plain_text, 0) != 1){ std::cout << "SMIME_write_PKCS7 failed" << std::endl; return -1;}
BIO_flush(bio_encrypted_smime);
//get internal data address
const char* encrypted = NULL;
BIO_get_mem_data(bio_encrypted_smime, &encrypted); //encrypted has no new resource, only the reflection of the internal BIO data.
//char encrypted[8 * 1024] = "";
//copy BIO to char array
//BIO_read(bio_encrypted_smime, encrypted, sizeof encrypted - 1); //if we read the data out of BIO, later we need to write it back, BIO_read deletes the internal data inside BIO
std::cout << "PKCS7_encrypt length:" << strlen(encrypted) << std::endl << encrypted << std::endl;
//please be careful while taking care of BIO object.
//if we call BIO_read against BIO to get out data, the operation will cause the data deleted in the BIO at the same time.
//here we recover BIO data.
//BIO_write(bio_encrypted_smime, encrypted, strlen(encrypted));
//BIO_flush(bio_encrypted_smime);
BIO* bio_pkcs7 = BIO_new(BIO_s_mem());
//read&load PKCS7 object from SMIME format.
PKCS7* pkcs7_smime = SMIME_read_PKCS7(bio_encrypted_smime, &bio_pkcs7);
if (NULL == pkcs7_smime) {std::cout << "SMIME_read_PKCS7 returns NULL" << std::endl;return -1;}
BIO_flush(bio_pkcs7);
BIO* bio_pkcs7_decrypt = BIO_new(BIO_s_mem());
//decrypt in the way of PKCS7_decrypt
if (0 == PKCS7_decrypt(pkcs7_smime, key, x509, bio_pkcs7_decrypt, 0)){ std::cout << "PKCS7_decrypt failed" << std::endl;return -1;}
BIO_flush(bio_pkcs7_decrypt);
//char decrypted[8 * 1024] = "";
//dump decrypted data.
//BIO_read(bio_pkcs7_decrypt, decrypted, sizeof decrypted - 1);
const char* decrypted = NULL;
//get internal data address
BIO_get_mem_data(bio_pkcs7_decrypt, &decrypted);
std::cout << "PKCS7_decrypt length: " << strlen(decrypted) << std::endl << decrypted << std::endl;
//cleanup, idiot! donot forget to release resource you piece of shit!
BIO_free(bio_pkcs7_decrypt);
BIO_free(bio_pkcs7);
BIO_free(bio_encrypted_smime);
PKCS7_free(pkcs7_smime);
PKCS7_free(pkcs7_encrypt);
}
X509_free(x509);
sk_X509_pop_free(x509_stack, X509_free);
return 0;
}
这段代码适用于openssl-1.0.1g。我尝试了线程和无线程配置选项。
但是如果我们在没有if-continue内容的情况下输入一个224长度的纯文本,加密仍然有效,它无法在SMIME_read_PKCS7的方法中解密,请检查上面的回溯评论。
如果输入长度为223,则加密S / MIME的长度为1200.
在224长度输入的情况下,S / MIME格式的加密结果的长度超过1200,SMIME_read_PKCS7无法处理该长度,检查上面的内部调用bt,但我没有找到任何文件或页面解释
SMIME_read_PKCS7的秘诀是什么?
缺陷1:
在@jww的帮助下。应该有
BIO_set_mem_eof_return(bio_encrypted_smime, 0);
在
PKCS7* pkcs7_smime = SMIME_read_PKCS7(bio_encrypted_smime, &bio_pkcs7);
缺陷2:
BIO_get_mem_data这个应该返回数据的长度,实际上我们需要根据长度来处理char *,除非你期望附加垃圾值,否则不要只依赖于null终止。
答案 0 :(得分:2)
SMIME_read_PKCS7的秘诀是什么?
嗯,真的没有秘密。它只是马车。根据{{3}}上的文档:
BUGS
The MIME parser used by SMIME_read_PKCS7() is somewhat primitive.
While it will handle most S/MIME messages more complex compound
formats may not work.
The parser assumes that the PKCS7 structure is always base64
encoded and will not handle the case where it is in binary
format or uses quoted printable format.
The use of a memory BIO to hold the signed content limits the
size of message which can be processed due to memory restraints:
a streaming single pass option should be available.
SMIME_read_PKCS7(3)上有一些好线程。