使用PKCS11提供程序以CMS格式加密签名文件和输出的最简单方法?

时间:2011-07-06 15:17:37

标签: .net security cryptography openssl pkcs#11

我正在寻找签署给定文件并以CMS格式获取输出的最简单方法,但这必须使用PKCS11提供程序,因为签名的私钥位于智能卡上。

我可以使用openssl从命令行获取正确格式的签名文件(但请注意,这是从文件获取证书而不是智能卡)

openssl cms -sign -in sign.txt -out signout.txt -signer signer.pem -outform DER

我想从使用最薄包装器的代码中做到这一点。我可以使用openssl库但是为了支持pkcs11,你需要通过引擎连接(opensc有一个)但是它开始变得非常大。令我感到震惊的是,在某处可以使用PKCS11上的简单包装器。

如果包装器位于“C”或.net中,我很高兴。 我可以自己打电话给PKCS11提供商并进行签名,如果我知道如何输出CMS那么也许一个库就可以了吗?

祝福 詹姆斯

2 个答案:

答案 0 :(得分:4)

参加派对的时间已经很晚了(在黑客同行中是2年了?),但这里是我的" minimal" OpenSSL + PKCS11签名程序(用C99编写,通过将所有变量定义移动到块顶部转换为C89)。

就我而言,我手工制作了一个自定义加密堆栈"在/ opt / crypto中,该程序的链接行是:

gcc -o tok-sign tok-sign.c -I /opt/crypto/include -g --std=gnu99 -Wall \
    -L/opt/crypto/lib -lssl -lcrypto -lrt -lp11

最奇怪的是理解"动态"引擎真的应该被称为"元引擎&#34 ;;并且我必须直接通过libp11接口来获取证书以进入CMS_sign操作。

它可以使用更多的评论,并且它很长;我为两者道歉。基本要点是:

  1. 初始化OpenSSL
  2. 处理命令行参数(输入,输出,键标签,令牌PIN)
  3. 配置dynamic元引擎以加载pkcs11引擎
  4. 配置pkcs1 1引擎以使用opensc pkcs11提供程序
  5. 使用libp11
  6. 从令牌中读取额外的证书
  7. 使用OpenSSL CMS_sign进行繁重的工作
  8. 以DER格式发出结果签名
  9. 执行所需的许多不同的清理工作。
  10. 这是程序本身。我应该把它变成某个博客文章。

    #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;
    }
    

答案 1 :(得分:0)

我前段时间写了一套PKCS7签名/加密工具,这里有一个可能有帮助的片段(C#.Net 3.5):

private byte[] signFile(byte[] fileContent, X509Certificate2 verificationCert)
{
    ContentInfo contentInfo = new ContentInfo(fileContent);           
    SignedCms signedCMS = new SignedCms(contentInfo);
    CmsSigner cmsSigner = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, verificationCert);
    cmsSigner.SignedAttributes.Add(new Pkcs9SigningTime());
    signedCMS.ComputeSignature(cmsSigner, false);
    byte[] encoded = signedCMS.Encode();

    return encoded;
}

我将签名内容写到.p7m文件中。我不确定如何修改它以使用智能卡,我必须了解更多信息。