diff --git a/src/sslengines/README b/src/sslengines/README new file mode 100644 index 00000000..8c0e53b3 --- /dev/null +++ b/src/sslengines/README @@ -0,0 +1,20 @@ +engine-pkcs11 +------------- + +This is an OpenSSL engine for making certificate requests for +a key that resides on an smart card. When the certificate +request has to be signed, the smart card is contacted through +the opensc-pkcs11 lib for creating the signature. + +Usage: + +- start the OpenSSL tool: openssl +- at the prompt, enter: engine dynamic -pre SO_PATH:engine_pkcs11.so -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD + (for Windows, use "engine_pkcs11" instead of "engine_pkcs11.so") +- at the prompt, enter: req -engine pkcs11 -new -key -keyform engine -out req.pem -text + +In the last line, has the format [S-]keyID, in which + - the optional slotNumber indicates which pkcs11 slot to take (starting from 0, which is also the default) + - keyID is the key ID in hex notation +Examples: 45 -> private key with ID = 0x45 in the first 'suited' slot + S2-46 -> private key with ID = 0x46 in the third slot diff --git a/src/sslengines/engine_pkcs11.c b/src/sslengines/engine_pkcs11.c index 465ea6d9..74032cfd 100644 --- a/src/sslengines/engine_pkcs11.c +++ b/src/sslengines/engine_pkcs11.c @@ -55,11 +55,11 @@ char* get_pin(UI_METHOD* ui_method, char* sc_pin, int maxlen) { UI* ui; ui=UI_new(); UI_set_method(ui,ui_method); - if(!UI_add_input_string(ui, "SmartCard Password: ", 0, sc_pin, 1, maxlen)) { - fprintf(stderr, "UI_add_input_string failed"); + if(!UI_add_input_string(ui, "SmartCard PIN: ", 0, sc_pin, 1, maxlen)) { + fprintf(stderr, "UI_add_input_string failed\n"); UI_free(ui); return NULL; } if(!UI_process(ui)) { - fprintf(stderr, "UI_process failed"); return NULL;} + fprintf(stderr, "UI_process failed\n"); return NULL;} UI_free(ui); return sc_pin; @@ -78,11 +78,11 @@ int pkcs11_init(ENGINE *engine) { int r=0; if(!quiet) - fprintf(stderr,"initializing engine"); + fprintf(stderr,"initializing engine\n"); ctx = PKCS11_CTX_new(); if (PKCS11_CTX_load(ctx, module) < 0) { - fprintf(stderr, "unable to load module"); + fprintf(stderr, "unable to load module\n"); return 0; } @@ -98,24 +98,120 @@ pkcs11_rsa_finish(RSA* rsa) { } +static int hex2byte(const char *hex) +{ + int b = 0; + if (hex[0]>='0' && hex[0]<='9') + b = hex[0] - '0'; + else if (hex[0]>='a'&&hex[0]<='f') + b = hex[0] - 'a' + 10; + else if (hex[0]>='A'&&hex[0]<='F') + b = hex[0] - 'A' + 10; + else + return -1; + b *= 16; + if (hex[1]>='0' && hex[1]<='9') + return b + hex[1] - '0'; + else if (hex[1]>='a'&&hex[1]<='f') + return b + hex[1] - 'a' + 10; + else if (hex[1]>='A'&&hex[1]<='F') + return b + hex[1] - 'A' + 10; + return -1; +} -EVP_PKEY *pkcs11_load_key(ENGINE *e, const char *s_key_id, +static int hex_to_bin(const char *in, unsigned char *out, size_t *outlen) +{ + int err = 0; + size_t left, count = 0; + + if (in == NULL || *in == '\0') { + *outlen = 0; + return 1; + } + + left = *outlen; + + while (*in != '\0') { + int byte = 0, nybbles = 2; + char c; + + while (nybbles-- && *in && *in != ':') { + byte <<= 4; + c = *in++; + if ('0' <= c && c <= '9') + c -= '0'; + else + if ('a' <= c && c <= 'f') + c = c - 'a' + 10; + else + if ('A' <= c && c <= 'F') + c = c - 'A' + 10; + else { + printf("hex_to_bin(): invalid char '%c' in hex string\n", c); + *outlen = 0; + return 0; + } + byte |= c; + } + if (*in == ':') + in++; + if (left <= 0) { + printf("hex_to_bin(): hex string too long"); + *outlen = 0; + return 0; + } + out[count++] = (unsigned char) byte; + left--; + c++; + } + +out: + *outlen = count; + return 1; +} + +EVP_PKEY *pkcs11_load_key(ENGINE *e, const char *s_slot_key_id, UI_METHOD *ui_method, void *callback_data, int private) { PKCS11_SLOT *slot_list, *slot; PKCS11_TOKEN *tok; - PKCS11_KEY *keys; + PKCS11_KEY *keys, *selected_key = NULL; PKCS11_CERT *certs; EVP_PKEY *pk; unsigned int count, n, m; + unsigned char key_id[100]; + const char *s_key_id; + int key_id_len = sizeof(key_id); + int slot_nr = -1; char flags[64]; int logged_in = 0; /* if(pin) {free(pin); pin=NULL;} // keep cached key? */ + /* Format of s_slot_key_id: [S-]keyID or NULL, + with slotNr in decimal (0 = first slot, ...), and keyID in hex. + E.g. "S0-45" or "46" */ + s_key_id = s_slot_key_id; + if (s_slot_key_id != NULL && + (s_slot_key_id[0] == 's' || s_slot_key_id[0] == 'S')) { + for (n = 1; s_slot_key_id[n] != '\0' && n < 8; n++) { + if (s_slot_key_id[n] == '-') { + char tmp[8]; + s_key_id += (n + 1); + memcpy(tmp, s_slot_key_id + 1, n - 1); + tmp[n - 1] = '\0'; + printf("tmp=%s\n", tmp); + slot_nr = atoi(tmp); + } + } + } + + if (!hex_to_bin(s_key_id, key_id, &key_id_len)) + fail("Invalid key ID\n"); + if (PKCS11_enumerate_slots(ctx, &slot_list, &count) < 0) - fail("failed to enumerate slots"); + fail("failed to enumerate slots\n"); printf("Found %u slot%s\n", count, (count <= 1)? "" : "s"); again: @@ -146,15 +242,21 @@ again: printf("\n"); } - if (!(slot = PKCS11_find_token(ctx))) - fail("didn't find any tokens"); + if (slot_nr == -1) { + if (!(slot = PKCS11_find_token(ctx))) + fail("didn't find any tokens\n"); + } + else if (slot_nr >= 0 && slot_nr < count) + slot = slot_list + slot_nr; + else { + printf("Invalid slot number: %d\n", slot_nr); + return NULL; + } tok = slot->token; if (!tok->initialized) { printf("Found uninitialized token; \n"); return NULL; - - } if (private && !tok->userPinSet && !tok->readOnly) { @@ -166,7 +268,7 @@ again: printf("Found token: %s\n", slot->token->label); if (PKCS11_enumerate_certs(tok, &certs, &count)) - fail("unable to enumerate certificates"); + fail("unable to enumerate certificates\n"); printf("Found %u certificate%s:\n", count, (count <= 1)? "" : "s"); for (n = 0; n < count; n++) { @@ -186,7 +288,7 @@ again: while (1) { if (PKCS11_enumerate_keys(tok, &keys, &count)) - fail("unable to enumerate keys"); + fail("unable to enumerate keys\n"); if (count) break; if (logged_in || !tok->loginRequired) @@ -196,7 +298,7 @@ again: get_pin(ui_method,pin,12); } if (PKCS11_login(slot, 0, pin)) - fail("Card login failed"); + fail("Card login failed\n"); logged_in++; } @@ -213,17 +315,29 @@ again: k->private? 'P' : ' ', k->needLogin? 'L' : ' ', k->label); + + if (key_id_len != 0 && k->id_len == key_id_len && + memcmp(k->id, key_id, key_id_len) == 0) { + printf(" ID = %s\n", s_key_id); + selected_key = k; + } } - if (count == 0) - return NULL; + if (selected_key == NULL) { + if (s_key_id != NULL) { + printf("No key with ID \"%s\" found.\n", s_key_id); + return NULL; + } + else /* Take the first key that was found */ + selected_key = &keys[0]; + } if(private) { - pk = PKCS11_get_private_key(&keys[0]); + pk = PKCS11_get_private_key(selected_key); } else { /*pk = PKCS11_get_public_key(&keys[0]); need a get_public_key? */ - pk = PKCS11_get_private_key(&keys[0]); + pk = PKCS11_get_private_key(selected_key); } return pk; @@ -234,7 +348,7 @@ EVP_PKEY *pkcs11_load_public_key(ENGINE *e, const char *s_key_id, EVP_PKEY *pk; pk=pkcs11_load_key(e, s_key_id, ui_method, callback_data, 0); if (pk == NULL) - fail("PKCS11_load_public_key returned NULL"); + fail("PKCS11_load_public_key returned NULL\n"); return pk; } @@ -243,6 +357,6 @@ EVP_PKEY *pkcs11_load_private_key(ENGINE *e, const char *s_key_id, EVP_PKEY* pk; pk=pkcs11_load_key(e, s_key_id, ui_method, callback_data, 1); if (pk == NULL) - fail("PKCS11_get_private_key returned NULL"); + fail("PKCS11_get_private_key returned NULL\n"); return pk; } diff --git a/src/sslengines/p11_cert.c b/src/sslengines/p11_cert.c index 4f3d7c82..0796359c 100644 --- a/src/sslengines/p11_cert.c +++ b/src/sslengines/p11_cert.c @@ -128,6 +128,7 @@ pkcs11_init_cert(PKCS11_CTX *ctx, PKCS11_TOKEN *token, PKCS11_CERT_private *kpriv; PKCS11_CERT *cert; char label[256], data[2048]; + unsigned char id[256]; CK_CERTIFICATE_TYPE cert_type; size_t size; @@ -158,6 +159,11 @@ pkcs11_init_cert(PKCS11_CTX *ctx, PKCS11_TOKEN *token, cert->x509 = d2i_X509(NULL, &p, size); } + cert->id_len = sizeof(id); + if (!pkcs11_getattr_var(token, obj, CKA_ID, id, &cert->id_len)) { + cert->id = (unsigned char *) malloc(cert->id_len); + memcpy(cert->id, id, cert->id_len); + } /* Initialize internal information */ kpriv->id_len = sizeof(kpriv->id); @@ -184,6 +190,8 @@ pkcs11_destroy_certs(PKCS11_TOKEN *token) if (cert->x509) X509_free(cert->x509); OPENSSL_free(cert->label); + if (cert->id) + free(cert->id); } if (priv->certs) OPENSSL_free(priv->certs); diff --git a/src/sslengines/p11_key.c b/src/sslengines/p11_key.c index c8244b87..b818f5b8 100644 --- a/src/sslengines/p11_key.c +++ b/src/sslengines/p11_key.c @@ -254,6 +254,7 @@ pkcs11_init_key(PKCS11_CTX *ctx, PKCS11_TOKEN *token, PKCS11_KEY_private *kpriv; PKCS11_KEY *key; char label[256]; + unsigned char id[256]; CK_KEY_TYPE key_type; PKCS11_KEY_ops *ops; size_t size; @@ -283,6 +284,11 @@ pkcs11_init_key(PKCS11_CTX *ctx, PKCS11_TOKEN *token, if (!pkcs11_getattr_s(token, obj, CKA_LABEL, label, sizeof(label))) key->label = BUF_strdup(label); + key->id_len = sizeof(id); + if (!pkcs11_getattr_var(token, obj, CKA_ID, id, &key->id_len)) { + key->id = (unsigned char *) malloc(key->id_len); + memcpy(key->id, id, key->id_len); + } key->private = (type == CKO_PRIVATE_KEY); /* Initialize internal information */ @@ -310,6 +316,8 @@ pkcs11_destroy_keys(PKCS11_TOKEN *token) if (key->evp_key) EVP_PKEY_free(key->evp_key); OPENSSL_free(key->label); + if (key->id) + free(key->id); } if (priv->keys) OPENSSL_free(priv->keys); diff --git a/src/sslengines/pkcs11-internal.h b/src/sslengines/pkcs11-internal.h index 28ecdb07..2ee7d709 100644 --- a/src/sslengines/pkcs11-internal.h +++ b/src/sslengines/pkcs11-internal.h @@ -115,6 +115,8 @@ ERR_PUT_error(ERR_LIB_PKCS11,(f),(r),__FILE__,__LINE__) /* PKCS11 key object (public or private) */ typedef struct PKCS11_key_st { char * label; + unsigned char * id; + int id_len; unsigned char private; /* private key present? */ unsigned char needLogin; /* login to read private key? */ EVP_PKEY * evp_key; /* initially NULL, need to call PKCS11_load_key */ @@ -124,6 +126,8 @@ typedef struct PKCS11_key_st { /* PKCS11 certificate object */ typedef struct PKCS11_cert_st { char * label; + unsigned char * id; + int id_len; X509 * x509; void * _private; } PKCS11_CERT;