From 3f4cb12702b840710c275908a631ad07eab66f80 Mon Sep 17 00:00:00 2001 From: Alessio Di Mauro Date: Tue, 12 Jul 2016 13:54:22 +0200 Subject: [PATCH] Add SSH export for RSA public key --- tool/cmdline.ggo | 2 +- tool/util.c | 70 ++++++++++++++++++++++++++++++++++++++++++ tool/util.h | 1 + tool/yubico-piv-tool.c | 23 +++++++++++--- 4 files changed, 90 insertions(+), 6 deletions(-) diff --git a/tool/cmdline.ggo b/tool/cmdline.ggo index 76fda5f..fa3e6dc 100644 --- a/tool/cmdline.ggo +++ b/tool/cmdline.ggo @@ -51,7 +51,7 @@ option "pin-retries" - "Number of retries before the pin code is blocked" int op option "puk-retries" - "Number of retries before the puk code is blocked" int optional dependon="pin-retries" option "input" i "Filename to use as input, - for stdin" string optional default="-" option "output" o "Filename to use as output, - for stdout" string optional default="-" -option "key-format" K "Format of the key being read/written" values="PEM","PKCS12","GZIP","DER" enum optional default="PEM" +option "key-format" K "Format of the key being read/written" values="PEM","PKCS12","GZIP","DER","SSH" enum optional default="PEM" option "password" p "Password for decryption of private key file" string optional option "subject" S "The subject to use for certificate request" string optional text " diff --git a/tool/util.c b/tool/util.c index 783a9ba..ab2d9cf 100644 --- a/tool/util.c +++ b/tool/util.c @@ -39,6 +39,7 @@ #include #include +#include #include @@ -611,3 +612,72 @@ unsigned char get_touch_policy(enum enum_touch_policy policy) { return 0; } } + +int SSH_write_X509(FILE *fp, X509 *x) { + + EVP_PKEY *pkey = NULL; + int ret = 0; + + pkey = X509_get_pubkey(x); + + if (pkey == NULL) { + return ret; + } + + switch (pkey->type) { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: { + RSA *rsa; + unsigned char n[256]; + + char rsa_id[] = "\x00\x00\x00\x07ssh-rsa"; + char rsa_f4[] = "\x00\x00\x00\x03\x01\x00\x01"; + + rsa = EVP_PKEY_get1_RSA(pkey); + + set_component(n, rsa->n, RSA_size(rsa)); + + uint32_t bytes = BN_num_bytes(rsa->n); + char len_buf[5]; + int len = 4; + + len_buf[0] = (bytes >> 24) & 0x000000ff; + len_buf[1] = (bytes << 16) & 0x000000ff; + len_buf[2] = (bytes >> 8) & 0x000000ff; + len_buf[3] = (bytes) & 0x000000ff; + + if (n[0] >= 0x80) { + // High bit set, need an extra byte + len++; + len_buf[3]++; + len_buf[4] = 0; + } + + fprintf(fp, "ssh-rsa "); + + BIO *b64 = BIO_new(BIO_f_base64()); + BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE); + + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + BIO_push(b64, bio); + + BIO_write(b64, rsa_id, sizeof(rsa_id) - 1); + BIO_write(b64, rsa_f4, sizeof(rsa_f4) - 1); + BIO_write(b64, len_buf, len); + BIO_write(b64, n, RSA_size(rsa)); + BIO_flush(b64); + BIO_free_all(b64); + + ret = 1; + + } break; + + case EVP_PKEY_EC: + break; + } + + EVP_PKEY_free(pkey); + + return ret; + +} diff --git a/tool/util.h b/tool/util.h index d9284f8..8c85c50 100644 --- a/tool/util.h +++ b/tool/util.h @@ -58,5 +58,6 @@ int get_hashnid(enum enum_hash, unsigned char); unsigned char get_piv_algorithm(enum enum_algorithm); unsigned char get_pin_policy(enum enum_pin_policy); unsigned char get_touch_policy(enum enum_touch_policy); +int SSH_write_X509(FILE *fp, X509 *x); #endif diff --git a/tool/yubico-piv-tool.c b/tool/yubico-piv-tool.c index fdb370a..55a03e7 100644 --- a/tool/yubico-piv-tool.c +++ b/tool/yubico-piv-tool.c @@ -1058,8 +1058,10 @@ static bool read_certificate(ykpiv_state *state, enum enum_slot slot, 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"); + if(key_format != key_format_arg_PEM && + key_format != key_format_arg_DER && + key_format != key_format_arg_SSH) { + fprintf(stderr, "Only PEM, DER and SSH format are supported for read-certificate.\n"); return false; } @@ -1075,7 +1077,8 @@ static bool read_certificate(ykpiv_state *state, enum enum_slot slot, if(*ptr++ == 0x70) { ptr += get_length(ptr, &cert_len); - if(key_format == key_format_arg_PEM) { + if(key_format == key_format_arg_PEM || + key_format == key_format_arg_SSH) { x509 = X509_new(); if(!x509) { fprintf(stderr, "Failed allocating x509 structure.\n"); @@ -1086,8 +1089,18 @@ static bool read_certificate(ykpiv_state *state, enum enum_slot slot, fprintf(stderr, "Failed parsing x509 information.\n"); goto read_cert_out; } - PEM_write_X509(output_file, x509); - ret = true; + + if (key_format == key_format_arg_PEM) { + PEM_write_X509(output_file, x509); + ret = true; + } + else { + if (!SSH_write_X509(output_file, x509)) { + fprintf(stderr, "Unable to extract public key or not an RSA key.\n"); + goto read_cert_out; + } + 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);