考虑下面的RSA_generate_key()调用
RSA * rsa = RSA_generate_key(8192, RSA_F4, NULL, NULL);生成8,192位RSA密钥可能需要很长时间(从几秒钟到几分钟不等)。假设包含上述代码行的应用程序为用户提供了一个取消密钥生成的按钮。
如何在密钥生成之前中止计算并使函数返回?我记得RSA_generate_key()的第三个参数是一个用于显示进度的回调函数--有没有办法让回调返回一个表示“中止操作并返回”的值?
在另一个线程中运行函数,然后终止该线程不是一种选择。
发布于 2013-04-05 05:59:58
由于RSA_generate_key提供了进度回调,因此您可以退出它以终止函数。通过一些额外的代码,您可以为RSA_generate_key创建一个包装器,该包装器接受一个通用的测试函数,该函数可用于检查窗口系统设置的超时或标志。
#include <openssl/rsa.h>
#include <stdbool.h>
#include <setjmp.h>
struct trampoline_ctx {
bool (*testfn)(void *);
void *testfn_arg;
jmp_buf env;
};
static void trampoline(int ignore1, int ignore2, void *arg)
{
struct trampoline_ctx *ctx = arg;
if (!ctx->testfn(ctx->testfn_arg))
longjmp(ctx->env, 1);
}
// like RSA_generate_key, but accepts a test function. If testfn returns
// false, key generation is terminated and NULL is returned.
RSA *
my_generate_key(int num, unsigned long e,
bool (*testfn)(void *), void *testfn_arg)
{
struct trampoline_ctx ctx;
ctx.testfn = testfn;
ctx.testfn_arg = testfn_arg;
if (setjmp(ctx.env))
return NULL;
return RSA_generate_key(num, e, trampoline, &ctx);
}这种方法的可移植性令人惊讶,因为C89和C99都要求使用longjmp。它的缺点是,如果你正在使用的函数动态分配资源,它可能会泄漏资源。然而,在实践中,如果不频繁地或仅根据明确的用户请求进行泄漏,泄漏可能会小到无法察觉。要肯定这一点,请在紧密循环中运行代码,并观察进程的资源消耗。
以下是上述函数的测试程序:
#include <stdio.h>
#include <sys/time.h>
double now()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + (double) tv.tv_usec / 1e6;
}
struct tt_ctx {
double start;
double limit;
};
bool test_time_limit(void *arg)
{
struct tt_ctx *ctx = arg;
return now() - ctx->start <= ctx->limit;
}
int main(int argc, char **argv)
{
int limit = atoi(argv[1]);
struct tt_ctx ctx;
ctx.start = now();
ctx.limit = limit / 1000.0;
RSA *key = my_generate_key(4096, 65537, test_time_limit, &ctx);
printf("%p\n", key);
return 0;
}测试程序假定POSIX时钟为gettimeofday,但可以很容易地转换为系统提供的另一个高分辨率时钟。测试步骤如下:
将这两段代码追加到一个文件中,并使用-lrsa进行编译。测试程序将在命令行上以毫秒为单位指定的时间限制内生成4096位RSA密钥。在所有情况下,它都会输出结果RSA *指针,以指示my_generate_key是完成了请求还是中止了请求。time的输出伴随着执行,作为一种健全性检查,以验证是否遵守了时间限制:
# try with a 10ms limit
$ time ./a.out 10
(nil) # too short
./a.out 10 0.02s user 0.00s system 85% cpu 0.023 total
# see if 100ms is enough time
$ time ./a.out 100
(nil) # still too short
./a.out 100 0.10s user 0.00s system 97% cpu 0.106 total
# try with 1 whole second:
$ time ./a.out 1000
0x2369010 # success!
./a.out 1000 0.64s user 0.00s system 99% cpu 0.649 total发布于 2013-03-03 14:39:03
进度回调不能用于取消函数,它纯粹是为了向用户显示进度。(Read more here)
派生一个线程来调用RSA_generate_key(),这样它就可以在后台执行。当用户按下cancel按钮时,终止该线程。
发布于 2013-04-05 03:34:58
我认为问题在于,回调通常不会给你发送信息的能力,只能接收回信息。正如其他一些帖子所提到的,您可以考虑更改实际键生成例程的源代码,并编译您自己的版本。在这种情况下,您可以通过引用将某种类型的值传递回调用例程,然后可以在外部设置该值,从而触发失败条件。
注意:RSA_generate_key实际上已被弃用,现在只是RSA_generate_key_ex的一个包装器。
根据rsa_gen.c文件的1.19.4.2版本,除非您在FIPS_mode中,否则密钥将由静态方法rsa_builtin_keygen生成。在FIPS_mode中,当进入RSA_generate_key时,它将由任何rsa->meth->rsa_keygen生成。
也就是说,有很多地方会将回调(cb参数)传递给其他方法,这样这些方法就可以反过来调用它来更新状态。
诀窍是要么更新BN_GENCB以包含某种"cancel“标志,要么改变在方法中实际调用回调的方式,这样当回调方法触发时,您可以设置一个标志,调用函数将通过中断生成子例程来遵守该标志。
你如何去做仍然是你需要弄清楚和解决的事情-但希望这能给你一个开始。
https://stackoverflow.com/questions/15183101
复制相似问题