使用OpenSSL C库在多个线程中生成椭圆曲线密钥对(EC_-key_-Generate_-key)
我想生成许多ec密钥对。加快了一点进程,我重写了我的应用程序,为这个作业使用了多个线程。以下是每个线程生成密钥的代码片段:使用OpenSSL C库在多个线程中生成椭圆曲线密钥对(EC_-key_-Generate_-key),c,multithreading,openssl,ecdsa,C,Multithreading,Openssl,Ecdsa,我想生成许多ec密钥对。加快了一点进程,我重写了我的应用程序,为这个作业使用了多个线程。以下是每个线程生成密钥的代码片段: (...) EC_KEY* _ec_key = EC_KEY_new(); EC_GROUP* ec_group_new = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); const EC_GROUP* ec_group = ec_group_new; if (!EC_KEY_set_group(ec_key,ec
(...)
EC_KEY* _ec_key = EC_KEY_new();
EC_GROUP* ec_group_new = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
const EC_GROUP* ec_group = ec_group_new;
if (!EC_KEY_set_group(ec_key,ec_group))
DieWithError("Error in initializeCrypto, EC_KEY_set_group failed!");
// Segfault at this position
if(!EC_KEY_generate_key(ec_key))
DieWithError ("Error in generateKeys, EC_KEY_generate_key failed!");
(...)
EC_GROUP_free(ec_group_new);
EC_KEY_free(ec_key);
乍一看,一切似乎都很顺利。在i5 520m上使用四个线程,应用程序的运行速度提高了一倍。但在3-4代E6密钥之后,它突然出现了故障。如果我锁定EC_KEY_generate_KEY操作,就不再有segfault,但是使用多线程的优势已经消失了。现在我的问题。是否可以将密钥的创建拆分为多个线程而不损坏内存?我在谷歌上没有找到任何信息。不过,报告没有提到任何关于线程安全的内容。非常感谢您的帮助。thx
// Segfault at this position
if(!EC_KEY_generate_key(ec_key))
DieWithError ("Error in generateKeys, EC_KEY_generate_key failed!");
...
... But then after 3-4 E6 key generations it suddenly segfaults.
您使用的是OpenSSL的随机数生成器,它不是线程安全的。下面是第125行附近的cryptlib.c
。请注意,列表中有随机数生成器和椭圆曲线齿轮
/* real #defines in crypto.h, keep these upto date */
static const char* const lock_names[CRYPTO_NUM_LOCKS] =
{
"<<ERROR>>",
"err",
"ex_data",
"x509",
"x509_info",
"x509_pkey",
"x509_crl",
"x509_req",
...
"ssl_ctx",
"ssl_session",
"ssl",
"ssl_method",
"rand",
"rand2",
...
"ecdsa",
"ec",
"ecdh",
"bn",
"ec_pre_comp",
...
};
/*real#在crypto.h中定义,保持这些最新*/
静态常量字符*常量锁\u名称[加密锁]=
{
"",
“呃”,
“ex_数据”,
“x509”,
“x509_信息”,
“x509_pkey”,
“x509_crl”,
“x509_需求”,
...
“ssl_ctx”,
“ssl_会话”,
“ssl”,
“ssl_方法”,
“兰德”,
“rand2”,
...
“ecdsa”,
“欧共体”,
“ecdh”,
“bn”,
“ec_pre_comp”,
...
};
您必须显式地设置锁。看
是否可以将密钥的创建拆分为多个线程而不损坏内存 是的,但是您必须使用OpenSSL的锁定机制 这是我的OpenSSL初始化程序在C++中的样子。它初始化锁并设置回调
pthread_mutex_t s_locks[CRYPTO_NUM_LOCKS] = { };
void Initialize()
{
static once_flag init;
std::call_once(init, []() {
// Standard OpenSSL library init
OPENSSL_no_config();
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
// Lock setup
LOCK_setup();
CALLBACK_setup();
});
}
void LOCK_setup()
{
ASSERT(CRYPTO_NUM_LOCKS == CRYPTO_num_locks());
if(CRYPTO_NUM_LOCKS != CRYPTO_num_locks())
throw runtime_error("CRYPTO_NUM_LOCKS mismatch");
for(unsigned i = 0; i < CRYPTO_NUM_LOCKS; ++i)
{
int rc = pthread_mutex_init(&s_locks[i], NULL);
ASSERT(rc == 0);
if(!(rc == 0))
throw runtime_error("pthread_mutex_init");
}
}
void CALLBACK_setup()
{
CRYPTO_set_id_callback(&ThreadIdFnc);
CRYPTO_set_locking_callback(&LockingFnc);
}
void LockingFnc(int mode, int idx, const char* file, int line)
{
ASSERT(mode == CRYPTO_LOCK || mode == CRYPTO_UNLOCK);
ASSERT(CRYPTO_NUM_LOCKS == CRYPTO_num_locks());
ASSERT(idx >= 0 && idx < CRYPTO_NUM_LOCKS);
if(!(idx >= 0 && idx < CRYPTO_NUM_LOCKS))
{
ostringstream oss;
oss << "LockingFnc: lock failed with bad index ";
oss << idx << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
// Log oss.str()
return;
}
if((mode & CRYPTO_LOCK) == CRYPTO_LOCK)
{
int rc = pthread_mutex_lock(&s_locks[idx]);
int err = errno;
ASSERT(rc == 0);
if(!(rc == 0))
{
ostringstream oss;
oss << "LockingFnc: lock failed with error ";
oss << err << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
throw runtime_error(oss.str());
}
}
else if((mode & CRYPTO_UNLOCK) == CRYPTO_UNLOCK)
{
int rc = pthread_mutex_unlock(&s_locks[idx]);
int err = errno;
ASSERT(rc == 0);
if(!(rc == 0))
{
ostringstream oss;
oss << "LockingFnc: unlock failed with error ";
oss << err << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
throw runtime_error(oss.str());
}
}
}
unsigned long ThreadIdFnc()
{
#if defined(AC_OS_APPLE)
ASSERT(sizeof(unsigned long) >= sizeof(pid_t));
return static_cast<unsigned long>(pthread_mach_thread_np(pthread_self()));
#elif defined(AC_OS_STARNIX)
ASSERT(sizeof(unsigned long) >= sizeof(pid_t));
return static_cast<unsigned long>(gettid());
#else
# error "Unsupported platform"
#endif
}
pthread_mutex_t s_locks[CRYPTO_NUM_locks]={};
void Initialize()
{
静态一次_标志初始化;
std::调用_一次(init,[](){
//标准OpenSSL库初始化
OPENSSL_no_config();
SSL_库_init();
SSL_加载_错误_字符串();
OpenSSL_添加_ssl_算法();
//锁定设置
锁定设置();
回调_设置();
});
}
无效锁定设置()
{
断言(CRYPTO_NUM_LOCKS==CRYPTO_NUM_LOCKS());
if(CRYPTO_NUM_LOCKS!=CRYPTO_NUM_LOCKS())
抛出运行时错误(“加密锁不匹配”);
for(无符号i=0;i=0&&idx=0&&idx oss Thx为您提供了一个很好的答案!您在第一个版本中的提示已经帮助我找到了一个解决我问题的示例。我找到了这个示例,并对我的代码进行了调整。但是仍然存在一些不确定性(由于我的好奇)。首先,我不知道CRYPTO_num_locks()是如何工作的,因为它似乎“知道”我将在我的程序中使用多少线程?其次,OpenSSL线程docu提出了另一个ThreadIdFnc(),带有其他参数和返回类型(请参阅int CRYPTO_THREADID_set_callback(void(*THREADID_func)(CRYPTO_THREADID*));OpenSSL锁定它们的组件(例如,随机数生成器)。锁定发生在共享数据周围,线程数不相关。CRYPTO_num_locks
是一个运行时函数。它返回您提供的数组中所需的最大锁数。编译时常量为CRYPTO_num_locks
。当您知道编译时的大小时,运行时无需malloc
e时间。threadid\u func
vsThreadIdFnc
命名并不重要。您可以将其命名为Foo
、bar
或Foo\u bar
。重要的是函数的签名和调用约定-函数的参数和返回类型。函数必须采用参数int、int、char*、int
和returnvoid
。/crypto/threads/mttest.c
中的示例代码可能会对您有所帮助。