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.
|