1*d40cb843Sjoerg /* $NetBSD: nbsvtool.c,v 1.2 2008/06/11 16:31:09 joerg Exp $ */ 21762b349Sjoerg 31762b349Sjoerg /*- 41762b349Sjoerg * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc. 51762b349Sjoerg * All rights reserved. 61762b349Sjoerg * 71762b349Sjoerg * This code is derived from software contributed to The NetBSD Foundation 81762b349Sjoerg * by Love H�rnquist �strand <lha@it.su.se> 91762b349Sjoerg * 101762b349Sjoerg * Redistribution and use in source and binary forms, with or without 111762b349Sjoerg * modification, are permitted provided that the following conditions 121762b349Sjoerg * are met: 131762b349Sjoerg * 1. Redistributions of source code must retain the above copyright 141762b349Sjoerg * notice, this list of conditions and the following disclaimer. 151762b349Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 161762b349Sjoerg * notice, this list of conditions and the following disclaimer in the 171762b349Sjoerg * documentation and/or other materials provided with the distribution. 181762b349Sjoerg * 191762b349Sjoerg * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 201762b349Sjoerg * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 211762b349Sjoerg * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 221762b349Sjoerg * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 231762b349Sjoerg * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 241762b349Sjoerg * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 251762b349Sjoerg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 261762b349Sjoerg * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 271762b349Sjoerg * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 281762b349Sjoerg * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 291762b349Sjoerg * POSSIBILITY OF SUCH DAMAGE. 301762b349Sjoerg */ 311762b349Sjoerg 321762b349Sjoerg #include <err.h> 331762b349Sjoerg #include <stdio.h> 341762b349Sjoerg #include <stdlib.h> 351762b349Sjoerg #include <string.h> 361762b349Sjoerg #include <unistd.h> 371762b349Sjoerg 381762b349Sjoerg #include <openssl/pkcs7.h> 391762b349Sjoerg #include <openssl/evp.h> 401762b349Sjoerg #include <openssl/x509.h> 411762b349Sjoerg #include <openssl/x509v3.h> 421762b349Sjoerg #include <openssl/pem.h> 431762b349Sjoerg #include <openssl/err.h> 441762b349Sjoerg #include <openssl/ui.h> 451762b349Sjoerg 461762b349Sjoerg static int verbose_flag; 471762b349Sjoerg static unsigned long key_usage = 0; 481762b349Sjoerg 491762b349Sjoerg /* 501762b349Sjoerg * openssl command line equivalents 511762b349Sjoerg * 521762b349Sjoerg * openssl smime -verify \ 531762b349Sjoerg * -inform PEM -in nbsvtool.c.sig -content nbsvtool.c \ 541762b349Sjoerg * -CAfile /secure/lha/su/CA/swupki-pca.crt -out /dev/null 551762b349Sjoerg * openssl smime -sign \ 561762b349Sjoerg * -noattr -binary -outform PEM -out nbsvtool.c.sig \ 571762b349Sjoerg * -in nbsvtool.c -signer /secure/lha/su/CA/lha.crt \ 581762b349Sjoerg * -certfile /secure/lha/su/CA/lha-chain \ 591762b349Sjoerg * -inkey /secure/lha/su/CA/lha.key 601762b349Sjoerg */ 611762b349Sjoerg 621762b349Sjoerg /* 631762b349Sjoerg * Create a detach PEM signature of file `infile' and store it in 641762b349Sjoerg * `outfile'. The signer certificate `cert' and private key 651762b349Sjoerg * `private_key' must be given. An additional hint to the verifier how 661762b349Sjoerg * to find the path from the `cert' to the x509 anchor can be passed 671762b349Sjoerg * in `cert_chain'. 681762b349Sjoerg */ 691762b349Sjoerg 701762b349Sjoerg static void 711762b349Sjoerg sign_file(X509 *cert, EVP_PKEY *private_key, STACK_OF(X509) *cert_chain, 721762b349Sjoerg const char *infile, const char *outfile) 731762b349Sjoerg { 741762b349Sjoerg BIO *out, *in; 751762b349Sjoerg PKCS7 *p7; 761762b349Sjoerg 771762b349Sjoerg out = BIO_new_file(outfile, "w"); 781762b349Sjoerg if (out == NULL) 791762b349Sjoerg err(EXIT_FAILURE, "Failed to open signature output file: %s", 801762b349Sjoerg outfile); 811762b349Sjoerg 821762b349Sjoerg in = BIO_new_file(infile, "r"); 831762b349Sjoerg if (in == NULL) 841762b349Sjoerg err(EXIT_FAILURE, "Failed to input file: %s", infile); 851762b349Sjoerg 861762b349Sjoerg p7 = PKCS7_sign(cert, private_key, cert_chain, in, 871762b349Sjoerg PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY); 881762b349Sjoerg if (p7 == NULL) 891762b349Sjoerg errx(EXIT_FAILURE, "Failed to create signature structure"); 901762b349Sjoerg 911762b349Sjoerg PEM_write_bio_PKCS7(out, p7); 921762b349Sjoerg 931762b349Sjoerg PKCS7_free(p7); 941762b349Sjoerg BIO_free(in); 951762b349Sjoerg BIO_free_all(out); 961762b349Sjoerg } 971762b349Sjoerg 981762b349Sjoerg /* 991762b349Sjoerg * Verifies a detached PEM signature in the file `sigfile' of file 1001762b349Sjoerg * `infile'. The trust anchor file `anchor' to the trust anchors must 1011762b349Sjoerg * be given. If its suspended that the sender didn't inlude the whole 1021762b349Sjoerg * path from the signing certificate to the given trust anchor, extra 1031762b349Sjoerg * certificates can be passed in `cert_chain'. 1041762b349Sjoerg */ 1051762b349Sjoerg 1061762b349Sjoerg static void 1071762b349Sjoerg verify_file(STACK_OF(X509) *cert_chain, const char *anchor, 1081762b349Sjoerg const char *infile, const char *sigfile) 1091762b349Sjoerg { 1101762b349Sjoerg STACK_OF(X509) *signers; 1111762b349Sjoerg X509_STORE *store; 1121762b349Sjoerg BIO *sig, *in; 1131762b349Sjoerg PKCS7 *p7; 1141762b349Sjoerg int ret, i; 115*d40cb843Sjoerg X509_NAME *name; 116*d40cb843Sjoerg char *subject; 1171762b349Sjoerg 1181762b349Sjoerg store = X509_STORE_new(); 1191762b349Sjoerg if (store == NULL) 1201762b349Sjoerg err(1, "Failed to create store"); 1211762b349Sjoerg 1221762b349Sjoerg X509_STORE_load_locations(store, anchor, NULL); 1231762b349Sjoerg 1241762b349Sjoerg in = BIO_new_file(infile, "r"); 1251762b349Sjoerg if (in == NULL) 1261762b349Sjoerg err(EXIT_FAILURE, "Failed to open input data file: %s", infile); 1271762b349Sjoerg 1281762b349Sjoerg sig = BIO_new_file(sigfile, "r"); 1291762b349Sjoerg if (sig == NULL) 1301762b349Sjoerg err(EXIT_FAILURE, "Failed to open signature input file: %s", 1311762b349Sjoerg sigfile); 1321762b349Sjoerg 1331762b349Sjoerg p7 = PEM_read_bio_PKCS7(sig, NULL, NULL, NULL); 1341762b349Sjoerg if (p7 == NULL) 1351762b349Sjoerg errx(EXIT_FAILURE, "Failed to parse the signature file %s", 1361762b349Sjoerg sigfile); 1371762b349Sjoerg 1381762b349Sjoerg ret = PKCS7_verify(p7, cert_chain, store, in, NULL, 0); 1391762b349Sjoerg if (ret != 1) 1401762b349Sjoerg errx(EXIT_FAILURE, "Failed to verify signature"); 1411762b349Sjoerg 1421762b349Sjoerg signers = PKCS7_get0_signers(p7, NULL, 0); 1431762b349Sjoerg if (signers == NULL) 1441762b349Sjoerg errx(EXIT_FAILURE, "Failed to get signers"); 1451762b349Sjoerg 1461762b349Sjoerg if (sk_X509_num(signers) == 0) 1471762b349Sjoerg errx(EXIT_FAILURE, "No signers ?"); 1481762b349Sjoerg 149*d40cb843Sjoerg if (key_usage != 0) { 150*d40cb843Sjoerg for (i = 0; i < sk_X509_num(signers); i++) { 151*d40cb843Sjoerg if ((sk_X509_value(signers, i)->ex_xkusage & key_usage) 152*d40cb843Sjoerg == key_usage) 153*d40cb843Sjoerg continue; 154*d40cb843Sjoerg name = X509_get_subject_name(sk_X509_value(signers, i)); 155*d40cb843Sjoerg subject = X509_NAME_oneline(name, NULL, 0); 156*d40cb843Sjoerg errx(EXIT_FAILURE, 157*d40cb843Sjoerg "Certificate doesn't match required key usage: %s", 158*d40cb843Sjoerg subject); 159*d40cb843Sjoerg } 160*d40cb843Sjoerg } 161*d40cb843Sjoerg 1621762b349Sjoerg if (verbose_flag) 1631762b349Sjoerg printf("Sigature ok, signed by:\n"); 1641762b349Sjoerg 1651762b349Sjoerg for (i = 0; i < sk_X509_num(signers); i++) { 1661762b349Sjoerg name = X509_get_subject_name(sk_X509_value(signers, i)); 1671762b349Sjoerg subject = X509_NAME_oneline(name, NULL, 0); 1681762b349Sjoerg 1691762b349Sjoerg if (verbose_flag) 1701762b349Sjoerg printf("\t%s\n", subject); 1711762b349Sjoerg 1721762b349Sjoerg OPENSSL_free(subject); 1731762b349Sjoerg } 1741762b349Sjoerg 1751762b349Sjoerg PKCS7_free(p7); 1761762b349Sjoerg BIO_free(in); 1771762b349Sjoerg BIO_free(sig); 1781762b349Sjoerg } 1791762b349Sjoerg 1801762b349Sjoerg /* 1811762b349Sjoerg * Parse and return a list PEM encoded certificates in the file 1821762b349Sjoerg * `file'. In case of error or an empty file, and error text will be 1831762b349Sjoerg * printed and the function will exit(3). 1841762b349Sjoerg */ 1851762b349Sjoerg 1861762b349Sjoerg static STACK_OF(X509) * 1871762b349Sjoerg file_to_certs(const char *file) 1881762b349Sjoerg { 1891762b349Sjoerg STACK_OF(X509) *certs; 1901762b349Sjoerg FILE *f; 1911762b349Sjoerg 1921762b349Sjoerg f = fopen(file, "r"); 1931762b349Sjoerg if (f == NULL) 1941762b349Sjoerg err(EXIT_FAILURE, "Cannot open certificate file %s", file); 1951762b349Sjoerg certs = sk_X509_new_null(); 1961762b349Sjoerg while (1) { 1971762b349Sjoerg X509 *cert; 1981762b349Sjoerg 1991762b349Sjoerg cert = PEM_read_X509(f, NULL, NULL, NULL); 2001762b349Sjoerg if (cert == NULL) { 2011762b349Sjoerg unsigned long ret; 2021762b349Sjoerg 2031762b349Sjoerg ret = ERR_GET_REASON(ERR_peek_error()); 2041762b349Sjoerg if (ret == PEM_R_NO_START_LINE) { 2051762b349Sjoerg /* End of file reached. no error */ 2061762b349Sjoerg ERR_clear_error(); 2071762b349Sjoerg break; 2081762b349Sjoerg } 2091762b349Sjoerg errx(EXIT_FAILURE, "Can't read certificate file %s", 2101762b349Sjoerg file); 2111762b349Sjoerg } 2121762b349Sjoerg sk_X509_insert(certs, cert, sk_X509_num(certs)); 2131762b349Sjoerg } 2141762b349Sjoerg fclose(f); 2151762b349Sjoerg if (sk_X509_num(certs) == 0) 2161762b349Sjoerg errx(EXIT_FAILURE, "No certificate found file %s", file); 2171762b349Sjoerg 2181762b349Sjoerg return certs; 2191762b349Sjoerg } 2201762b349Sjoerg 2211762b349Sjoerg static int 2221762b349Sjoerg ssl_pass_cb(char *buf, int size, int rwflag, void *u) 2231762b349Sjoerg { 2241762b349Sjoerg 2251762b349Sjoerg if (UI_UTIL_read_pw_string(buf, size, "Passphrase: ", 0)) 2261762b349Sjoerg return 0; 2271762b349Sjoerg return strlen(buf); 2281762b349Sjoerg } 2291762b349Sjoerg 2301762b349Sjoerg static struct { 2311762b349Sjoerg X509 *certificate; 2321762b349Sjoerg STACK_OF(X509) *cert_chain; 2331762b349Sjoerg EVP_PKEY *private_key; 2341762b349Sjoerg } crypto_state; 2351762b349Sjoerg 2361762b349Sjoerg /* 2371762b349Sjoerg * Load the certificate file `cert_file' with the associated private 2381762b349Sjoerg * key file `key_file'. The private key is checked to make sure it 2391762b349Sjoerg * matches the certificate. The optional hints for the path to the CA 2401762b349Sjoerg * is stored in `chain_file'. 2411762b349Sjoerg */ 2421762b349Sjoerg 2431762b349Sjoerg static void 2441762b349Sjoerg load_keys(const char *cert_file, const char *chain_file, const char *key_file) 2451762b349Sjoerg { 2461762b349Sjoerg STACK_OF(X509) *c; 2471762b349Sjoerg FILE *f; 2481762b349Sjoerg int ret; 2491762b349Sjoerg 2501762b349Sjoerg if (cert_file == NULL) 2511762b349Sjoerg errx(EXIT_FAILURE, "No certificate file given"); 2521762b349Sjoerg if (key_file == NULL) 2531762b349Sjoerg errx(EXIT_FAILURE, "No private key file given"); 2541762b349Sjoerg 2551762b349Sjoerg c = file_to_certs(cert_file); 2561762b349Sjoerg 2571762b349Sjoerg if (sk_X509_num(c) != 1) 2581762b349Sjoerg errx(EXIT_FAILURE, 2591762b349Sjoerg "More then one certificate in the certificate file"); 2601762b349Sjoerg crypto_state.certificate = sk_X509_value(c, 0); 2611762b349Sjoerg 2621762b349Sjoerg if (chain_file) 2631762b349Sjoerg crypto_state.cert_chain = file_to_certs(chain_file); 2641762b349Sjoerg 2651762b349Sjoerg /* load private key */ 2661762b349Sjoerg f = fopen(key_file, "r"); 2671762b349Sjoerg if (f == NULL) 2681762b349Sjoerg errx(1, "Failed to open private key file %s", key_file); 2691762b349Sjoerg 2701762b349Sjoerg crypto_state.private_key = 2711762b349Sjoerg PEM_read_PrivateKey(f, NULL, ssl_pass_cb, NULL); 2721762b349Sjoerg fclose(f); 2731762b349Sjoerg if (crypto_state.private_key == NULL) 2741762b349Sjoerg errx(EXIT_FAILURE, "Can't read private key %s", key_file); 2751762b349Sjoerg 2761762b349Sjoerg ret = X509_check_private_key(crypto_state.certificate, 2771762b349Sjoerg crypto_state.private_key); 2781762b349Sjoerg if (ret != 1) 2791762b349Sjoerg errx(EXIT_FAILURE, 2801762b349Sjoerg "The private key %s doesn't match the certificate %s", 2811762b349Sjoerg key_file, cert_file); 2821762b349Sjoerg } 2831762b349Sjoerg 2841762b349Sjoerg static void __dead 2851762b349Sjoerg usage(int exit_code) 2861762b349Sjoerg { 2871762b349Sjoerg 2881762b349Sjoerg printf("%s usage\n", getprogname()); 2891762b349Sjoerg printf("%s -k keyfile -c cert-chain [-f cert-chain] sign file\n", 2901762b349Sjoerg getprogname()); 2911762b349Sjoerg printf("%s [-u code|...] [-a x509-anchor-file] verify filename.sp7\n", 2921762b349Sjoerg getprogname()); 2931762b349Sjoerg printf("%s [-u code|...] [-a x509-anchor-file] verify filename otherfilename.sp7\n", 2941762b349Sjoerg getprogname()); 2951762b349Sjoerg printf("%s [-u code|...] [-a x509-anchor-file] verify-code file ...\n", 2961762b349Sjoerg getprogname()); 2971762b349Sjoerg exit(exit_code); 2981762b349Sjoerg } 2991762b349Sjoerg 3001762b349Sjoerg int 3011762b349Sjoerg main(int argc, char **argv) 3021762b349Sjoerg { 3031762b349Sjoerg const char *anchors = NULL; 3041762b349Sjoerg const char *cert_file = NULL, *key_file = NULL, *chain_file = NULL; 3051762b349Sjoerg const char *file; 3061762b349Sjoerg char *sigfile; 3071762b349Sjoerg int ch; 3081762b349Sjoerg 3091762b349Sjoerg setprogname(argv[0]); 3101762b349Sjoerg 3111762b349Sjoerg OpenSSL_add_all_algorithms(); 3121762b349Sjoerg ERR_load_crypto_strings(); 3131762b349Sjoerg 3141762b349Sjoerg while ((ch = getopt(argc, argv, "a:c:f:hk:u:v")) != -1) { 3151762b349Sjoerg switch (ch) { 3161762b349Sjoerg case 'a': 3171762b349Sjoerg anchors = optarg; 3181762b349Sjoerg break; 3191762b349Sjoerg case 'f': 3201762b349Sjoerg chain_file = optarg; 3211762b349Sjoerg break; 3221762b349Sjoerg case 'k': 3231762b349Sjoerg key_file = optarg; 3241762b349Sjoerg break; 3251762b349Sjoerg case 'c': 3261762b349Sjoerg cert_file = optarg; 3271762b349Sjoerg break; 3281762b349Sjoerg case 'u': 3291762b349Sjoerg if (strcmp("ssl-server", optarg) == 0) 3301762b349Sjoerg key_usage |= XKU_SSL_SERVER; 3311762b349Sjoerg else if (strcmp("ssl-client", optarg) == 0) 3321762b349Sjoerg key_usage |= XKU_SSL_CLIENT; 3331762b349Sjoerg else if (strcmp("code", optarg) == 0) 3341762b349Sjoerg key_usage |= XKU_CODE_SIGN; 3351762b349Sjoerg else if (strcmp("smime", optarg) == 0) 3361762b349Sjoerg key_usage |= XKU_SMIME; 3371762b349Sjoerg else 3381762b349Sjoerg errx(1, "Unknown keyusage: %s", optarg); 3391762b349Sjoerg break; 3401762b349Sjoerg case 'v': 3411762b349Sjoerg verbose_flag = 1; 3421762b349Sjoerg break; 3431762b349Sjoerg case 'h': 3441762b349Sjoerg usage(EXIT_SUCCESS); 3451762b349Sjoerg default: 3461762b349Sjoerg usage(EXIT_FAILURE); 3471762b349Sjoerg } 3481762b349Sjoerg } 3491762b349Sjoerg 3501762b349Sjoerg argc -= optind; 3511762b349Sjoerg argv += optind; 3521762b349Sjoerg 3531762b349Sjoerg if (argc < 1) { 3541762b349Sjoerg fprintf(stderr, "Command missing [sign|verify]\n"); 3551762b349Sjoerg usage(EXIT_FAILURE); 3561762b349Sjoerg } 3571762b349Sjoerg 3581762b349Sjoerg if (strcmp(argv[0], "sign") == 0) { 3591762b349Sjoerg 3601762b349Sjoerg if (argc < 2) 3611762b349Sjoerg usage(1); 3621762b349Sjoerg 3631762b349Sjoerg file = argv[1]; 3641762b349Sjoerg 3651762b349Sjoerg asprintf(&sigfile, "%s.sp7", file); 3661762b349Sjoerg if (sigfile == NULL) 3671762b349Sjoerg err(EXIT_FAILURE, "asprintf failed"); 3681762b349Sjoerg 3691762b349Sjoerg load_keys(cert_file, chain_file, key_file); 3701762b349Sjoerg 3711762b349Sjoerg sign_file(crypto_state.certificate, 3721762b349Sjoerg crypto_state.private_key, 3731762b349Sjoerg crypto_state.cert_chain, 3741762b349Sjoerg file, 3751762b349Sjoerg sigfile); 3761762b349Sjoerg 3771762b349Sjoerg } else if (strcmp(argv[0], "verify") == 0 3781762b349Sjoerg || strcmp(argv[0], "verify-code") == 0) { 3791762b349Sjoerg 3801762b349Sjoerg if (strcmp(argv[0], "verify-code") == 0) 3811762b349Sjoerg key_usage |= XKU_CODE_SIGN; 3821762b349Sjoerg 3831762b349Sjoerg if (argc < 2) 3841762b349Sjoerg usage(1); 3851762b349Sjoerg else if (argc < 3) { 3861762b349Sjoerg char *dot; 3871762b349Sjoerg 3881762b349Sjoerg sigfile = argv[1]; 3891762b349Sjoerg 3901762b349Sjoerg file = strdup(sigfile); 3911762b349Sjoerg if (file == NULL) 3921762b349Sjoerg err(1, "strdup failed"); 3931762b349Sjoerg 3941762b349Sjoerg dot = strrchr(file, '.'); 3951762b349Sjoerg if (dot == NULL || strchr(dot, '/') != NULL) 3961762b349Sjoerg errx(EXIT_FAILURE, 3971762b349Sjoerg "File name missing suffix"); 3981762b349Sjoerg if (strcmp(".sp7", dot) != 0) 3991762b349Sjoerg errx(EXIT_FAILURE, 4001762b349Sjoerg "File name bad suffix (%s)", dot); 4011762b349Sjoerg *dot = '\0'; 4021762b349Sjoerg } else { 4031762b349Sjoerg file = argv[1]; 4041762b349Sjoerg sigfile = argv[2]; 4051762b349Sjoerg } 4061762b349Sjoerg verify_file(crypto_state.cert_chain, anchors, file, sigfile); 4071762b349Sjoerg } else { 4081762b349Sjoerg fprintf(stderr, "Unknown command: %s\n", argv[0]); 4091762b349Sjoerg usage(EXIT_FAILURE); 4101762b349Sjoerg } 4111762b349Sjoerg 4121762b349Sjoerg return 0; 4131762b349Sjoerg } 414