1 /* $NetBSD: nbsvtool.c,v 1.1 2008/05/11 17:58:09 joerg Exp $ */
4 * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Love Hörnquist Åstrand <lha@it.su.se>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
38 #include <openssl/pkcs7.h>
39 #include <openssl/evp.h>
40 #include <openssl/x509.h>
41 #include <openssl/x509v3.h>
42 #include <openssl/pem.h>
43 #include <openssl/err.h>
44 #include <openssl/ui.h>
46 static int verbose_flag
;
47 static unsigned long key_usage
= 0;
50 * openssl command line equivalents
52 * openssl smime -verify \
53 * -inform PEM -in nbsvtool.c.sig -content nbsvtool.c \
54 * -CAfile /secure/lha/su/CA/swupki-pca.crt -out /dev/null
55 * openssl smime -sign \
56 * -noattr -binary -outform PEM -out nbsvtool.c.sig \
57 * -in nbsvtool.c -signer /secure/lha/su/CA/lha.crt \
58 * -certfile /secure/lha/su/CA/lha-chain \
59 * -inkey /secure/lha/su/CA/lha.key
63 * Create a detach PEM signature of file `infile' and store it in
64 * `outfile'. The signer certificate `cert' and private key
65 * `private_key' must be given. An additional hint to the verifier how
66 * to find the path from the `cert' to the x509 anchor can be passed
71 sign_file(X509
*cert
, EVP_PKEY
*private_key
, STACK_OF(X509
) *cert_chain
,
72 const char *infile
, const char *outfile
)
77 out
= BIO_new_file(outfile
, "w");
79 err(EXIT_FAILURE
, "Failed to open signature output file: %s",
82 in
= BIO_new_file(infile
, "r");
84 err(EXIT_FAILURE
, "Failed to input file: %s", infile
);
86 p7
= PKCS7_sign(cert
, private_key
, cert_chain
, in
,
87 PKCS7_DETACHED
|PKCS7_NOATTR
|PKCS7_BINARY
);
89 errx(EXIT_FAILURE
, "Failed to create signature structure");
91 PEM_write_bio_PKCS7(out
, p7
);
99 * Verifies a detached PEM signature in the file `sigfile' of file
100 * `infile'. The trust anchor file `anchor' to the trust anchors must
101 * be given. If its suspended that the sender didn't inlude the whole
102 * path from the signing certificate to the given trust anchor, extra
103 * certificates can be passed in `cert_chain'.
107 verify_file(STACK_OF(X509
) *cert_chain
, const char *anchor
,
108 const char *infile
, const char *sigfile
)
110 STACK_OF(X509
) *signers
;
118 store
= X509_STORE_new();
120 err(1, "Failed to create store");
122 X509_STORE_load_locations(store
, anchor
, NULL
);
124 in
= BIO_new_file(infile
, "r");
126 err(EXIT_FAILURE
, "Failed to open input data file: %s", infile
);
128 sig
= BIO_new_file(sigfile
, "r");
130 err(EXIT_FAILURE
, "Failed to open signature input file: %s",
133 p7
= PEM_read_bio_PKCS7(sig
, NULL
, NULL
, NULL
);
135 errx(EXIT_FAILURE
, "Failed to parse the signature file %s",
138 ret
= PKCS7_verify(p7
, cert_chain
, store
, in
, NULL
, 0);
140 errx(EXIT_FAILURE
, "Failed to verify signature");
142 signers
= PKCS7_get0_signers(p7
, NULL
, 0);
144 errx(EXIT_FAILURE
, "Failed to get signers");
146 if (sk_X509_num(signers
) == 0)
147 errx(EXIT_FAILURE
, "No signers ?");
149 if (key_usage
!= 0) {
150 for (i
= 0; i
< sk_X509_num(signers
); i
++) {
151 if ((sk_X509_value(signers
, i
)->ex_xkusage
& key_usage
)
154 name
= X509_get_subject_name(sk_X509_value(signers
, i
));
155 subject
= X509_NAME_oneline(name
, NULL
, 0);
157 "Certificate doesn't match required key usage: %s",
163 printf("Sigature ok, signed by:\n");
165 for (i
= 0; i
< sk_X509_num(signers
); i
++) {
166 name
= X509_get_subject_name(sk_X509_value(signers
, i
));
167 subject
= X509_NAME_oneline(name
, NULL
, 0);
170 printf("\t%s\n", subject
);
172 OPENSSL_free(subject
);
181 * Parse and return a list PEM encoded certificates in the file
182 * `file'. In case of error or an empty file, and error text will be
183 * printed and the function will exit(3).
186 static STACK_OF(X509
) *
187 file_to_certs(const char *file
)
189 STACK_OF(X509
) *certs
;
192 f
= fopen(file
, "r");
194 err(EXIT_FAILURE
, "Cannot open certificate file %s", file
);
195 certs
= sk_X509_new_null();
199 cert
= PEM_read_X509(f
, NULL
, NULL
, NULL
);
203 ret
= ERR_GET_REASON(ERR_peek_error());
204 if (ret
== PEM_R_NO_START_LINE
) {
205 /* End of file reached. no error */
209 errx(EXIT_FAILURE
, "Can't read certificate file %s",
212 sk_X509_insert(certs
, cert
, sk_X509_num(certs
));
215 if (sk_X509_num(certs
) == 0)
216 errx(EXIT_FAILURE
, "No certificate found file %s", file
);
222 ssl_pass_cb(char *buf
, int size
, int rwflag
, void *u
)
225 if (UI_UTIL_read_pw_string(buf
, size
, "Passphrase: ", 0))
232 STACK_OF(X509
) *cert_chain
;
233 EVP_PKEY
*private_key
;
237 * Load the certificate file `cert_file' with the associated private
238 * key file `key_file'. The private key is checked to make sure it
239 * matches the certificate. The optional hints for the path to the CA
240 * is stored in `chain_file'.
244 load_keys(const char *cert_file
, const char *chain_file
, const char *key_file
)
250 if (cert_file
== NULL
)
251 errx(EXIT_FAILURE
, "No certificate file given");
252 if (key_file
== NULL
)
253 errx(EXIT_FAILURE
, "No private key file given");
255 c
= file_to_certs(cert_file
);
257 if (sk_X509_num(c
) != 1)
259 "More then one certificate in the certificate file");
260 crypto_state
.certificate
= sk_X509_value(c
, 0);
263 crypto_state
.cert_chain
= file_to_certs(chain_file
);
265 /* load private key */
266 f
= fopen(key_file
, "r");
268 errx(1, "Failed to open private key file %s", key_file
);
270 crypto_state
.private_key
=
271 PEM_read_PrivateKey(f
, NULL
, ssl_pass_cb
, NULL
);
273 if (crypto_state
.private_key
== NULL
)
274 errx(EXIT_FAILURE
, "Can't read private key %s", key_file
);
276 ret
= X509_check_private_key(crypto_state
.certificate
,
277 crypto_state
.private_key
);
280 "The private key %s doesn't match the certificate %s",
281 key_file
, cert_file
);
288 printf("%s usage\n", getprogname());
289 printf("%s -k keyfile -c cert-chain [-f cert-chain] sign file\n",
291 printf("%s [-u code|...] [-a x509-anchor-file] verify filename.sp7\n",
293 printf("%s [-u code|...] [-a x509-anchor-file] verify filename otherfilename.sp7\n",
295 printf("%s [-u code|...] [-a x509-anchor-file] verify-code file ...\n",
301 main(int argc
, char **argv
)
303 const char *anchors
= NULL
;
304 const char *cert_file
= NULL
, *key_file
= NULL
, *chain_file
= NULL
;
309 setprogname(argv
[0]);
311 OpenSSL_add_all_algorithms();
312 ERR_load_crypto_strings();
314 while ((ch
= getopt(argc
, argv
, "a:c:f:hk:u:v")) != -1) {
329 if (strcmp("ssl-server", optarg
) == 0)
330 key_usage
|= XKU_SSL_SERVER
;
331 else if (strcmp("ssl-client", optarg
) == 0)
332 key_usage
|= XKU_SSL_CLIENT
;
333 else if (strcmp("code", optarg
) == 0)
334 key_usage
|= XKU_CODE_SIGN
;
335 else if (strcmp("smime", optarg
) == 0)
336 key_usage
|= XKU_SMIME
;
338 errx(1, "Unknown keyusage: %s", optarg
);
354 fprintf(stderr
, "Command missing [sign|verify]\n");
358 if (strcmp(argv
[0], "sign") == 0) {
365 asprintf(&sigfile
, "%s.sp7", file
);
367 err(EXIT_FAILURE
, "asprintf failed");
369 load_keys(cert_file
, chain_file
, key_file
);
371 sign_file(crypto_state
.certificate
,
372 crypto_state
.private_key
,
373 crypto_state
.cert_chain
,
377 } else if (strcmp(argv
[0], "verify") == 0
378 || strcmp(argv
[0], "verify-code") == 0) {
380 if (strcmp(argv
[0], "verify-code") == 0)
381 key_usage
|= XKU_CODE_SIGN
;
390 file
= strdup(sigfile
);
392 err(1, "strdup failed");
394 dot
= strrchr(file
, '.');
395 if (dot
== NULL
|| strchr(dot
, '/') != NULL
)
397 "File name missing suffix");
398 if (strcmp(".sp7", dot
) != 0)
400 "File name bad suffix (%s)", dot
);
406 verify_file(crypto_state
.cert_chain
, anchors
, file
, sigfile
);
408 fprintf(stderr
, "Unknown command: %s\n", argv
[0]);