這次派對非常晚(在同伴黑客中有2年?),但這裏是我的「最小」OpenSSL + PKCS11簽名程序(用C99編寫,可轉換爲C89,將所有變量定義移動到塊頂部)。
在我的情況,我在/ opt /加密建立了一個自定義的加密堆棧「手動」,並且此程序的鏈接線:
gcc -o tok-sign tok-sign.c -I /opt/crypto/include -g --std=gnu99 -Wall \
-L/opt/crypto/lib -lssl -lcrypto -lrt -lp11
最奇怪位被理解的是,「動態」引擎真的應該被稱爲「元引擎」;而且我必須直接通過libp11接口才能將證書傳遞到CMS_sign
操作中。
它可以使用更多的評論,並且它很長;我爲兩人道歉。的基本要點是:
- 初始化的OpenSSL
- 處理命令行參數(輸入,輸出,鍵標籤,令牌PIN)
- 配置
dynamic
元發動機加載pkcs11
發動機
- 配置在
pkcs1
1引擎來使用通過opensc PKCS11提供商
- 閱讀的額外證書關令牌
libp11
- 使用OpenSSL的
CMS_sign
做繁重的工作
- 發出DER格式的結果簽名
- 做清理所需的許多不同位。
這是程序本身。我應該把它變成一個博客文章。
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <openssl/cms.h>
#include <openssl/conf.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <libp11.h>
#define FAIL(msg, dest) \
do { \
fprintf(stderr, "error: " msg "\n"); \
goto dest; \
} while (0)
static
void
print_time(const char * label)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
fprintf(stderr, "+%8lld.%09ld: %s\n",
(long long)ts.tv_sec, ts.tv_nsec, label);
}
int
main(int argc, char * argv [])
{
enum
{
ARG_IN_DATA_FILE_IX = 1,
ARG_OUT_SIG_FILE_IX = 2,
ARG_KEY_LABEL_IX = 3,
ARG_KEY_PIN_IX = 4
};
int exit_code = 0;
/* -------------------------------------------------------------- */
/* initialization */
exit_code = 1;
SSL_load_error_strings();
SSL_library_init();
/* -------------------------------------------------------------- */
/* command-line processing */
exit_code = 2;
if (argc != 5)
{
fprintf(stderr, "usage: %s IN_DATA_FILE OUT_SIG_FILE"
" KEY_LABEL KEY_PIN\n", argv[0]);
return 1;
}
BIO * in_data_file = BIO_new_file(argv[ ARG_IN_DATA_FILE_IX ], "rb");
if (! in_data_file)
{
perror(argv[ ARG_IN_DATA_FILE_IX ]);
goto end;
}
BIO * out_sig_file = BIO_new_file(argv[ ARG_OUT_SIG_FILE_IX ], "wb");
if (! out_sig_file)
{
perror(argv[ ARG_OUT_SIG_FILE_IX ]);
goto free_in_data_file;
}
const char * key_label = argv[ ARG_KEY_LABEL_IX ];
char * key_id = calloc(sizeof(char), strlen(key_label) + 7);
strcpy(key_id, "label_");
strcat(key_id, key_label);
const char * key_pin = argv[ ARG_KEY_PIN_IX ];
/* -------------------------------------------------------------- */
/* load dynamic modules/engines */
exit_code = 3;
/* mandatory is "not optional"... */
const int CMD_MANDATORY = 0;
ENGINE_load_dynamic();
ENGINE * pkcs11 = ENGINE_by_id("dynamic");
if (! pkcs11)
FAIL("retrieving 'dynamic' engine", free_out_sig_file);
char * engine_pkcs11_so = "/opt/crypto/lib/engines/engine_pkcs11.so";
if (0 != access(engine_pkcs11_so, R_OK))
{
engine_pkcs11_so = "/lib/engines/engine_pkcs11.so";
if (0 != access(engine_pkcs11_so, R_OK))
FAIL("finding 'engine_pkcs11.so'", free_pkcs11);
}
if (1 != ENGINE_ctrl_cmd_string(pkcs11, "SO_PATH", engine_pkcs11_so, CMD_MANDATORY))
FAIL("pkcs11: setting so_path <= 'engine_pkcs11.so'", free_pkcs11);
if (1 != ENGINE_ctrl_cmd_string(pkcs11, "ID", "pkcs11", CMD_MANDATORY))
FAIL("pkcs11: setting id <= 'pkcs11'", free_pkcs11);
if (1 != ENGINE_ctrl_cmd(pkcs11, "LIST_ADD", 1, NULL, NULL, CMD_MANDATORY))
FAIL("pkcs11: setting list_add <= 1", free_pkcs11);
if (1 != ENGINE_ctrl_cmd(pkcs11, "LOAD", 1, NULL, NULL, CMD_MANDATORY))
FAIL("pkcs11: setting load <= 1", free_pkcs11);
ENGINE * tok = ENGINE_by_id("pkcs11");
if (! tok)
FAIL("tok: unable to get engine", free_pkcs11);
char * opensc_pkcs11_so = "/opt/crypto/lib/opensc-pkcs11.so";
if (0 != access(opensc_pkcs11_so, R_OK))
{
opensc_pkcs11_so = "/lib/opensc-pkcs11.so";
if (0 != access(opensc_pkcs11_so, R_OK))
FAIL("finding 'opensc-pkcs11.so'", free_tok);
}
if (1 != ENGINE_ctrl_cmd_string(tok, "MODULE_PATH", opensc_pkcs11_so, CMD_MANDATORY))
FAIL("setting module_path <= 'opensc-pkcs11.so'", free_tok);
if (1 != ENGINE_ctrl_cmd_string(tok, "PIN", key_pin, CMD_MANDATORY))
FAIL("setting pin", free_tok);
if (1 != ENGINE_init(tok))
FAIL("tok: unable to initialize engine", free_tok);
/* -------------------------------------------------------------- */
/* reading from token */
exit_code = 4;
EVP_PKEY * key = ENGINE_load_private_key(tok, key_id, NULL, NULL);
if (! key)
FAIL("reading private key", free_tok);
PKCS11_CTX * p11_ctx = PKCS11_CTX_new();
if (! p11_ctx)
FAIL("opening pkcs11 context", free_key);
if (0 != PKCS11_CTX_load(p11_ctx, opensc_pkcs11_so))
FAIL("unable to load module", free_p11_ctx);
PKCS11_SLOT * p11_slots;
unsigned int num_p11_slots;
if (0 != PKCS11_enumerate_slots(p11_ctx, &p11_slots, &num_p11_slots))
FAIL("enumerating slots", free_p11_ctx_module);
PKCS11_SLOT * p11_used_slot =
PKCS11_find_token(p11_ctx, p11_slots, num_p11_slots);
if (! p11_used_slot)
FAIL("finding token", free_p11_slots);
PKCS11_CERT * p11_certs;
unsigned int num_p11_certs;
if (0 != PKCS11_enumerate_certs(p11_used_slot->token, &p11_certs, &num_p11_certs))
FAIL("enumerating certs", free_p11_slots);
STACK_OF(X509) * extra_certs = sk_X509_new_null();
if (! extra_certs)
FAIL("allocating extra certs", free_p11_slots);
X509 * key_cert = NULL;
for (unsigned int i = 0; i < num_p11_certs; ++i)
{
PKCS11_CERT * p11_cert = p11_certs + i;
if (! p11_cert->label)
continue;
// fprintf(stderr, "p11: got cert label='%s', x509=%p\n",
// p11_cert->label, p11_cert->x509);
if (! p11_cert->x509)
{
fprintf(stderr, "p11: ... no x509, ignoring\n");
continue;
}
const char * label = p11_cert->label;
const unsigned int label_len = strlen(label);
if (strcmp(label, key_label) == 0)
{
// fprintf(stderr, "p11: ... saving as signing cert\n");
key_cert = p11_cert->x509;
}
else if (strncmp(label, "encrypt", 7) == 0 &&
label_len == 8 &&
'0' <= label[7] && label[7] <= '3')
{
// fprintf(stderr, "p11: ... ignoring as encrypting cert\n");
}
else
{
// fprintf(stderr, "p11: ... saving as extra cert\n");
if (! sk_X509_push(extra_certs, p11_cert->x509))
FAIL("pushing extra cert", free_extra_certs);
}
}
if (! key_cert)
FAIL("finding signing cert", free_extra_certs);
/* -------------------------------------------------------------- */
/* signing */
exit_code = 5;
print_time("calling CMS_sign");
CMS_ContentInfo * ci = CMS_sign(key_cert, key, extra_certs, in_data_file,
CMS_DETACHED | CMS_BINARY);
/* if (1 != PEM_write_bio_CMS(out_sig_file, ci))
FAIL("could not write signature in PEM", free_ci); */
print_time("calling i2d_CMS_bio");
if (1 != i2d_CMS_bio(out_sig_file, ci))
FAIL("could not write signature in DER", free_ci);
print_time("done");
/* -------------------------------------------------------------- */
/* success */
exit_code = 0;
/* -------------------------------------------------------------- */
/* cleanup */
free_ci:
CMS_ContentInfo_free(ci);
free_extra_certs:
/* these certs are actually "owned" by the libp11 code, and are
* presumably freed with the slot or context. */
sk_X509_free(extra_certs);
free_p11_slots:
PKCS11_release_all_slots(p11_ctx, p11_slots, num_p11_slots);
free_p11_ctx_module:
PKCS11_CTX_unload(p11_ctx);
free_p11_ctx:
PKCS11_CTX_free(p11_ctx);
free_key:
EVP_PKEY_free(key);
free_tok:
ENGINE_free(tok);
free_pkcs11:
ENGINE_free(pkcs11);
free_out_sig_file:
BIO_vfree(out_sig_file);
free_in_data_file:
BIO_vfree(in_data_file);
ERR_print_errors_fp(stderr);
ERR_remove_state(/* pid= */ 0);
ENGINE_cleanup();
CONF_modules_unload(/* all= */ 1);
EVP_cleanup();
ERR_free_strings();
CRYPTO_cleanup_all_ex_data();
end:
return exit_code;
}
如果你不介意的商業解決方案,我們的SecureBlackbox(http://www.eldos.com/sbb/net-pki.php)會做你需要的代碼行的打什麼。另一種方法可能是編寫自己的CMS實現。 –
你是什麼意思,開始變得很大?你真的想要解決什麼樣的問題?在我看來,如果要保持輕量級,在OpenSSL下推出預製的PKCS#11模塊就是完成工作的方式。你使用智能卡獲得了哪些資源? –
在分配方面變得非常大。該卡有pkcs11驅動程序,所以很好。從命令行Openssl不能使用引擎簽名使用智能卡,但可能從代碼(尚未嘗試),但我需要openssl和引擎鏈接到我的應用程序 –