diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 98b0fb5..e6b6f6e 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -30,7 +30,7 @@ option "key" k "Authentication key to use" string optional default="010203040506 option "action" a "Action to take" values="version","generate","set-mgm-key", "reset","pin-retries","import-key","import-certificate","set-chuid", "request-certificate","verify-pin","change-pin","change-puk","unblock-pin", - "selfsign-certificate","delete-certificate" enum multiple + "selfsign-certificate","delete-certificate","read-certificate" enum multiple text " Multiple actions may be given at once and will be executed in order for example --action=verify-pin --action=request-certificate\n" diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index e6ca527..bd3f84e 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -1009,6 +1009,64 @@ static bool delete_certificate(ykpiv_state *state, enum enum_slot slot) { } } +static bool read_certificate(ykpiv_state *state, enum enum_slot slot, + enum enum_key_format key_format, const char *output_file_name) { + FILE *output_file; + int object = get_object_id(slot); + unsigned char data[2048]; + const unsigned char *ptr = data; + unsigned long len = sizeof(data); + int cert_len; + bool ret = false; + X509 *x509 = NULL; + + if(key_format != key_format_arg_PEM && key_format != key_format_arg_DER) { + fprintf(stderr, "Only PEM and DER format are supported for read-certificate.\n"); + return false; + } + + output_file = open_file(output_file_name, OUTPUT); + if(!output_file) { + return false; + } + + if(ykpiv_fetch_object(state, object, data, &len) != YKPIV_OK) { + fprintf(stderr, "Failed fetching certificate.\n"); + goto read_cert_out; + } + + if(*ptr++ == 0x70) { + ptr += get_length(ptr, &cert_len); + if(key_format == key_format_arg_PEM) { + x509 = X509_new(); + if(!x509) { + fprintf(stderr, "Failed allocating x509 structure.\n"); + goto read_cert_out; + } + x509 = d2i_X509(NULL, &ptr, cert_len); + if(!x509) { + fprintf(stderr, "Failed parsing x509 information.\n"); + goto read_cert_out; + } + PEM_write_X509(output_file, x509); + ret = true; + } else { /* key_format_arg_DER */ + /* XXX: This will just dump the raw data in tag 0x70.. */ + fwrite(ptr, (size_t)cert_len, 1, output_file); + ret = true; + } + } + +read_cert_out: + if(output_file != stdout) { + fclose(output_file); + } + if(x509) { + X509_free(x509); + } + return ret; +} + static bool sign_file(ykpiv_state *state, const char *input, const char *output, const char *slot, enum enum_algorithm algorithm, enum enum_hash hash, int verbosity) { @@ -1190,6 +1248,7 @@ int main(int argc, char *argv[]) { case action_arg_changeMINUS_puk: case action_arg_unblockMINUS_pin: case action_arg_selfsignMINUS_certificate: + case action_arg_readMINUS_certificate: case action__NULL: default: if(verbosity) { @@ -1393,6 +1452,17 @@ int main(int argc, char *argv[]) { } } break; + case action_arg_readMINUS_certificate: + if(args_info.slot_arg == slot__NULL) { + fprintf(stderr, "The read-certificate action needs a slot (-s) to operate on.\n"); + ret = EXIT_FAILURE; + } else { + if(read_certificate(state, args_info.slot_arg, args_info.key_format_arg, + args_info.output_arg) == false) { + ret = EXIT_FAILURE; + } + } + break; case action__NULL: default: fprintf(stderr, "Wrong action. %d.\n", action);