(对不起,这很冗长)我正在尝试将OpenSSL支持添加到用Objective C for OS X 10.6(Snow Leopard)编写的Cocoa应用程序中。为了简化问题,我有一个小的包装器类,它包含各种BIO和密码上下文结构,AETOpenSSLWrapper。它看起来像以下
·H
@interface AETOpenSSLWrapper: public NSObject
{
BIO *writeBIO,encBIO;
EVP_CIPHER_CTX *ctx;
unsigned char *readWriteBuff;
}
@property (readwrite,assign) BIO *writeBIO,*encBIO;
@property (readwrite,assign) EVP_CIPHER_CTX *ctx;
@property (readwrite,assign) unsigned char *readWriteBuff;
-(id)init;
...
-(void)dealloc;
@end
的.m
@implementation AETOpenSSLWrapper
@synthesize writeBIO,encBIO,ctx,readWriteBuff;
-(id)init
{
self=[super init];
if(self)
{
writeBIO=BIO_new(BIO_s_file());
encBIO=...
ctx=...
buff=...
(error handling omitted)
}
return self;
}
@end
然后使用各种实用方法链接BIO,写入输出BIO,刷新等,特别是一个 - (void)pushEncryptingBIO链接加密过滤器BIO(已经用密钥,盐和初始向量初始化)
-(void)pushEncryptingBIO
{
writeBIO=BIO_push(encBIO,writeBIO);
}
最后还有我的dealloc例程。这是直接从openssl-1.0.1c发行版提供的 enc 程序中解除的
-(void)dealloc
{
if(readWriteBuff!=NULL)
OPENSSL_free(readWriteBuff);
if(writeBIO!=NULL)
BIO_free_all(writeBIO);
if(encBIO!=NULL) <----------- this looks wrong
BIO_free(encBIO); <---+
[super dealloc];
}
等效代码位于加密输入缓冲区的循环之前,以及openssl源代码树中apps / enc.c中MAIN()例程的末尾:
第657 - 658行
if (benc != NULL)
wbio=BIO_push(benc,wbio);
和第682 - 688行
end:
ERR_print_errors(bio_err);
if (strbuf != NULL) OPENSSL_free(strbuf);
if (buff != NULL) OPENSSL_free(buff);
if (in != NULL) BIO_free(in);
if (out != NULL) BIO_free_all(out);
if (benc != NULL) BIO_free(benc); <--- are we sure about this?
问题是(最后):是否应该调用BIO_free(benc)
(或我的代码中的BIO_free(encBIO)
),因为BIO被推送到writeBIO/out
链,该链被释放BIO_free_all
?看看BIO_free_all
的实现,它只是在BIO链上运行,减少引用计数和释放,因为它不会使指针空出来。这看起来像必须是一个bug,但显然我不愿意假设SSL维护者已经错过了这个而我没有。如果我离开BIO_free(encBIO)
电话,我会偶尔崩溃(十分之一),如果我不这样做,我就不会发生泄密。这是Apple事件处理程序,因此调试进一步复杂化。有什么建议吗?
答案 0 :(得分:1)
我前段时间有机会与BIO合作,我认为你是对的。从OpenSSL手册页:
BIO_free_all()释放整个BIO链,如果发生错误,它就不会停止释放链中的单个BIO。
此外,由于OpenSSL清理函数采用指向结构的指针,因此它们无法更改指针的值(即,它需要指针的地址才能这样做)。即使OpenSSL将指针参数设置为NULL,也只有副本为NULL,结果证明是无用的行为。因此,您可以将其设置为NULL。
为了给您一个具体的例子,下面的C ++代码用于使用PKCS#5标准加密纯文本。它接收密码作为参数,用于导出AES密钥。以下代码不会泄漏(使用valgrind检查)。
static int ENCRYPTION_FAILED = 1;
static const EVP_MD *MD = EVP_sha256();
static const EVP_CIPHER *CIPHER = EVP_aes_256_cbc();
static int base64Encrypt(const string& toEncrypt, const string& password, string& base64Encrypted)
{
static const char magic[]="Salted__";
int ret = 0;
EVP_CIPHER_CTX *ctx = NULL;
BUF_MEM *bptr = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
unsigned char salt[PKCS5_SALT_LEN];
char *encrypted = NULL;
/* Allow enough space in output buffer for additional block */
BIO *bMem = NULL;
BIO *b64 = NULL;
BIO *benc = NULL;
// setting bio context
if ( (bMem = BIO_new(BIO_s_mem())) == NULL ){
ret = ENCRYPTION_FAILED; goto err0;
}
if ( (b64 = BIO_new(BIO_f_base64())) == NULL ){
ret = ENCRYPTION_FAILED; goto err0;
}
BIO_push(b64,bMem);
// Generating salt
if (RAND_pseudo_bytes(salt, sizeof(salt) ) < 0){
ret = ENCRYPTION_FAILED; goto err0;
}
if ((password.size() == 0) && EVP_CIPHER_iv_length(CIPHER) != 0) {
ret = ENCRYPTION_FAILED; goto err0;
}
// writing salt to bio, base 64 encoded
if (BIO_write(b64, magic, sizeof magic-1) != sizeof magic-1 || BIO_write(b64, (char *)salt, sizeof salt) != sizeof salt) {
ret = ENCRYPTION_FAILED; goto err0;
}
// deriving key
if (!EVP_BytesToKey(CIPHER, MD, salt, (unsigned char *)password.c_str(), password.size(), PKCS5_DEFAULT_ITER, key, iv)){
ret = ENCRYPTION_FAILED; goto err0;
}
if ( (benc=BIO_new(BIO_f_cipher())) == NULL ){
ret = ENCRYPTION_FAILED; goto err0;
}
BIO_get_cipher_ctx(benc, &ctx);
if (!EVP_CipherInit_ex(ctx, CIPHER, NULL, NULL, NULL, 1)){
ret = ENCRYPTION_FAILED; goto err0;
}
if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1)){
ret = ENCRYPTION_FAILED; goto err0;
}
BIO_push(benc, b64);
// writing to mem bio
if (BIO_write(benc, (char *)toEncrypt.c_str(), toEncrypt.size()) != (int)toEncrypt.size()){
ret = ENCRYPTION_FAILED; goto err0;
}
if (!BIO_flush(benc)){
ret = ENCRYPTION_FAILED; goto err0;
}
BIO_get_mem_ptr(benc, &bptr);
if (bptr->length <= 0){
ret = ENCRYPTION_FAILED; goto err0;
}
encrypted = new char[bptr->length + 1];
memcpy(encrypted, bptr->data, bptr->length);
encrypted[bptr->length] = '\0';
base64Encrypted = encrypted;
delete[] encrypted;
if (benc != NULL) BIO_free_all(benc);
return 0;
err0:
if (benc != NULL) BIO_free_all(benc);
return ret;
}
正如您所看到的,我已经链接了三个BIO,并且对BIO_free_all(benc)的单次调用会清除所有BIO。
问候。