私は、OpenSSL エンジンのどの側面を「かなり単純」であると特徴付けるかはわかりません。コマンド ライン バージョンは混沌としていて、コマンド ラインが何をしているのかは、自分でコードを書いてからしかわかりませんでした。操作の順序と有効期間はあまりよくわかりません (そして、それらが何であるかはまだ完全にはわかりませんが、システムを実行していて、メモリ リークはもうしていません。)
機能するバージョンを github にアップしました: https://github.com/tkil/openssl-pkcs11-samples
の関連部分のガイド ツアーを次に示しますtok-sign.c
。
まず、いくつかのヘルパー:
#define FAIL( msg, dest ) \
do { \
fprintf( stderr, "error: " msg "\n" ); \
goto dest; \
} while ( 0 )
/* mandatory is "not optional"... */
const int CMD_MANDATORY = 0;
おそらく、このdynamic
エンジンの最も奇妙な点は、それが実際にはメタエンジンであることです。さまざまなパラメーターをフィードするとLOAD
、動的ライブラリが読み込まれ、新しいエンジンが利用可能になります。操作の正しい順序がわかれば、コードは簡単です。ここでは、動的エンジンにアクセスして構成し、エンジンを取り込むように要求しますpkcs11
。
ENGINE_load_dynamic();
ENGINE * dyn = ENGINE_by_id( "dynamic" );
if ( ! dyn )
FAIL( "retrieving 'dynamic' engine", free_out_sig_file );
// this is the bridge between OpenSSL and any generic PCKS11 provider:
char * engine_pkcs11_so = "/opt/crypto/lib/engines/engine_pkcs11.so";
if ( 1 != ENGINE_ctrl_cmd_string( dyn, "SO_PATH", engine_pkcs11_so, CMD_MANDATORY ) )
FAIL( "dyn: setting so_path <= 'engine_pkcs11.so'", free_dyn );
if ( 1 != ENGINE_ctrl_cmd_string( dyn, "ID", "pkcs11", CMD_MANDATORY ) )
FAIL( "dyn: setting id <= 'pkcs11'", free_dyn );
if ( 1 != ENGINE_ctrl_cmd( dyn, "LIST_ADD", 1, NULL, NULL, CMD_MANDATORY ) )
FAIL( "dyn: setting list_add <= 1", free_dyn );
if ( 1 != ENGINE_ctrl_cmd( dyn, "LOAD", 1, NULL, NULL, CMD_MANDATORY ) )
FAIL( "dyn: setting load <= 1", free_dyn );
この時点で、これらの呼び出しがすべて成功した場合、OpenSSL インスタンスは「pkcs11」と呼ばれる新しいエンジンにアクセスできるようになります。次に、その新しいエンジンにアクセスして正しく構成し、初期化する必要があります。
ENGINE * pkcs11 = ENGINE_by_id( "pkcs11" );
if ( ! pkcs11 )
FAIL( "pkcs11: unable to get engine", free_dyn );
// this is the actual pkcs11 provider we're using. in this case, it's
// from the OpenSC package.
char * opensc_pkcs11_so = "/opt/crypto/lib/opensc-pkcs11.so";
if ( 1 != ENGINE_ctrl_cmd_string( pkcs11, "MODULE_PATH", opensc_pkcs11_so, CMD_MANDATORY ) )
FAIL( "setting module_path <= 'opensc-pkcs11.so'", free_pkcs11 );
if ( 1 != ENGINE_ctrl_cmd_string( pkcs11, "PIN", key_pin, CMD_MANDATORY ) )
FAIL( "setting pin", free_pkcs11 );
if ( 1 != ENGINE_init( pkcs11 ) )
FAIL( "pkcs11: unable to initialize engine", free_pkcs11 );
トークンにアクセスできるようになったので、OpenSSL 操作で使用する秘密鍵を取得できます。
EVP_PKEY * key = ENGINE_load_private_key( pkcs11, key_id, NULL, NULL );
if ( ! key )
FAIL( "reading private key", free_pkcs11 );
ただし、ENGINE インターフェイスを介して対応する証明書を抽出する方法がわからなかったため、LibP11 に直接アクセスしました。
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 );
最後に、CMS_Sign リクエストのデータ収集を開始します。トークンのすべての証明書を見て、秘密鍵に対応するものを選択し、残りを OpenSSL に保存しますSTACK_OF(X509)
。
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 );
最後に、データに署名してから、署名を DER 形式のファイルに出力できます。
CMS_ContentInfo * ci = CMS_sign( key_cert, key, extra_certs, in_data_file,
CMS_DETACHED | CMS_BINARY );
if ( ! ci )
FAIL( "could not create signing structure", free_extra_certs );
if ( 1 != i2d_CMS_bio( out_sig_file, ci ) )
FAIL( "could not write signature in DER", free_ci );
その後、それはただのクリーンアップです:
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_pkcs11:
ENGINE_free( pkcs11 );
free_dyn:
ENGINE_free( dyn );