diff --git a/lib/hx509/ChangeLog b/lib/hx509/ChangeLog index 4945c0315..87275c927 100644 --- a/lib/hx509/ChangeLog +++ b/lib/hx509/ChangeLog @@ -1,4 +1,18 @@ +2007-01-09 Love Hörnquist Åstrand + + * Factor out private key operation out of the signing, operations, + support import, export, and generation of private keys. Add + support for writing PEM and PKCS12 files with private keys in them. + + * data/gen-req.sh: Generate a no password pkcs12 file. + +2007-01-08 Love Hörnquist Åstrand + + * cms.c: Check for internal ASN1 encoder error. + 2007-01-05 Love Hörnquist Åstrand + + * Makefile.am: Drop most of the pkcs11 files. * test_ca.in: test reissueing ca certificate (xxx time validAfter). diff --git a/lib/hx509/Makefile.am b/lib/hx509/Makefile.am index efb3cdd07..41be5ae3b 100644 --- a/lib/hx509/Makefile.am +++ b/lib/hx509/Makefile.am @@ -138,6 +138,7 @@ CLEANFILES = $(BUILT_SOURCES) \ $(TESTS) \ hxtool-commands.c hxtool-commands.h *.tmp \ request.out \ + out.pem out2.pem \ sd.data sd.data.out \ ev.data ev.data.out \ cert-proxy.der cert-ca.der cert-ee.der pkcs10-request.der \ diff --git a/lib/hx509/cert.c b/lib/hx509/cert.c index 7096f78c8..1c5fcbff1 100644 --- a/lib/hx509/cert.c +++ b/lib/hx509/cert.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -236,8 +236,8 @@ int _hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key) { if (cert->private_key) - _hx509_free_private_key(&cert->private_key); - cert->private_key = private_key; + _hx509_private_key_free(&cert->private_key); + cert->private_key = _hx509_private_key_ref(private_key); return 0; } @@ -258,7 +258,7 @@ hx509_cert_free(hx509_cert cert) (cert->release)(cert, cert->ctx); if (cert->private_key) - _hx509_free_private_key(&cert->private_key); + _hx509_private_key_free(&cert->private_key); free_Certificate(cert->data); free(cert->data); @@ -1071,6 +1071,14 @@ _hx509_cert_private_key(hx509_cert p) return p->private_key; } +int +_hx509_cert_private_key_exportable(hx509_cert p) +{ + if (p->private_key == NULL) + return 0; + return _hx509_private_key_exportable(p->private_key); +} + int _hx509_cert_private_decrypt(hx509_context context, const heim_octet_string *ciphertext, diff --git a/lib/hx509/collector.c b/lib/hx509/collector.c index dbe91bdfd..ba2c434ce 100644 --- a/lib/hx509/collector.c +++ b/lib/hx509/collector.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -101,7 +101,7 @@ free_private_key(struct private_key *key) { free_AlgorithmIdentifier(&key->alg); if (key->private_key) - _hx509_free_private_key(&key->private_key); + _hx509_private_key_free(&key->private_key); der_free_octet_string(&key->localKeyId); free(key); } @@ -188,10 +188,8 @@ match_localkeyid(hx509_context context, ret = hx509_certs_find(context, certs, &q, &cert); if (ret == 0) { - if (value->private_key) { + if (value->private_key) _hx509_cert_assign_key(cert, value->private_key); - value->private_key = NULL; - } hx509_cert_free(cert); } return ret; @@ -229,7 +227,6 @@ match_keys(hx509_context context, struct private_key *value, hx509_certs certs) ret = _hx509_match_keys(c, value->private_key); if (ret) { _hx509_cert_assign_key(c, value->private_key); - value->private_key = NULL; hx509_cert_free(c); found = 0; break; @@ -246,9 +243,9 @@ match_keys(hx509_context context, struct private_key *value, hx509_certs certs) } int -_hx509_collector_collect(hx509_context context, - struct hx509_collector *c, - hx509_certs *ret_certs) +_hx509_collector_collect_certs(hx509_context context, + struct hx509_collector *c, + hx509_certs *ret_certs) { hx509_certs certs; int ret, i; @@ -279,6 +276,37 @@ _hx509_collector_collect(hx509_context context, return 0; } +int +_hx509_collector_collect_private_keys(hx509_context context, + struct hx509_collector *c, + hx509_private_key **keys) +{ + int i, nkeys; + + *keys = NULL; + + for (i = 0, nkeys = 0; i < c->val.len; i++) + if (c->val.data[i]->private_key) + nkeys++; + + *keys = calloc(nkeys + 1, sizeof(**keys)); + if (*keys == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory"); + return ENOMEM; + } + + for (i = 0, nkeys = 0; i < c->val.len; i++) { + if (c->val.data[i]->private_key) { + (*keys)[nkeys++] = c->val.data[i]->private_key; + c->val.data[i]->private_key = NULL; + } + } + (*keys)[nkeys++] = NULL; + + return 0; +} + + void _hx509_collector_free(struct hx509_collector *c) { diff --git a/lib/hx509/crypto.c b/lib/hx509/crypto.c index 884682bd0..4d96ee4e0 100644 --- a/lib/hx509/crypto.c +++ b/lib/hx509/crypto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -42,14 +42,22 @@ enum crypto_op_type { COT_SIGN }; -struct hx509_private_key { - const struct signature_alg *md; - const heim_oid *signature_alg; - struct { - RSA *rsa; - } private_key; - /* new crypto layer */ - void *key; + +struct hx509_private_key_ops { + const char *pemtype; + const heim_oid *(*key_oid)(void); + int (*get_spki)(hx509_context, + const hx509_private_key, + SubjectPublicKeyInfo *); + int (*export)(hx509_context context, + const hx509_private_key, + heim_octet_string *); + int (*import)(hx509_context, + const void *data, + size_t len, + hx509_private_key private_key); + int (*generate_private_key)(hx509_context context, + hx509_private_key private_key); int (*handle_alg)(const hx509_private_key, const AlgorithmIdentifier *, enum crypto_op_type); @@ -60,20 +68,29 @@ struct hx509_private_key { AlgorithmIdentifier *, heim_octet_string *); #if 0 - const AlgorithmIdentifier * - (*preferred_sig_alg)(const hx509_private_key_key, - const hx509_peer_info); + const AlgorithmIdentifier *(*preferred_sig_alg) + (const hx509_private_key, + const hx509_peer_info); int (*unwrap)(hx509_context context, const hx509_private_key, const AlgorithmIdentifier *, const heim_octet_string *, heim_octet_string *); - int (*get_spki)(hx509_context context, - const hx509_private_key_key, - SubjectPublicKeyInfo *); #endif }; +struct hx509_private_key { + unsigned int ref; + const struct signature_alg *md; + const heim_oid *signature_alg; + union { + RSA *rsa; + void *keydata; + } private_key; + /* new crypto layer */ + hx509_private_key_ops *ops; +}; + /* * */ @@ -106,13 +123,9 @@ struct signature_alg { const heim_octet_string *, AlgorithmIdentifier *, heim_octet_string *); - int (*parse_private_key)(hx509_context, - const struct signature_alg *, - const void *data, - size_t len, - hx509_private_key private_key); - int (*private_key2SPKI)(hx509_private_key private_key, - SubjectPublicKeyInfo *spki); + int (*private_key2SPKI)(hx509_context, + hx509_private_key, + SubjectPublicKeyInfo *); }; /* @@ -330,11 +343,10 @@ rsa_create_signature(hx509_context context, } static int -rsa_parse_private_key(hx509_context context, - const struct signature_alg *sig_alg, - const void *data, - size_t len, - hx509_private_key private_key) +rsa_private_key_import(hx509_context context, + const void *data, + size_t len, + hx509_private_key private_key) { const unsigned char *p = data; @@ -351,7 +363,8 @@ rsa_parse_private_key(hx509_context context, } static int -rsa_private_key2SPKI(hx509_private_key private_key, +rsa_private_key2SPKI(hx509_context context, + hx509_private_key private_key, SubjectPublicKeyInfo *spki) { int len, ret; @@ -361,14 +374,17 @@ rsa_private_key2SPKI(hx509_private_key private_key, len = i2d_RSAPublicKey(private_key->private_key.rsa, NULL); spki->subjectPublicKey.data = malloc(len); - if (spki->subjectPublicKey.data == NULL) + if (spki->subjectPublicKey.data == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory"); return ENOMEM; + } spki->subjectPublicKey.length = len * 8; ret = _hx509_set_digest_alg(&spki->algorithm, oid_id_pkcs1_rsaEncryption(), "\x05\x00", 2); if (ret) { + hx509_set_error_string(context, 0, ret, "malloc - out of memory"); free(spki->subjectPublicKey.data); spki->subjectPublicKey.data = NULL; spki->subjectPublicKey.length = 0; @@ -383,6 +399,90 @@ rsa_private_key2SPKI(hx509_private_key private_key, return 0; } +static int +cb_func(int a, int b, BN_GENCB *c) +{ + return 1; +} + +static int +rsa_generate_private_key(hx509_context context, hx509_private_key private_key) +{ + BN_GENCB cb; + BIGNUM *e; + int ret; + + static const int default_rsa_e = 65537; + static const int default_rsa_bits = 1024; + + private_key->private_key.rsa = RSA_new(); + if (private_key->private_key.rsa == NULL) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Failed to generate RSA key"); + return HX509_PARSING_KEY_FAILED; + } + + e = BN_new(); + BN_set_word(e, default_rsa_e); + + BN_GENCB_set(&cb, cb_func, NULL); + ret = RSA_generate_key_ex(private_key->private_key.rsa, + default_rsa_bits, e, &cb); + BN_free(e); + if (ret != 1) { + hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, + "Failed to generate RSA key"); + return HX509_PARSING_KEY_FAILED; + } + private_key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption(); + + return 0; +} + +static int +rsa_private_key_export(hx509_context context, + const hx509_private_key key, + heim_octet_string *data) +{ + int ret; + + data->data = NULL; + data->length = 0; + + ret = i2d_RSAPrivateKey(key->private_key.rsa, NULL); + if (ret <= 0) { + ret = EINVAL; + hx509_set_error_string(context, 0, ret, + "Private key is not exportable"); + return ret; + } + + data->data = malloc(ret); + if (data->data == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "malloc out of memory"); + return ret; + } + data->length = ret; + + { + unsigned char *p = data->data; + i2d_RSAPrivateKey(key->private_key.rsa, &p); + } + + return 0; +} + + +static hx509_private_key_ops rsa_private_key_ops = { + "RSA PRIVATE KEY", + oid_id_pkcs1_rsaEncryption, + rsa_private_key2SPKI, + rsa_private_key_export, + rsa_private_key_import, + rsa_generate_private_key +}; + /* * @@ -473,14 +573,13 @@ dsa_verify_signature(hx509_context context, return ret; } +#if 0 static int dsa_parse_private_key(hx509_context context, - const struct signature_alg *sig_alg, const void *data, size_t len, hx509_private_key private_key) { -#if 0 const unsigned char *p = data; private_key->private_key.dsa = @@ -490,12 +589,12 @@ dsa_parse_private_key(hx509_context context, private_key->signature_alg = oid_id_dsa_with_sha1(); return 0; -#else +/* else */ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "No support to parse DSA keys"); return HX509_PARSING_KEY_FAILED; -#endif } +#endif static int @@ -699,7 +798,6 @@ static struct signature_alg pkcs1_rsa_sha1_alg = { PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature, - rsa_parse_private_key, rsa_private_key2SPKI }; @@ -712,7 +810,6 @@ static struct signature_alg rsa_with_sha256_alg = { PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature, - rsa_parse_private_key, rsa_private_key2SPKI }; @@ -725,7 +822,6 @@ static struct signature_alg rsa_with_sha1_alg = { PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature, - rsa_parse_private_key, rsa_private_key2SPKI }; @@ -738,7 +834,6 @@ static struct signature_alg rsa_with_md5_alg = { PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature, - rsa_parse_private_key, rsa_private_key2SPKI }; @@ -751,7 +846,6 @@ static struct signature_alg rsa_with_md2_alg = { PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature, - rsa_parse_private_key, rsa_private_key2SPKI }; @@ -764,7 +858,6 @@ static struct signature_alg dsa_sha1_alg = { PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, dsa_verify_signature, /* create_signature */ NULL, - dsa_parse_private_key }; static struct signature_alg sha256_alg = { @@ -838,19 +931,29 @@ find_sig_alg(const heim_oid *oid) return NULL; } -static const struct signature_alg * -find_key_alg(const heim_oid *oid) +/* + * + */ + +static struct hx509_private_key_ops *private_algs[] = { + &rsa_private_key_ops, + NULL +}; + +static hx509_private_key_ops * +find_private_alg(const heim_oid *oid) { int i; - for (i = 0; sig_algs[i]; i++) { - if (sig_algs[i]->key_oid == NULL) + for (i = 0; private_algs[i]; i++) { + if (private_algs[i]->key_oid == NULL) continue; - if (der_heim_oid_cmp((*sig_algs[i]->key_oid)(), oid) == 0) - return sig_algs[i]; + if (der_heim_oid_cmp((*private_algs[i]->key_oid)(), oid) == 0) + return private_algs[i]; } return NULL; } + int _hx509_verify_signature(hx509_context context, const Certificate *signer, @@ -916,11 +1019,11 @@ _hx509_create_signature(hx509_context context, { const struct signature_alg *md; - if (signer && signer->handle_alg && - (*signer->handle_alg)(signer, alg, COT_SIGN)) + if (signer && signer->ops && signer->ops->handle_alg && + (*signer->ops->handle_alg)(signer, alg, COT_SIGN)) { - return (*signer->sign)(context, signer, alg, data, - signatureAlgorithm, sig); + return (*signer->ops->sign)(context, signer, alg, data, + signatureAlgorithm, sig); } md = find_sig_alg(&alg->algorithm); @@ -1089,28 +1192,26 @@ _hx509_parse_private_key(hx509_context context, size_t len, hx509_private_key *private_key) { - const struct signature_alg *md; + struct hx509_private_key_ops *ops; int ret; *private_key = NULL; - md = find_key_alg(key_oid); - if (md == NULL) { + ops = find_private_alg(key_oid); + if (ops == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } - ret = _hx509_new_private_key(private_key); + ret = _hx509_private_key_init(private_key, ops, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); return ret; } - ret = (*md->parse_private_key)(context, md, data, len, *private_key); + ret = (*ops->import)(context, data, len, *private_key); if (ret) - _hx509_free_private_key(private_key); - else - (*private_key)->md = md; + _hx509_private_key_free(private_key); return ret; } @@ -1124,13 +1225,42 @@ _hx509_private_key2SPKI(hx509_context context, hx509_private_key private_key, SubjectPublicKeyInfo *spki) { - const struct signature_alg *md = private_key->md; - if (md->private_key2SPKI == NULL) { + const struct hx509_private_key_ops *ops = private_key->ops; + if (ops == NULL || ops->get_spki == NULL) { hx509_set_error_string(context, 0, HX509_UNIMPLEMENTED_OPERATION, "Private key have no key2SPKI function"); return HX509_UNIMPLEMENTED_OPERATION; } - return (*md->private_key2SPKI)(private_key, spki); + return (*ops->get_spki)(context, private_key, spki); +} + +int +_hx509_generate_private_key(hx509_context context, + const heim_oid *key_oid, + hx509_private_key *private_key) +{ + struct hx509_private_key_ops *ops; + int ret; + + *private_key = NULL; + + ops = find_private_alg(key_oid); + if (ops == NULL) { + hx509_clear_error_string(context); + return HX509_SIG_ALG_NO_SUPPORTED; + } + + ret = _hx509_private_key_init(private_key, ops, NULL); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + return ret; + } + + ret = (*ops->generate_private_key)(context, *private_key); + if (ret) + _hx509_private_key_free(private_key); + + return ret; } @@ -1259,17 +1389,47 @@ hx509_signature_rsa(void) { return &_hx509_signature_rsa_data; } int -_hx509_new_private_key(hx509_private_key *key) +_hx509_private_key_init(hx509_private_key *key, + hx509_private_key_ops *ops, + void *keydata) { *key = calloc(1, sizeof(**key)); if (*key == NULL) return ENOMEM; + (*key)->ref = 1; + (*key)->ops = ops; + (*key)->private_key.keydata = keydata; return 0; } -int -_hx509_free_private_key(hx509_private_key *key) +hx509_private_key +_hx509_private_key_ref(hx509_private_key key) { + if (key->ref <= 0) + _hx509_abort("refcount <= 0"); + key->ref++; + if (key->ref == 0) + _hx509_abort("refcount == 0"); + return key; +} + +const char * +_hx509_private_pem_name(hx509_private_key key) +{ + return key->ops->pemtype; +} + +int +_hx509_private_key_free(hx509_private_key *key) +{ + if (key == NULL || *key == NULL) + return 0; + + if ((*key)->ref <= 0) + _hx509_abort("refcount <= 0"); + if (--(*key)->ref > 0) + return 0; + if ((*key)->private_key.rsa) RSA_free((*key)->private_key.rsa); (*key)->private_key.rsa = NULL; @@ -1288,6 +1448,41 @@ _hx509_private_key_assign_rsa(hx509_private_key key, void *ptr) key->md = &pkcs1_rsa_sha1_alg; } +int +_hx509_private_key_oid(hx509_context context, + const hx509_private_key key, + heim_oid *data) +{ + int ret; + ret = der_copy_oid((*key->ops->key_oid)(), data); + if (ret) + hx509_set_error_string(context, 0, ret, "malloc out of memory"); + return ret; +} + +int +_hx509_private_key_exportable(hx509_private_key key) +{ + if (key->ops->export == NULL) + return 0; + return 1; +} + +int +_hx509_private_key_export(hx509_context context, + const hx509_private_key key, + heim_octet_string *data) +{ + if (key->ops->export == NULL) { + hx509_clear_error_string(context); + return HX509_UNIMPLEMENTED_OPERATION; + } + return (*key->ops->export)(context, key, data); +} + +/* + * + */ struct hx509cipher { const char *name; @@ -2082,6 +2277,7 @@ _hx509_match_keys(hx509_cert c, hx509_private_key private_key) rsa->q = BN_dup(private_key->private_key.rsa->q); rsa->dmp1 = BN_dup(private_key->private_key.rsa->dmp1); rsa->dmq1 = BN_dup(private_key->private_key.rsa->dmq1); + rsa->iqmp = BN_dup(private_key->private_key.rsa->iqmp); if (rsa->n == NULL || rsa->e == NULL || rsa->d == NULL || rsa->p == NULL|| rsa->q == NULL || diff --git a/lib/hx509/hx509.h b/lib/hx509/hx509.h index 98ab9baba..33b365f4e 100644 --- a/lib/hx509/hx509.h +++ b/lib/hx509/hx509.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -93,6 +93,9 @@ typedef enum { HX509_QUERY_OPTION_END = 0xffff } hx509_query_option; +/* flags to hx509_certs_init */ +#define HX509_CERTS_CREATE 0x01 + /* flags to hx509_set_error_string */ #define HX509_ERROR_APPEND 0x01 diff --git a/lib/hx509/hx_locl.h b/lib/hx509/hx_locl.h index b426ec235..ab713734b 100644 --- a/lib/hx509/hx_locl.h +++ b/lib/hx509/hx_locl.h @@ -77,6 +77,8 @@ typedef struct hx509_path hx509_path; typedef void (*_hx509_cert_release_func)(struct hx509_cert_data *, void *); +typedef struct hx509_private_key_ops hx509_private_key_ops; + #include #include @@ -146,6 +148,7 @@ struct hx509_keyset_ops { int flags; int (*init)(hx509_context, hx509_certs, void **, int, const char *, hx509_lock); + int (*store)(hx509_context, hx509_certs, void *, int, hx509_lock); int (*free)(hx509_certs, void *); int (*add)(hx509_context, hx509_certs, void *, hx509_cert); int (*query)(hx509_context, hx509_certs, void *, @@ -155,6 +158,8 @@ struct hx509_keyset_ops { int (*iter_end)(hx509_context, hx509_certs, void *, void *); int (*printinfo)(hx509_context, hx509_certs, void *, int (*)(void *, char *), void *); + int (*getkeys)(hx509_context, hx509_certs, void *, hx509_private_key **); + int (*addkey)(hx509_context, hx509_certs, void *, hx509_private_key); }; struct _hx509_password { diff --git a/lib/hx509/hxtool-commands.in b/lib/hx509/hxtool-commands.in index 665c69b93..de2bdef00 100644 --- a/lib/hx509/hxtool-commands.in +++ b/lib/hx509/hxtool-commands.in @@ -236,6 +236,25 @@ command = { argument="certificate ..." help = "Validate content of certificates" } +command = { + name = "certificate-copy" + name = "cc" + option = { + long = "in-pass" + type = "strings" + argument = "password" + help = "password, prompter, or environment" + } + option = { + long = "out-pass" + type = "string" + argument = "password" + help = "password, prompter, or environment" + } + min_args="2" + argument="in-certificates-1 ... out-certificate" + help = "Copy in certificates stores into out certificate store" +} command = { name = "ocsp-fetch" option = { @@ -511,11 +530,6 @@ command = { type = "string" help = "pkcs10 request" } - option = { - long = "out-key" - type = "string" - help = "out key" - } option = { long = "generate-key" type = "string" diff --git a/lib/hx509/hxtool.c b/lib/hx509/hxtool.c index 598528ef9..9d010aab8 100644 --- a/lib/hx509/hxtool.c +++ b/lib/hx509/hxtool.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -540,6 +540,38 @@ pcert_validate(struct validate_options *opt, int argc, char **argv) return 0; } +int +certificate_copy(struct certificate_copy_options *opt, int argc, char **argv) +{ + hx509_certs certs; + hx509_lock lock; + int ret; + + hx509_lock_init(context, &lock); + lock_strings(lock, &opt->in_pass_strings); + + ret = hx509_certs_init(context, argv[argc - 1], + HX509_CERTS_CREATE, lock, &certs); + if (ret) + hx509_err(context, 1, ret, "hx509_certs_init"); + + while(argc-- > 1) { + int ret; + ret = hx509_certs_append(context, certs, lock, argv[0]); + if (ret) + hx509_err(context, 1, ret, "hx509_certs_append"); + argv++; + } + + ret = hx509_certs_store(context, certs, 0, NULL); + if (ret) + hx509_err(context, 1, ret, "hx509_certs_store"); + + hx509_certs_free(&certs); + + return 0; +} + struct verify { hx509_verify_ctx ctx; hx509_certs chain; @@ -801,24 +833,25 @@ ocsp_print(struct ocsp_print_options *opt, int argc, char **argv) static int read_private_key(const char *fn, hx509_private_key *key) { - void *data; - size_t len; + hx509_private_key *keys; + hx509_certs certs; int ret; *key = NULL; - ret = _hx509_map_file(fn, &data, &len, NULL); + ret = hx509_certs_init(context, fn, 0, NULL, &certs); if (ret) - err(1, "read_private_key: map_file: %s: %d", fn, ret); - - ret = _hx509_parse_private_key(context, - oid_id_pkcs1_rsaEncryption(), - data, - len, - key); - _hx509_unmap_file(data, len); + hx509_err(context, ret, 1, "hx509_certs_init: %s", fn); + + ret = _hx509_certs_keys_get(context, certs, &keys); + hx509_certs_free(&certs); if (ret) - errx(1, "read_private_key: _hx509_parse_private_key: %d", ret); + hx509_err(context, ret, 1, "hx509_certs_keys_get"); + if (keys[0] == NULL) + errx(1, "no keys in key store: %s", fn); + + *key = _hx509_private_key_ref(keys[0]); + _hx509_certs_keys_free(context, keys); return 0; } @@ -944,7 +977,7 @@ request_create(struct request_create_options *opt, int argc, char **argv) if (ret) hx509_err(context, ret, 1, "_hx509_request_to_pkcs10"); - _hx509_free_private_key(&signer); + _hx509_private_key_free(&signer); _hx509_request_free(&req); if (ret == 0) @@ -1269,6 +1302,7 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv) hx509_certs cacerts = NULL; hx509_cert signer = NULL, cert = NULL; hx509_private_key private_key = NULL; + hx509_private_key cert_key = NULL; hx509_name subject = NULL; SubjectPublicKeyInfo spki; int delta = 0; @@ -1319,7 +1353,8 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv) if (ret) hx509_err(context, ret, 1, "no CA certificate found"); } else if (opt->self_signed_flag) { - if (opt->generate_key_string == NULL && opt->ca_private_key_string == NULL) + if (opt->generate_key_string == NULL + && opt->ca_private_key_string == NULL) errx(1, "no signing private key"); } else errx(1, "missing ca key"); @@ -1364,19 +1399,19 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv) } if (opt->generate_key_string) { - hx509_private_key key; - get_key(opt->out_key_string, - opt->generate_key_string, - opt->key_bits_integer, - &key); - - ret = _hx509_private_key2SPKI(context, key, &spki); + ret = _hx509_generate_private_key(context, + oid_id_pkcs1_rsaEncryption(), + &cert_key); + if (ret) + hx509_err(context, ret, 1, "generate private key"); + + ret = _hx509_private_key2SPKI(context, cert_key, &spki); if (ret) errx(1, "_hx509_private_key2SPKI: %d\n", ret); if (opt->self_signed_flag) - private_key = key; + private_key = cert_key; } if (opt->subject_string) { @@ -1451,19 +1486,29 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv) hx509_err(context, ret, 1, "hx509_ca_sign"); } - { - Certificate *c = _hx509_get_cert(cert); - heim_octet_string data; - size_t size; - - ASN1_MALLOC_ENCODE(Certificate, data.data, data.length, c, &size, ret); + if (cert_key) { + ret = _hx509_cert_assign_key(cert, cert_key); if (ret) - err(1, "malloc out of memory"); - if (data.length != size) - _hx509_abort("internal ASN.1 encoder error"); + hx509_err(context, ret, 1, "_hx509_cert_assign_key"); + } - rk_dumpdata(opt->certificate_string, data.data, data.length); - free(data.data); + { + hx509_certs certs; + + ret = hx509_certs_init(context, opt->certificate_string, + HX509_CERTS_CREATE, NULL, &certs); + if (ret) + hx509_err(context, ret, 1, "hx509_certs_init"); + + ret = hx509_certs_add(context, certs, cert); + if (ret) + hx509_err(context, ret, 1, "hx509_certs_add"); + + ret = hx509_certs_store(context, certs, 0, NULL); + if (ret) + hx509_err(context, 1, ret, "hx509_certs_store"); + + hx509_certs_free(&certs); } return 0; diff --git a/lib/hx509/keyset.c b/lib/hx509/keyset.c index 01772d249..dd00ab1fe 100644 --- a/lib/hx509/keyset.c +++ b/lib/hx509/keyset.c @@ -122,6 +122,24 @@ hx509_certs_init(hx509_context context, return 0; } +int +hx509_certs_store(hx509_context context, + hx509_certs certs, + int flags, + hx509_lock lock) +{ + if (certs->ops->store == NULL) { + hx509_set_error_string(context, 0, EINVAL, + "keystore if type %s doesn't support " + "store operation", + certs->ops->name); + return EINVAL; + } + + return (*certs->ops->store)(context, certs, certs->ops_data, flags, lock); +} + + void hx509_certs_free(hx509_certs *certs) { @@ -381,3 +399,41 @@ _hx509_pi_printf(int (*func)(void *, char *), void *ctx, (*func)(ctx, str); free(str); } + +int +_hx509_certs_keys_get(hx509_context context, + hx509_certs certs, + hx509_private_key **keys) +{ + if (certs->ops->getkeys == NULL) { + *keys = NULL; + return 0; + } + return (*certs->ops->getkeys)(context, certs, certs->ops_data, keys); +} + +int +_hx509_certs_keys_add(hx509_context context, + hx509_certs certs, + hx509_private_key key) +{ + if (certs->ops->addkey == NULL) { + hx509_set_error_string(context, 0, EINVAL, + "keystore if type %s doesn't support " + "key add operation", + certs->ops->name); + return EINVAL; + } + return (*certs->ops->addkey)(context, certs, certs->ops_data, key); +} + + +void +_hx509_certs_keys_free(hx509_context context, + hx509_private_key *keys) +{ + int i; + for (i = 0; keys[i]; i++) + _hx509_private_key_free(&keys[i]); + free(keys); +} diff --git a/lib/hx509/ks_dir.c b/lib/hx509/ks_dir.c index 6f372b3b8..15d44982c 100644 --- a/lib/hx509/ks_dir.c +++ b/lib/hx509/ks_dir.c @@ -207,6 +207,7 @@ static struct hx509_keyset_ops keyset_dir = { "DIR", 0, dir_init, + NULL, dir_free, NULL, NULL, diff --git a/lib/hx509/ks_file.c b/lib/hx509/ks_file.c index 27a25ba41..fad7298dd 100644 --- a/lib/hx509/ks_file.c +++ b/lib/hx509/ks_file.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan + * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -36,6 +36,7 @@ RCSID("$Id$"); struct ks_file { hx509_certs certs; + char *fn; }; struct header { @@ -430,8 +431,12 @@ parse_pem_file(hx509_context context, break; } - p = malloc(i); + p = emalloc(i); i = base64_decode(buf, p); + if (i < 0) { + free(p); + goto out; + } data = erealloc(data, len + i); memcpy(((char *)data) + len, p, i); @@ -459,6 +464,7 @@ parse_pem_file(hx509_context context, "Found no matching PEM format for %s", type); } + out: free(data); data = NULL; len = 0; @@ -498,9 +504,10 @@ file_init(hx509_context context, hx509_certs certs, void **data, int flags, const char *residue, hx509_lock lock) { - char *files = NULL, *p, *pnext; + char *p, *pnext; struct ks_file *f = NULL; - struct hx509_collector *c; + struct hx509_collector *c = NULL; + hx509_private_key *keys = NULL; int ret; *data = NULL; @@ -508,25 +515,41 @@ file_init(hx509_context context, if (lock == NULL) lock = _hx509_empty_lock; - c = _hx509_collector_alloc(context, lock); - if (c == NULL) - return ENOMEM; - f = calloc(1, sizeof(*f)); if (f == NULL) { hx509_clear_error_string(context); - ret = ENOMEM; - goto out; + return ENOMEM; } - files = strdup(residue); - if (files == NULL) { + f->fn = strdup(residue); + if (f->fn == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } - for (p = files; p != NULL; p = pnext) { + /* + * XXX this is broken, the function should parse the file before + * overwriting it + */ + + if (flags & HX509_CERTS_CREATE) { + ret = hx509_certs_init(context, "MEMORY:ks-file-create", + 0, lock, &f->certs); + if (ret) + goto out; + *data = f; + return 0; + } + + c = _hx509_collector_alloc(context, lock); + if (c == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + + for (p = f->fn; p != NULL; p = pnext) { int found_data; pnext = strchr(p, ','); @@ -559,15 +582,29 @@ file_init(hx509_context context, } } - ret = _hx509_collector_collect(context, c, &f->certs); + ret = _hx509_collector_collect_certs(context, c, &f->certs); + if (ret) + goto out; + + ret = _hx509_collector_collect_private_keys(context, c, &keys); + if (ret == 0) { + int i; + + for (i = 0; keys[i]; i++) + _hx509_certs_keys_add(context, f->certs, keys[i]); + _hx509_certs_keys_free(context, keys); + } + out: if (ret == 0) *data = f; - else + else { + if (f->fn) + free(f->fn); free(f); - free(files); - - _hx509_collector_free(c); + } + if (c) + _hx509_collector_free(c); return ret; } @@ -576,11 +613,117 @@ file_free(hx509_certs certs, void *data) { struct ks_file *f = data; hx509_certs_free(&f->certs); + free(f->fn); free(f); return 0; } +static void +pem_header(FILE *f, const char *type, const char *str) +{ + fprintf(f, "-----%s %s-----\n", type, str); +} +static int +dump_pem_file(hx509_context context, const char *header, + FILE *f, const void *data, size_t size) +{ + const char *p = data; + size_t length; + char *line; + +#define ENCODE_LINE_LENGTH 54 + + pem_header(f, "BEGIN", header); + + while (size > 0) { + ssize_t l; + + length = size; + if (length > ENCODE_LINE_LENGTH) + length = ENCODE_LINE_LENGTH; + + l = base64_encode(p, length, &line); + if (l < 0) { + hx509_set_error_string(context, 0, ENOMEM, + "malloc - out of memory"); + return ENOMEM; + } + size -= length; + fprintf(f, "%s\n", line); + p += length; + free(line); + } + + pem_header(f, "END", header); + + return 0; +} + +static int +store_private_key(hx509_context context, FILE *f, hx509_private_key key) +{ + heim_octet_string data; + int ret; + + ret = _hx509_private_key_export(context, key, &data); + if (ret == 0) + dump_pem_file(context, _hx509_private_pem_name(key), f, + data.data, data.length); + free(data.data); + return ret; +} + +static int +store_func(hx509_context context, void *ctx, hx509_cert c) +{ + FILE *f = (FILE *)ctx; + size_t size; + heim_octet_string data; + int ret; + + ASN1_MALLOC_ENCODE(Certificate, data.data, data.length, + _hx509_get_cert(c), &size, ret); + if (ret) + return ret; + if (data.length != size) + _hx509_abort("internal ASN.1 encoder error"); + + dump_pem_file(context, "CERTIFICATE", f, data.data, data.length); + free(data.data); + + if (_hx509_cert_private_key_exportable(c)) + store_private_key(context, f, _hx509_cert_private_key(c)); + + return 0; +} + +static int +file_store(hx509_context context, + hx509_certs certs, void *data, int flags, hx509_lock lock) +{ + struct ks_file *f = data; + FILE *fh; + int ret; + + fh = fopen(f->fn, "w"); + if (fh == NULL) { + hx509_set_error_string(context, 0, ENOENT, + "Failed to open file %s for writing"); + return ENOENT; + } + + ret = hx509_certs_iter(context, f->certs, store_func, fh); + fclose(fh); + return ret; +} + +static int +file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) +{ + struct ks_file *f = data; + return hx509_certs_add(context, f->certs, c); +} static int file_iter_start(hx509_context context, @@ -608,17 +751,40 @@ file_iter_end(hx509_context context, return hx509_certs_end_seq(context, f->certs, cursor); } +static int +file_getkeys(hx509_context context, + hx509_certs certs, + void *data, + hx509_private_key **keys) +{ + struct ks_file *f = data; + return _hx509_certs_keys_get(context, f->certs, keys); +} + +static int +file_addkey(hx509_context context, + hx509_certs certs, + void *data, + hx509_private_key key) +{ + struct ks_file *f = data; + return _hx509_certs_keys_add(context, f->certs, key); +} static struct hx509_keyset_ops keyset_file = { "FILE", 0, file_init, + file_store, file_free, - NULL, + file_add, NULL, file_iter_start, file_iter, - file_iter_end + file_iter_end, + NULL, + file_getkeys, + file_addkey }; void diff --git a/lib/hx509/ks_mem.c b/lib/hx509/ks_mem.c index a2cedfad7..dd7b7166b 100644 --- a/lib/hx509/ks_mem.c +++ b/lib/hx509/ks_mem.c @@ -42,8 +42,11 @@ RCSID("Id$"); struct mem_data { char *name; - unsigned long len; - hx509_cert *val; + struct { + unsigned long len; + hx509_cert *val; + } certs; + hx509_private_key *keys; }; static int @@ -72,9 +75,11 @@ mem_free(hx509_certs certs, void *data) struct mem_data *mem = data; unsigned long i; - for (i = 0; i < mem->len; i++) - hx509_cert_free(mem->val[i]); - free(mem->val); + for (i = 0; i < mem->certs.len; i++) + hx509_cert_free(mem->certs.val[i]); + free(mem->certs.val); + for (i = 0; mem->keys && mem->keys[i]; i++) + _hx509_private_key_free(&mem->keys[i]); free(mem->name); free(mem); @@ -87,13 +92,14 @@ mem_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) struct mem_data *mem = data; hx509_cert *val; - val = realloc(mem->val, (mem->len + 1) * sizeof(mem->val[0])); + val = realloc(mem->certs.val, + (mem->certs.len + 1) * sizeof(mem->certs.val[0])); if (val == NULL) return ENOMEM; - mem->val = val; - mem->val[mem->len] = hx509_cert_ref(c); - mem->len++; + mem->certs.val = val; + mem->certs.val[mem->certs.len] = hx509_cert_ref(c); + mem->certs.len++; return 0; } @@ -125,12 +131,12 @@ mem_iter(hx509_context contexst, unsigned long *iter = cursor; struct mem_data *mem = data; - if (*iter >= mem->len) { + if (*iter >= mem->certs.len) { *cert = NULL; return 0; } - *cert = hx509_cert_ref(mem->val[*iter]); + *cert = hx509_cert_ref(mem->certs.val[*iter]); (*iter)++; return 0; } @@ -145,16 +151,69 @@ mem_iter_end(hx509_context context, return 0; } +static int +mem_getkeys(hx509_context context, + hx509_certs certs, + void *data, + hx509_private_key **keys) +{ + struct mem_data *mem = data; + int i; + + for (i = 0; mem->keys && mem->keys[i]; i++) + ; + *keys = calloc(i, sizeof(**keys)); + for (i = 0; mem->keys && mem->keys[i]; i++) { + (*keys)[i] = _hx509_private_key_ref(mem->keys[i]); + if ((*keys)[i] == NULL) { + while (--i >= 0) + _hx509_private_key_free(&(*keys)[i]); + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + } + (*keys)[i] = NULL; + return 0; +} + +static int +mem_addkey(hx509_context context, + hx509_certs certs, + void *data, + hx509_private_key key) +{ + struct mem_data *mem = data; + void *ptr; + int i; + + for (i = 0; mem->keys && mem->keys[i]; i++) + ; + ptr = realloc(mem->keys, (i + 2) * sizeof(*mem->keys)); + if (ptr == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + mem->keys = ptr; + mem->keys[i++] = _hx509_private_key_ref(key); + mem->keys[i++] = NULL; + return 0; +} + + static struct hx509_keyset_ops keyset_mem = { "MEMORY", 0, mem_init, + NULL, mem_free, mem_add, NULL, mem_iter_start, mem_iter, - mem_iter_end + mem_iter_end, + NULL, + mem_getkeys, + mem_addkey }; void diff --git a/lib/hx509/ks_null.c b/lib/hx509/ks_null.c index b3b3e6ffb..934eef5a0 100644 --- a/lib/hx509/ks_null.c +++ b/lib/hx509/ks_null.c @@ -82,6 +82,7 @@ struct hx509_keyset_ops keyset_null = { "NULL", 0, null_init, + NULL, null_free, NULL, NULL, diff --git a/lib/hx509/ks_p11.c b/lib/hx509/ks_p11.c index 77f6479ee..beabed0a1 100644 --- a/lib/hx509/ks_p11.c +++ b/lib/hx509/ks_p11.c @@ -616,7 +616,7 @@ collect_private_key(hx509_context context, localKeyId.data = query[0].pValue; localKeyId.length = query[0].ulValueLen; - ret = _hx509_new_private_key(&key); + ret = _hx509_private_key_init(&key, NULL, NULL); if (ret) return ret; @@ -661,7 +661,7 @@ collect_private_key(hx509_context context, &localKeyId); if (ret) { - _hx509_free_private_key(&key); + _hx509_private_key_free(&key); return ret; } return 0; @@ -790,7 +790,7 @@ p11_list_keys(hx509_context context, if (ret) goto out; - ret = _hx509_collector_collect(context, collector, &slot->certs); + ret = _hx509_collector_collect_certs(context, collector, &slot->certs); out: _hx509_collector_free(collector); @@ -1164,6 +1164,7 @@ static struct hx509_keyset_ops keyset_pkcs11 = { "PKCS11", 0, p11_init, + NULL, p11_free, NULL, NULL, diff --git a/lib/hx509/ks_p12.c b/lib/hx509/ks_p12.c index 4685ca0fa..45f186ae2 100644 --- a/lib/hx509/ks_p12.c +++ b/lib/hx509/ks_p12.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan + * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -36,6 +36,7 @@ RCSID("$Id$"); struct ks_pkcs12 { hx509_certs certs; + char *fn; }; typedef int (*collector_func)(hx509_context, @@ -65,24 +66,47 @@ find_attribute(const PKCS12_Attributes *attrs, const heim_oid *oid) return NULL; } +static int +keyBag_parser(hx509_context context, + struct hx509_collector *c, + const void *data, size_t length, + const PKCS12_Attributes *attrs) +{ + const PKCS12_Attribute *attr; + PKCS8PrivateKeyInfo ki; + const heim_octet_string *os = NULL; + int ret; + + attr = find_attribute(attrs, oid_id_pkcs_9_at_localKeyId()); + if (attr) + os = &attr->attrValues; + + ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL); + if (ret) + return ret; + + _hx509_collector_private_key_add(context, + c, + &ki.privateKeyAlgorithm, + NULL, + &ki.privateKey, + &attr->attrValues); + free_PKCS8PrivateKeyInfo(&ki); + return 0; +} + static int ShroudedKeyBag_parser(hx509_context context, struct hx509_collector *c, const void *data, size_t length, const PKCS12_Attributes *attrs) { - const PKCS12_Attribute *attr; PKCS8EncryptedPrivateKeyInfo pk; - PKCS8PrivateKeyInfo ki; heim_octet_string content; int ret; memset(&pk, 0, sizeof(pk)); - attr = find_attribute(attrs, oid_id_pkcs_9_at_localKeyId()); - if (attr == NULL) - return 0; - ret = decode_PKCS8EncryptedPrivateKeyInfo(data, length, &pk, NULL); if (ret) return ret; @@ -96,22 +120,9 @@ ShroudedKeyBag_parser(hx509_context context, if (ret) return ret; - ret = decode_PKCS8PrivateKeyInfo(content.data, content.length, - &ki, NULL); + ret = keyBag_parser(context, c, content.data, content.length, attrs); der_free_octet_string(&content); - if (ret) - return ret; - - _hx509_collector_private_key_add(context, - c, - &ki.privateKeyAlgorithm, - NULL, - &ki.privateKey, - &attr->attrValues); - - free_PKCS8PrivateKeyInfo(&ki); - - return 0; + return ret; } static int @@ -130,6 +141,11 @@ certBag_parser(hx509_context context, if (ret) return ret; + if (der_heim_oid_cmp(oid_id_pkcs_9_at_certTypes_x509(), &cb.certType)) { + free_PKCS12_CertBag(&cb); + return 0; + } + ret = decode_PKCS12_OctetString(cb.certValue.data, cb.certValue.length, &os, @@ -285,6 +301,7 @@ envelopedData_parser(hx509_context context, struct type bagtypes[] = { + { oid_id_pkcs12_keyBag, keyBag_parser }, { oid_id_pkcs12_pkcs8ShroudedKeyBag, ShroudedKeyBag_parser }, { oid_id_pkcs12_certBag, certBag_parser }, { oid_id_pkcs7_data, safeContent_parser }, @@ -334,6 +351,21 @@ p12_init(hx509_context context, goto out; } + p12->fn = strdup(residue); + if (p12->fn == NULL) { + ret = ENOMEM; + goto out; + } + + if (flags & HX509_CERTS_CREATE) { + ret = hx509_certs_init(context, "MEMORY:ks-file-create", + 0, lock, &p12->certs); + if (ret) + goto out; + *data = p12; + return 0; + } + ret = _hx509_map_file(residue, &buf, &len, NULL); if (ret) goto out; @@ -392,7 +424,7 @@ p12_init(hx509_context context, free_PKCS12_AuthenticatedSafe(&as); - ret = _hx509_collector_collect(context, c, &p12->certs); + ret = _hx509_collector_collect_certs(context, c, &p12->certs); if (ret == 0) *data = p12; @@ -408,15 +440,212 @@ out: return ret; } +static int +addBag(hx509_context context, + PKCS12_AuthenticatedSafe *as, + const heim_oid *oid, + void *data, + size_t length) +{ + void *ptr; + int ret; + + ptr = realloc(as->val, sizeof(as->val[0]) * (as->len + 1)); + if (ptr == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "malloc out of memory"); + return ENOMEM; + } + as->val = ptr; + + ret = der_copy_oid(oid, &as->val[as->len].contentType); + + as->val[as->len].content = calloc(1, sizeof(*as->val[0].content)); + if (as->val[as->len].content == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "malloc out of memory"); + return ENOMEM; + } + + as->val[as->len].content->data = data; + as->val[as->len].content->length = length; + + as->len++; + + return 0; +} + +static int +store_func(hx509_context context, void *ctx, hx509_cert c) +{ + PKCS12_AuthenticatedSafe *as = ctx; + PKCS12_OctetString os; + PKCS12_CertBag cb; + size_t size; + int ret; + + memset(&os, 0, sizeof(os)); + memset(&cb, 0, sizeof(cb)); + + os.data = NULL; + os.length = 0; + + ASN1_MALLOC_ENCODE(Certificate, os.data, os.length, + _hx509_get_cert(c), &size, ret); + if (ret) + goto out; + ASN1_MALLOC_ENCODE(PKCS12_OctetString, + cb.certValue.data,cb.certValue.length, + &os, &size, ret); + free(os.data); + if (ret) + goto out; + ret = der_copy_oid(oid_id_pkcs_9_at_certTypes_x509(), &cb.certType); + if (ret) { + free_PKCS12_CertBag(&cb); + goto out; + } + ASN1_MALLOC_ENCODE(PKCS12_CertBag, os.data, os.length, + &cb, &size, ret); + free(cb.certValue.data); + if (ret) + goto out; + + ret = addBag(context, as, oid_id_pkcs12_certBag(), os.data, os.length); + + if (_hx509_cert_private_key_exportable(c)) { + hx509_private_key key = _hx509_cert_private_key(c); + PKCS8PrivateKeyInfo pki; + + memset(&pki, 0, sizeof(pki)); + + ret = der_parse_hex_heim_integer("00", &pki.version); + if (ret) + return ret; + ret = _hx509_private_key_oid(context, key, + &pki.privateKeyAlgorithm.algorithm); + if (ret) { + free_PKCS8PrivateKeyInfo(&pki); + return ret; + } + ret = _hx509_private_key_export(context, + _hx509_cert_private_key(c), + &pki.privateKey); + if (ret) { + free_PKCS8PrivateKeyInfo(&pki); + return ret; + } + /* set attribute, oid_id_pkcs_9_at_localKeyId() */ + + ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, os.data, os.length, + &pki, &size, ret); + free_PKCS8PrivateKeyInfo(&pki); + if (ret) + return ret; + + ret = addBag(context, as, oid_id_pkcs12_keyBag(), os.data, os.length); + if (ret) + return ret; + } + +out: + return ret; +} + +static int +p12_store(hx509_context context, + hx509_certs certs, void *data, int flags, hx509_lock lock) +{ + struct ks_pkcs12 *p12 = data; + PKCS12_PFX pfx; + PKCS12_AuthenticatedSafe as; + PKCS12_OctetString asdata; + size_t size; + int ret; + + memset(&as, 0, sizeof(as)); + memset(&pfx, 0, sizeof(pfx)); + + ret = hx509_certs_iter(context, p12->certs, store_func, &as); + if (ret) + goto out; + + ASN1_MALLOC_ENCODE(PKCS12_AuthenticatedSafe, asdata.data, asdata.length, + &as, &size, ret); + free_PKCS12_AuthenticatedSafe(&as); + if (ret) + return ret; + + ret = der_parse_hex_heim_integer("03", &pfx.version); + if (ret) { + free(asdata.data); + goto out; + } + + pfx.authSafe.content = calloc(1, sizeof(*pfx.authSafe.content)); + + ASN1_MALLOC_ENCODE(PKCS12_OctetString, + pfx.authSafe.content->data, + pfx.authSafe.content->length, + &asdata, &size, ret); + free(asdata.data); + if (ret) + goto out; + + ret = der_copy_oid(oid_id_pkcs7_data(), &pfx.authSafe.contentType); + if (ret) + goto out; + + ASN1_MALLOC_ENCODE(PKCS12_PFX, asdata.data, asdata.length, + &pfx, &size, ret); + if (ret) + goto out; + +#if 0 + const struct _hx509_password *pw; + + pw = _hx509_lock_get_passwords(lock); + if (pw != NULL) { + pfx.macData = calloc(1, sizeof(*pfx.macData)); + if (pfx.macData == NULL) { + ret = ENOMEM; + hx509_set_error_string(context, 0, ret, "malloc out of memory"); + return ret; + } + if (pfx.macData == NULL) { + free(asdata.data); + goto out; + } + } + ret = calculate_hash(&aspath, pw, pfx.macData); +#endif + + rk_dumpdata(p12->fn, asdata.data, asdata.length); + free(asdata.data); + +out: + free_PKCS12_AuthenticatedSafe(&as); + free_PKCS12_PFX(&pfx); + + return ret; +} + + static int p12_free(hx509_certs certs, void *data) { struct ks_pkcs12 *p12 = data; hx509_certs_free(&p12->certs); + free(p12->fn); free(p12); return 0; } +static int +p12_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) +{ + struct ks_pkcs12 *p12 = data; + return hx509_certs_add(context, p12->certs, c); +} + static int p12_iter_start(hx509_context context, hx509_certs certs, @@ -452,8 +681,9 @@ static struct hx509_keyset_ops keyset_pkcs12 = { "PKCS12", 0, p12_init, + p12_store, p12_free, - NULL, + p12_add, NULL, p12_iter_start, p12_iter, diff --git a/lib/hx509/test_ca.in b/lib/hx509/test_ca.in index b4889f4b2..c4b6ca020 100644 --- a/lib/hx509/test_ca.in +++ b/lib/hx509/test_ca.in @@ -44,7 +44,7 @@ fi echo "create certificate request" ${hxtool} request-create \ --subject="CN=Love,DC=it,DC=su,DC=se" \ - --key=$srcdir/data/key.der \ + --key=FILE:$srcdir/data/key.der \ pkcs10-request.der || exit 1 echo "issue certificate" @@ -52,7 +52,7 @@ ${hxtool} issue-certificate \ --ca-certificate=FILE:$srcdir/data/ca.crt,$srcdir/data/ca.key \ --subject="cn=foo" \ --req="pkcs10-request.der" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "verify certificate" ${hxtool} verify --missing-revoke \ @@ -65,7 +65,7 @@ ${hxtool} issue-certificate \ --subject="cn=foo" \ --lifetime="10years 1 month" \ --req="pkcs10-request.der" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "issue certificate (with https ekus)" ${hxtool} issue-certificate \ @@ -74,7 +74,7 @@ ${hxtool} issue-certificate \ --type="https-server" \ --type="https-client" \ --req="pkcs10-request.der" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "issue certificate (pkinit KDC)" ${hxtool} issue-certificate \ @@ -83,7 +83,7 @@ ${hxtool} issue-certificate \ --type="pkinit-kdc" \ --pk-init-principal="krbtgt/TEST.H5L.SE@TEST.H5L.SE" \ --req="pkcs10-request.der" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "issue certificate (pkinit client)" ${hxtool} issue-certificate \ @@ -92,7 +92,7 @@ ${hxtool} issue-certificate \ --type="pkinit-client" \ --pk-init-principal="lha@TEST.H5L.SE" \ --req="pkcs10-request.der" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "issue certificate (hostnames)" ${hxtool} issue-certificate \ @@ -102,7 +102,7 @@ ${hxtool} issue-certificate \ --hostname="www.test.h5l.se" \ --hostname="ftp.test.h5l.se" \ --req="pkcs10-request.der" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "issue certificate (email)" ${hxtool} issue-certificate \ @@ -111,14 +111,14 @@ ${hxtool} issue-certificate \ --email="lha@test.h5l.se" \ --email="test@test.h5l.se" \ --req="pkcs10-request.der" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "issue self-signed cert" ${hxtool} issue-certificate \ --self-signed \ - --ca-private-key=$srcdir/data/key.der \ + --ca-private-key=FILE:$srcdir/data/key.der \ --subject="cn=test" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "issue ca cert" ${hxtool} issue-certificate \ @@ -126,22 +126,22 @@ ${hxtool} issue-certificate \ --issue-ca \ --subject="cn=ca-cert" \ --req="pkcs10-request.der" \ - --certificate="cert-ca.der" || exit 1 + --certificate="FILE:cert-ca.der" || exit 1 echo "issue self-signed ca cert" ${hxtool} issue-certificate \ --self-signed \ --issue-ca \ - --ca-private-key=$srcdir/data/key.der \ + --ca-private-key=FILE:$srcdir/data/key.der \ --subject="cn=ca-root" \ - --certificate="cert-ca.der" || exit 1 + --certificate="FILE:cert-ca.der" || exit 1 echo "issue proxy certificate" ${hxtool} issue-certificate \ --ca-certificate=FILE:$srcdir/data/test.crt,$srcdir/data/test.key \ --issue-proxy \ --req="pkcs10-request.der" \ - --certificate="cert-proxy.der" || exit 1 + --certificate="FILE:cert-proxy.der" || exit 1 echo "verify proxy cert" ${hxtool} verify --missing-revoke \ @@ -156,17 +156,15 @@ ${hxtool} issue-certificate \ --issue-ca \ --serial-number="deadbeaf" \ --generate-key=rsa \ - --out-key="ca2-key.der" \ --subject="cn=ca2-cert" \ - --certificate="cert-ca.der" || exit 1 + --certificate="FILE:cert-ca.der" || exit 1 echo "issue ee cert (generate rsa key)" ${hxtool} issue-certificate \ - --ca-certificate=FILE:cert-ca.der,ca2-key.der \ + --ca-certificate=FILE:cert-ca.der \ --generate-key=rsa \ - --out-key="ee2-key.der" \ --subject="cn=cert-ee2" \ - --certificate="cert-ee.der" || exit 1 + --certificate="FILE:cert-ee.der" || exit 1 echo "verify certificate" ${hxtool} verify --missing-revoke \ @@ -186,17 +184,15 @@ ${hxtool} cms-verify-sd \ sd.data sd.data.out > /dev/null || exit 1 cmp "$srcdir/test_name.c" sd.data.out || exit 1 -openssl x509 -in cert-ca.der -inform der -text > diff1 echo "extend ca cert" ${hxtool} issue-certificate \ --self-signed \ --issue-ca \ --lifetime="2years" \ --serial-number="deadbeaf" \ - --ca-private-key=ca2-key.der \ + --ca-private-key=FILE:cert-ca.der \ --subject="cn=ca2-cert" \ - --certificate="cert-ca.der" || exit 1 -openssl x509 -in cert-ca.der -inform der -text > diff2 + --certificate="FILE:cert-ca.der" || exit 1 echo "verify certificate generated by previous ca" ${hxtool} verify --missing-revoke \ diff --git a/lib/hx509/test_crypto.in b/lib/hx509/test_crypto.in index 8100db12e..652a8ca32 100644 --- a/lib/hx509/test_crypto.in +++ b/lib/hx509/test_crypto.in @@ -148,5 +148,20 @@ ${hxtool} crypto-available \ cmp test ${srcdir}/tst-crypto-available3 > /dev/null || \ { echo "available3 failure"; exit 1; } +echo "copy keystore FILE existing -> FILE" +${hxtool} certificate-copy \ + FILE:${srcdir}/data/test.crt,${srcdir}/data/test.key \ + FILE:out.pem || exit 1 + +echo "copy keystore FILE -> FILE" +${hxtool} certificate-copy \ + FILE:out.pem \ + FILE:out2.pem || exit 1 + +echo "copy keystore FILE -> PKCS12" +${hxtool} certificate-copy \ + FILE:out.pem \ + PKCS12:out2.pem || exit 1 + exit 0 diff --git a/lib/hx509/test_query.in b/lib/hx509/test_query.in index 4f25c8a81..ad7f2a33e 100644 --- a/lib/hx509/test_query.in +++ b/lib/hx509/test_query.in @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2005 Kungliga Tekniska Högskolan +# Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan # (Royal Institute of Technology, Stockholm, Sweden). # All rights reserved. # @@ -60,6 +60,11 @@ ${hxtool} query \ --friendlyname=friendlyname-test-not \ PKCS12:$srcdir/data/test.p12 >/dev/null 2>/dev/null && exit 1 +echo "make sure entry is found (friendlyname, no-pw)" +${hxtool} query \ + --friendlyname=friendlyname-cert \ + PKCS12:$srcdir/data/test-nopw.p12 >/dev/null 2>/dev/null || exit 1 + echo "check for ca cert (friendlyname)" ${hxtool} query \ --pass=PASS:foobar \ diff --git a/lib/hx509/test_req.in b/lib/hx509/test_req.in index 1491f9df4..6c5000961 100644 --- a/lib/hx509/test_req.in +++ b/lib/hx509/test_req.in @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2005 Kungliga Tekniska Högskolan +# Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan # (Royal Institute of Technology, Stockholm, Sweden). # All rights reserved. # @@ -44,7 +44,7 @@ fi ${hxtool} request-create \ --subject="CN=Love,DC=it,DC=su,DC=se" \ - --key=$srcdir/data/key.der \ + --key=FILE:$srcdir/data/key.der \ request.out || exit 1 ${hxtool} pkcs10-print \ @@ -53,5 +53,5 @@ ${hxtool} pkcs10-print \ ${hxtool} request-create \ --subject="CN=Love,DC=it,DC=su,DC=se" \ --dnsname=nutcracker.it.su.se \ - --key=$srcdir/data/key.der \ + --key=FILE:$srcdir/data/key.der \ request.out || exit 1