How I did this:

1) You can make up your own EVP_PKEY that uses your own engine implementation 
and attach a data ptr to it
                        EVP_PKEY* returnPKey;
                        returnPKey = EVP_PKEY_new();
                        if( returnPKey )
                        {
                            ENGINE* engine = ENGINE_by_id(YOUR_ENGINE_ID);
                            RSA* sc_rsa = RSA_new_method(engine);
                            if( sc_rsa )
                            {
                                // attach a reference to a structure holding 
your smart card middleware info
                                RSA_set_ex_data(sc_rsa, 
ENGINE_smartcard_rsa_idx_middleware(),
                                                (void*)middleware->handle);
                                
                                EVP_PKEY* pk = X509_get_pubkey( returnCert );
                                if( pk )
                                {
                                    sc_rsa->e = BN_new();
                                    sc_rsa->n = BN_new();
                                    BN_copy(sc_rsa->e, pk->pkey.rsa->e);
                                    BN_copy(sc_rsa->n, pk->pkey.rsa->n);
                                    EVP_PKEY_free(pk);
                                    
                                    EVP_PKEY_set1_RSA(returnPKey, sc_rsa);
                                    RSA_free(sc_rsa);
                                    
                                    *outCert = make this X509 from your smart 
card certificate;
                                    *outpkey = returnPKey;
                                }
                                else
                                    LogError("smartcards_fetch_identity can't 
get pubkey\n");
                            }

Then for your engine you will need some methods to configure it as follows:
void ENGINE_load_smartcard_keychain(void);
/*
 * ENGINE_tss_keychain_rsa_idx_middleware returns a ex_data index where engine 
user should store the
 *  pointer to the info needed to use the middleware
 */
int ENGINE_smartcard_rsa_idx_middleware(void);

Your ENGINE_load_smartcard_keychain method should set global values that get 
returned by ENGINE_smartcard_rsa_idx_middleware:
        gMiddlewareRSAIndex = RSA_get_ex_new_index(0x1234, NULL, NULL, NULL, 
NULL);

Configure your engine filling in an RSA_METHOD structure with what you will 
need. You don’t really need all the methods in RSA_METHOD structure, and if you 
don’t need them add a stub that returns a 0. I did not need either of the 
mod_exp method or the public key encrypt and decrypt methods. I also did not 
need the verify or keygen methods. Your init and finish methods just need to 
return 1.
I set the RSA_METHOD flags to 
RSA_FLAG_FIPS_METHOD|RSA_METHOD_FLAG_NO_CHECK|RSA_FLAG_CACHE_PUBLIC 

This leaves the cipher methods for private key encrypt/decrypt and sign. The 
private key methods will be where all the work is done.  Write a function to 
perform the smartcard ‘crypt’ method and use it in private encrypt/decrypt and 
the signing methods. You will need to pay attention to padding and make sure 
you know how to pad for PKCS1 type 1.  The RSA_SSLV23_PADDING is not required 
and you can just return an error if you get called with this. 

I handle the PIN entry requirement by having the engine return a specific error 
if the PIN is needed, then handle the PIN entry in the application.  Once the 
PIN is entered and available to the middleware, I retry the connection.

The trick is to get a pointer to your middleware implementation from the 
private key engine methods like this:

    my_middleware_handle =
        (my_middleware_handle)RSA_get_ex_data(rsa, gMiddlewareRSAIndex);

I found that writing the engine was more straightforward that attempting to use 
PKCS11.

> On Dec 14, 2020, at 1:08 AM, George <whipp...@gmail.com> wrote:
> 
> Hi,
> 
>    I'm new to OpenSSL and am trying to set up mutual authentication in a 
> client. The client is setup with OpenSSL 1.0.2u. and the client's certificate 
> + private key is stored on a Smart Card.  When the client receives a 
> certificate request from the server during the mutual authentication 
> handshake, the OpenSSL client_cert_cb callback function is automatically 
> invoked. The problem is that client_cert_cb requires a private key. 
> Unfortunately, it is not possible to get a private key from a Smart Card. Is 
> there a way to send a certificate to the server without needing the private 
> key?
> 
> I'm setting up the callback function with:
>  
> void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, int (*client_cert_cb)(SSL *ssl, 
> X509 **x509, EVP_PKEY **pkey));
> 
> 
> Here is a sample of what my code looks like when I set this up:
> 
> SSL_CTX_set_client_cert_cb(context, openSSLClientAuthenticationCallBack);
> 
> int openSSLClientAuthenticationCallBack(SSL *ssl, X509 **x509, EVP_PKEY 
> **pkey)
> {
> . . .
> }
> 
> 
> I can access the Smart Card using the PKCS#11 interface and I'm able to get 
> the certificate and sign it, etc. However, I cannot get the actual private 
> key from the Smart Card.
> 
> Does anyone know how I can get around this problem?
> 
> 
> Thanks,
> George
> 

Reply via email to