#include #include #include #include #include #include #include #include #include int quiet = 0; char *opt_outfile = NULL; char *opt_cert = NULL; const struct option options[] = { { "extract-key", 0, 0, 'k' }, { "certificate-id", 1, 0, 'c' }, { "reader", 1, 0, 'r' }, { "output", 1, 0, 'o' }, { "quiet", 0, 0, 'q' }, { 0, 0, 0, 0 } }; const char *option_help[] = { "Extracts the public key from a certificate", "Uses certificate with ID ", "Uses reader number ", "Outputs to file ", "Quiet operation", }; struct sc_context *ctx = NULL; struct sc_card *card = NULL; struct sc_pkcs15_card *p15card = NULL; void print_usage_and_die() { int i = 0; printf("Usage: sc-ssh [OPTIONS]\nOptions:\n"); while (options[i].name) { char buf[40], tmp[5]; const char *arg_str; if (options[i].val > 0 && options[i].val < 128) sprintf(tmp, ", -%c", options[i].val); else tmp[0] = 0; switch (options[i].has_arg) { case 1: arg_str = " "; break; case 2: arg_str = " [arg]"; break; default: arg_str = ""; break; } sprintf(buf, "--%s%s%s", options[i].name, tmp, arg_str); printf(" %-30s%s\n", buf, option_help[i]); i++; } exit(2); } u8 * bignum_to_buf(BIGNUM *value, int *length, int *skip) { /* Function ripped from bufaux.c in OpenSSH * Compliments to Tatu Ylönen */ int bytes = BN_num_bytes(value) + 1; u8 *buf = malloc(bytes); int oi; int hasnohigh = 0; buf[0] = '\0'; if (buf == NULL) return NULL; /* Get the value of in binary */ oi = BN_bn2bin(value, buf+1); if (oi != bytes-1) return NULL; hasnohigh = (buf[1] & 0x80) ? 0 : 1; if (value->neg) { /**XXX should be two's-complement */ int i, carry; u_char *uc = buf; for(i = bytes-1, carry = 1; i>=0; i--) { uc[i] ^= 0xff; if(carry) carry = !++uc[i]; } } *skip = hasnohigh; *length = bytes; return buf; } int put_string(const u8 *in, int inlen, u8 *out, int outlen, int *skip) { u8 *out0 = out; if (outlen < 4 + inlen) return -1; *out++ = (inlen >> 24) & 0xFF; *out++ = (inlen >> 16) & 0xFF; *out++ = (inlen >> 8) & 0xFF; *out++ = (inlen) & 0xFF; memcpy(out, in, inlen); out += inlen; *skip = out - out0; return 0; } int write_ssh_key(struct sc_pkcs15_cert_info *cinfo, RSA *rsa) { u8 *buf = malloc(10240), *p = buf, *num; int r, len, skip, left = 10240; FILE *outf; if (buf == NULL) return 1; put_string("ssh-rsa", 7, p, left, &skip); left -= skip; p += skip; num = bignum_to_buf(rsa->e, &len, &skip); if (num == NULL) return 1; put_string(num+skip, len-skip, p, left, &skip); left -= skip; p += skip; free(num); num = bignum_to_buf(rsa->n, &len, &skip); if (num == NULL) return 1; put_string(num+skip, len-skip, p, left, &skip); left -= skip; p += skip; free(num); len = p - buf; p = malloc(len*5/3); r = sc_base64_encode(buf, len, p, len*5/3, 0); if (r) { fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r)); return 1; } if (opt_outfile == NULL) outf = stdout; else { outf = fopen(opt_outfile, "w"); if (outf == NULL) { fprintf(stderr, "Unable to open '%s' for writing.\n", opt_outfile); return 2; } } fprintf(outf, "ssh-rsa %s libsc-cert-%02X\n", p, cinfo->id.value[0]); free(p), free(buf); return 0; } int extract_key() { int r, i; struct sc_pkcs15_id id; u8 *p = id.value; char *certp = opt_cert; struct sc_pkcs15_cert_info *cinfo; struct sc_pkcs15_cert *cert; X509 *x509; EVP_PKEY *pubkey; if (opt_cert) { if (strlen(opt_cert)/2 >= SC_PKCS15_MAX_ID_SIZE) { fprintf(stderr, "Certificate id too long.\n"); return 2; } id.len = 0; while (*certp) { int byte; if (sscanf(certp, "%02X", &byte) != 1) break; certp += 2; *p = byte; p++; id.len++; } } r = sc_pkcs15_enum_certificates(p15card); if (r < 0) { fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); return 1; } if (opt_cert) { for (i = 0; i < p15card->cert_count; i++) { cinfo = &p15card->cert_info[i]; if (sc_pkcs15_compare_id(&id, &cinfo->id) == 1) break; } if (i == p15card->cert_count) { fprintf(stderr, "Certificate with ID '%s' not found.\n", opt_cert); return 2; } } else { cinfo = &p15card->cert_info[0]; } if (!quiet) fprintf(stderr, "Using certificate '%s'.\n", cinfo->com_attr.label); r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); if (r) { fprintf(stderr, "Certificate read failed: %s\n", sc_strerror(r)); return 1; } x509 = X509_new(); p = cert->data; if (!d2i_X509(&x509, &p, cert->data_len)) { fprintf(stderr, "Unable to parse X.509 certificate.\n"); return 1; } pubkey = X509_get_pubkey(x509); if (pubkey->type != EVP_PKEY_RSA) { fprintf(stderr, "Public key is of unknown type.\n"); return 1; } r = write_ssh_key(cinfo, pubkey->pkey.rsa); EVP_PKEY_free(pubkey); X509_free(x509); return r; } int main(int argc, char *const argv[]) { int err = 0, r, c, long_optind = 0; int action_count = 0, do_extract_key = 0; int opt_reader = 0; while (1) { c = getopt_long(argc, argv, "r:o:qkc:", options, &long_optind); if (c == -1) break; if (c == '?') continue; switch (c) { case 'k': do_extract_key = 1; action_count++; break; case 'c': opt_cert = optarg; break; case 'r': opt_reader = atoi(optarg); break; case 'o': opt_outfile = optarg; break; case 'q': quiet++; break; } } if (action_count == 0) print_usage_and_die(); r = sc_establish_context(&ctx); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (opt_reader >= ctx->reader_count || opt_reader < 0) { fprintf(stderr, "Illegal reader number. Only %d reader(s) configured.\n", ctx->reader_count); err = 1; goto end; } if (sc_detect_card(ctx, opt_reader) != 1) { fprintf(stderr, "Card not present.\n"); return 3; } if (!quiet) fprintf(stderr, "Connecting to card in reader %s...\n", ctx->readers[opt_reader]); r = sc_connect_card(ctx, opt_reader, &card); if (r) { fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r)); err = 1; goto end; } sc_lock(card); if (!quiet) fprintf(stderr, "Trying to find a PKCS#15 compatible card...\n"); r = sc_pkcs15_bind(card, &p15card); if (r) { fprintf(stderr, "PKCS#15 initialization failed: %s\n", sc_strerror(r)); err = 1; goto end; } if (!quiet) fprintf(stderr, "Found %s!\n", p15card->label); if (do_extract_key) { if ((err = extract_key())) goto end; action_count--; } end: if (p15card) sc_pkcs15_unbind(p15card); if (card) { sc_unlock(card); sc_disconnect_card(card); } if (ctx) sc_destroy_context(ctx); return err; }