/** * Parse an EC domain parameter identifier as defined in RFC 5656 */ static chunk_t parse_ec_identifier(chunk_t identifier) { chunk_t oid = chunk_empty; if (chunk_equals(identifier, chunk_from_str("nistp256"))) { oid = asn1_build_known_oid(OID_PRIME256V1); } else if (chunk_equals(identifier, chunk_from_str("nistp384"))) { oid = asn1_build_known_oid(OID_SECT384R1); } else if (chunk_equals(identifier, chunk_from_str("nistp521"))) { oid = asn1_build_known_oid(OID_SECT521R1); } else { char ascii[64]; if (snprintf(ascii, sizeof(ascii), "%.*s", (int)identifier.len, identifier.ptr) < sizeof(ascii)) { oid = asn1_wrap(ASN1_OID, "m", asn1_oid_from_string(ascii)); } } return oid; }
/** * Create a self signed certificate. */ static int self() { cred_encoding_type_t form = CERT_ASN1_DER; key_type_t type = KEY_RSA; hash_algorithm_t digest = HASH_SHA1; certificate_t *cert = NULL; private_key_t *private = NULL; public_key_t *public = NULL; char *file = NULL, *dn = NULL, *hex = NULL, *error = NULL, *keyid = NULL; identification_t *id = NULL; linked_list_t *san, *ocsp, *permitted, *excluded, *policies, *mappings; int pathlen = X509_NO_CONSTRAINT, inhibit_any = X509_NO_CONSTRAINT; int inhibit_mapping = X509_NO_CONSTRAINT, require_explicit = X509_NO_CONSTRAINT; chunk_t serial = chunk_empty; chunk_t encoding = chunk_empty; time_t lifetime = 1095; time_t not_before, not_after; x509_flag_t flags = 0; x509_cert_policy_t *policy = NULL; char *arg; san = linked_list_create(); ocsp = linked_list_create(); permitted = linked_list_create(); excluded = linked_list_create(); policies = linked_list_create(); mappings = linked_list_create(); while (TRUE) { switch (command_getopt(&arg)) { case 'h': goto usage; case 't': if (streq(arg, "rsa")) { type = KEY_RSA; } else if (streq(arg, "ecdsa")) { type = KEY_ECDSA; } else { error = "invalid input type"; goto usage; } continue; case 'g': digest = enum_from_name(hash_algorithm_short_names, arg); if (digest == -1) { error = "invalid --digest type"; goto usage; } continue; case 'i': file = arg; continue; case 'x': keyid = arg; continue; case 'd': dn = arg; continue; case 'a': san->insert_last(san, identification_create_from_string(arg)); continue; case 'l': lifetime = atoi(arg); if (!lifetime) { error = "invalid --lifetime value"; goto usage; } continue; case 's': hex = arg; continue; case 'b': flags |= X509_CA; continue; case 'p': pathlen = atoi(arg); continue; case 'n': permitted->insert_last(permitted, identification_create_from_string(arg)); continue; case 'N': excluded->insert_last(excluded, identification_create_from_string(arg)); continue; case 'P': { chunk_t oid; oid = asn1_oid_from_string(arg); if (!oid.len) { error = "--cert-policy OID invalid"; goto usage; } INIT(policy, .oid = oid, ); policies->insert_last(policies, policy); continue; } case 'C': if (!policy) { error = "--cps-uri must follow a --cert-policy"; goto usage; } policy->cps_uri = arg; continue; case 'U': if (!policy) { error = "--user-notice must follow a --cert-policy"; goto usage; } policy->unotice_text = arg; continue; case 'M': { char *pos = strchr(arg, ':'); x509_policy_mapping_t *mapping; chunk_t subject_oid, issuer_oid; if (pos) { *pos++ = '\0'; issuer_oid = asn1_oid_from_string(arg); subject_oid = asn1_oid_from_string(pos); } if (!pos || !issuer_oid.len || !subject_oid.len) { error = "--policy-map OIDs invalid"; goto usage; } INIT(mapping, .issuer = issuer_oid, .subject = subject_oid, ); mappings->insert_last(mappings, mapping); continue; } case 'E': require_explicit = atoi(arg); continue; case 'H': inhibit_mapping = atoi(arg); continue; case 'A': inhibit_any = atoi(arg); continue; case 'e': if (streq(arg, "serverAuth")) { flags |= X509_SERVER_AUTH; } else if (streq(arg, "clientAuth")) { flags |= X509_CLIENT_AUTH; } else if (streq(arg, "ikeIntermediate")) { flags |= X509_IKE_INTERMEDIATE; } else if (streq(arg, "crlSign")) { flags |= X509_CRL_SIGN; } else if (streq(arg, "ocspSigning")) { flags |= X509_OCSP_SIGNER; } continue; case 'f': if (!get_form(arg, &form, CRED_CERTIFICATE)) { error = "invalid output format"; goto usage; } continue; case 'o': ocsp->insert_last(ocsp, arg); continue; case EOF: break; default: error = "invalid --self option"; goto usage; } break; } if (!dn) { error = "--dn is required"; goto usage; } id = identification_create_from_string(dn); if (id->get_type(id) != ID_DER_ASN1_DN) { error = "supplied --dn is not a distinguished name"; goto end; } if (file) { private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type, BUILD_FROM_FILE, file, BUILD_END); }
END_TEST /******************************************************************************* * oid_from_string */ START_TEST(test_asn1_oid_from_string) { typedef struct { char *string; chunk_t oid; } testdata_t; testdata_t test[] = { { "", chunk_empty }, { " ", chunk_empty }, { "0.2.262.1", chunk_from_chars( 0x02, 0x82, 0x06, 0x01) }, { "1.2.840.10045.4.1", chunk_from_chars( 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01) }, { "1.3.6.1.4.1.36906.1", chunk_from_chars( 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa0, 0x2a, 0x01) }, { "2.16.840.1.101.3.4.2.1", chunk_from_chars( 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01) }, { "0.10.100.1000.10000.100000.1000000.10000000.100000000.268435455", chunk_from_chars(0x0a,0x64, 0x87, 0x68, 0xce, 0x10, 0x86, 0x8d, 0x20, 0xbd, 0x84, 0x40, 0x84, 0xe2, 0xad, 0x00, 0xaf, 0xd7, 0xc2, 0x00, 0xff, 0xff, 0xff, 0x7f) }, { "0.1.2.3.4.5.6.7.8.9.10.128.129.130.131.132.133.134.135.136.137." "256.257.258.259.260.261.262.263.264.265.384.385.386.387.388." "2097153", chunk_from_chars( 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x81, 0x00, 0x81, 0x01, 0x81, 0x02, 0x81, 0x03, 0x81, 0x04, 0x81, 0x05, 0x81, 0x06, 0x81, 0x07, 0x81, 0x08, 0x81, 0x09, 0x82, 0x00, 0x82, 0x01, 0x82, 0x02, 0x82, 0x03, 0x82, 0x04, 0x82, 0x05, 0x82, 0x06, 0x82, 0x07, 0x82, 0x08, 0x82, 0x09, 0x83, 0x00, 0x83, 0x01, 0x83, 0x02, 0x83, 0x03, 0x83, 0x04, 0x81, 0x80, 0x80, 0x01) }, { "0.1.2.3.4.5.6.7.8.9.10.128.129.130.131.132.133.134.135.136.137." "256.257.258.259.260.261.262.263.264.265.384.385.386.387.388." "1.2097153", chunk_empty }, { "1.a.2.b.3", chunk_empty } }; int i; chunk_t oid = chunk_empty; for (i = 0; i < countof(test); i++) { oid = asn1_oid_from_string(test[i].string); if (test[i].oid.len == 0) { ck_assert(oid.len == 0 && oid.ptr == NULL); } else { ck_assert(chunk_equals(oid, test[i].oid)); chunk_free(&oid); } } }
/** * Sign a CRL */ static int sign_crl() { cred_encoding_type_t form = CERT_ASN1_DER; private_key_t *private = NULL; public_key_t *public = NULL; certificate_t *ca = NULL, *crl = NULL; crl_t *lastcrl = NULL; x509_t *x509; hash_algorithm_t digest = HASH_UNKNOWN; signature_params_t *scheme = NULL; char *arg, *cacert = NULL, *cakey = NULL, *lastupdate = NULL, *error = NULL; char *basecrl = NULL; char serial[512], *keyid = NULL; int serial_len; crl_reason_t reason = CRL_REASON_UNSPECIFIED; time_t thisUpdate, nextUpdate, date = time(NULL); time_t lifetime = 15 * 24 * 60 * 60; char *datetu = NULL, *datenu = NULL, *dateform = NULL; linked_list_t *list, *cdps; enumerator_t *enumerator, *lastenum = NULL; x509_cdp_t *cdp; chunk_t crl_serial = chunk_empty, baseCrlNumber = chunk_empty; chunk_t critical_extension_oid = chunk_empty; chunk_t encoding = chunk_empty; bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE, lib->ns); list = linked_list_create(); cdps = linked_list_create(); while (TRUE) { switch (command_getopt(&arg)) { case 'h': goto usage; case 'g': if (!enum_from_name(hash_algorithm_short_names, arg, &digest)) { error = "invalid --digest type"; goto usage; } continue; case 'R': if (streq(arg, "pss")) { pss = TRUE; } else if (!streq(arg, "pkcs1")) { error = "invalid RSA padding"; goto usage; } continue; case 'c': cacert = arg; continue; case 'k': cakey = arg; continue; case 'x': keyid = arg; continue; case 'a': lastupdate = arg; continue; case 'l': lifetime = atoi(arg) * 24 * 60 * 60; if (!lifetime) { error = "invalid --lifetime value"; goto usage; } continue; case 'D': dateform = arg; continue; case 'F': datetu = arg; continue; case 'T': datenu = arg; continue; case 'z': serial_len = read_serial(arg, serial, sizeof(serial)); if (serial_len < 0) { snprintf(serial, sizeof(serial), "parsing certificate '%s' failed", arg); error = serial; goto error; } add_revoked(list, chunk_create(serial, serial_len), reason, date); date = time(NULL); reason = CRL_REASON_UNSPECIFIED; continue; case 's': { chunk_t chunk; int hex_len; hex_len = strlen(arg); if ((hex_len / 2) + (hex_len % 2) > sizeof(serial)) { error = "invalid serial"; goto usage; } chunk = chunk_from_hex(chunk_create(arg, hex_len), serial); serial_len = chunk.len; add_revoked(list, chunk_create(serial, serial_len), reason, date); date = time(NULL); reason = CRL_REASON_UNSPECIFIED; continue; } case 'b': basecrl = arg; continue; case 'u': INIT(cdp, .uri = strdup(arg), ); cdps->insert_last(cdps, cdp); continue; case 'r': if (streq(arg, "key-compromise")) { reason = CRL_REASON_KEY_COMPROMISE; } else if (streq(arg, "ca-compromise")) { reason = CRL_REASON_CA_COMPROMISE; } else if (streq(arg, "affiliation-changed")) { reason = CRL_REASON_AFFILIATION_CHANGED; } else if (streq(arg, "superseded")) { reason = CRL_REASON_SUPERSEDED; } else if (streq(arg, "cessation-of-operation")) { reason = CRL_REASON_CESSATION_OF_OPERATON; } else if (streq(arg, "certificate-hold")) { reason = CRL_REASON_CERTIFICATE_HOLD; } else { error = "invalid revocation reason"; goto usage; } continue; case 'd': date = atol(arg); if (!date) { error = "invalid date"; goto usage; } continue; case 'f': if (!get_form(arg, &form, CRED_CERTIFICATE)) { error = "invalid output format"; goto usage; } continue; case 'X': chunk_free(&critical_extension_oid); critical_extension_oid = asn1_oid_from_string(arg); continue; case EOF: break; default: error = "invalid --signcrl option"; goto usage; } break; } if (!cacert) { error = "--cacert is required"; goto usage; } if (!cakey && !keyid) { error = "--cakey or --keyid is required"; goto usage; } if (!calculate_lifetime(dateform, datetu, datenu, lifetime, &thisUpdate, &nextUpdate)) { error = "invalid --this/next-update datetime"; goto usage; } ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, cacert, BUILD_END); if (!ca) { error = "parsing CA certificate failed"; goto error; } x509 = (x509_t*)ca; if (!(x509->get_flags(x509) & (X509_CA | X509_CRL_SIGN))) { error = "CA certificate misses CA basicConstraint / CRLSign keyUsage"; goto error; } public = ca->get_public_key(ca);