pkcs7.c revision 1.1
1/* $NetBSD: pkcs7.c,v 1.1 2009/02/02 20:44:07 joerg Exp $ */ 2#if HAVE_CONFIG_H 3#include "config.h" 4#endif 5#include <nbcompat.h> 6#if HAVE_SYS_CDEFS_H 7#include <sys/cdefs.h> 8#endif 9 10__RCSID("$NetBSD: pkcs7.c,v 1.1 2009/02/02 20:44:07 joerg Exp $"); 11 12/*- 13 * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc. 14 * All rights reserved. 15 * 16 * This code is derived from software contributed to The NetBSD Foundation 17 * by Love H�rnquist �strand <lha@it.su.se> 18 * 19 * Redistribution and use in source and binary forms, with or without 20 * modification, are permitted provided that the following conditions 21 * are met: 22 * 1. Redistributions of source code must retain the above copyright 23 * notice, this list of conditions and the following disclaimer. 24 * 2. Redistributions in binary form must reproduce the above copyright 25 * notice, this list of conditions and the following disclaimer in the 26 * documentation and/or other materials provided with the distribution. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 */ 40 41#if HAVE_ERR_H 42#include <err.h> 43#endif 44 45#include <openssl/pkcs7.h> 46#include <openssl/evp.h> 47#include <openssl/x509.h> 48#include <openssl/x509v3.h> 49#include <openssl/pem.h> 50#include <openssl/err.h> 51#include <openssl/ui.h> 52 53#include "lib.h" 54 55#ifndef __UNCONST 56#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) 57#endif 58 59static const int pkg_key_usage = XKU_CODE_SIGN | XKU_SMIME; 60 61static int 62check_ca(X509 *cert) 63{ 64 if ((cert->ex_flags & EXFLAG_KUSAGE) != 0 && 65 (cert->ex_kusage & KU_KEY_CERT_SIGN) != KU_KEY_CERT_SIGN) 66 return 0; 67 if ((cert->ex_flags & EXFLAG_BCONS) != 0) 68 return (cert->ex_flags & EXFLAG_CA) == EXFLAG_CA; 69 if ((cert->ex_flags & (EXFLAG_V1|EXFLAG_SS)) == (EXFLAG_V1|EXFLAG_SS)) 70 return 1; 71 if ((cert->ex_flags & EXFLAG_KUSAGE) != 0) 72 return 1; 73 if ((cert->ex_flags & EXFLAG_NSCERT) != 0 && 74 (cert->ex_nscert & NS_ANY_CA) != 0) 75 return 1; 76 return 0; 77} 78 79static STACK_OF(X509) * 80file_to_certs(const char *file) 81{ 82 unsigned long ret; 83 STACK_OF(X509) *certs; 84 FILE *f; 85 86 if ((f = fopen(file, "r")) == NULL) { 87 warn("open failed %s", file); 88 return NULL; 89 } 90 91 certs = sk_X509_new_null(); 92 for (;;) { 93 X509 *cert; 94 95 cert = PEM_read_X509(f, NULL, NULL, NULL); 96 if (cert == NULL) { 97 ret = ERR_GET_REASON(ERR_peek_error()); 98 if (ret == PEM_R_NO_START_LINE) { 99 /* End of file reached. no error */ 100 ERR_clear_error(); 101 break; 102 } 103 sk_X509_free(certs); 104 warnx("Can't read certificate in file: %s", file); 105 return NULL; 106 } 107 sk_X509_insert(certs, cert, sk_X509_num(certs)); 108 } 109 110 fclose(f); 111 112 if (sk_X509_num(certs) == 0) { 113 sk_X509_free(certs); 114 certs = NULL; 115 warnx("No certificate found in file %s", file); 116 } 117 118 return certs; 119} 120 121int 122easy_pkcs7_verify(const char *content, size_t len, 123 const char *signature, size_t signature_len, 124 const char *anchor, int is_pkg) 125{ 126 STACK_OF(X509) *cert_chain, *signers; 127 X509_STORE *store; 128 BIO *sig, *in; 129 PKCS7 *p7; 130 int i, status; 131 X509_NAME *name; 132 char *subject; 133 134 OpenSSL_add_all_algorithms(); 135 ERR_load_crypto_strings(); 136 137 status = -1; 138 139 if (cert_chain_file) 140 cert_chain = file_to_certs(cert_chain_file); 141 else 142 cert_chain = NULL; 143 144 store = X509_STORE_new(); 145 if (store == NULL) { 146 sk_X509_free(cert_chain); 147 warnx("Failed to create certificate store"); 148 return -1; 149 } 150 151 X509_STORE_load_locations(store, anchor, NULL); 152 153 in = BIO_new_mem_buf(__UNCONST(content), len); 154 sig = BIO_new_mem_buf(__UNCONST(signature), signature_len); 155 signers = NULL; 156 157 p7 = PEM_read_bio_PKCS7(sig, NULL, NULL, NULL); 158 if (p7 == NULL) { 159 warnx("Failed to parse the signature"); 160 goto cleanup; 161 } 162 163 if (PKCS7_verify(p7, cert_chain, store, in, NULL, 0) != 1) { 164 warnx("Failed to verify signature"); 165 goto cleanup; 166 } 167 168 signers = PKCS7_get0_signers(p7, NULL, 0); 169 if (signers == NULL) { 170 warnx("Failed to get signers"); 171 goto cleanup; 172 } 173 174 if (sk_X509_num(signers) == 0) { 175 warnx("No signers found"); 176 goto cleanup; 177 } 178 179 for (i = 0; i < sk_X509_num(signers); i++) { 180 /* Compute ex_xkusage */ 181 X509_check_purpose(sk_X509_value(signers, i), -1, -1); 182 183 if (check_ca(sk_X509_value(signers, i))) { 184 warnx("CA keys are not valid for signatures"); 185 goto cleanup; 186 } 187 if (is_pkg) { 188 if (sk_X509_value(signers, i)->ex_xkusage != pkg_key_usage) { 189 warnx("Certificate must have CODE SIGNING " 190 "and EMAIL PROTECTION property"); 191 goto cleanup; 192 } 193 } else { 194 if (sk_X509_value(signers, i)->ex_xkusage != 0) { 195 warnx("Certificate must not have any property"); 196 goto cleanup; 197 } 198 } 199 } 200 201 printf("Sigature ok, signed by:\n"); 202 203 for (i = 0; i < sk_X509_num(signers); i++) { 204 name = X509_get_subject_name(sk_X509_value(signers, i)); 205 subject = X509_NAME_oneline(name, NULL, 0); 206 207 printf("\t%s\n", subject); 208 209 OPENSSL_free(subject); 210 } 211 212 status = 0; 213 214cleanup: 215 sk_X509_free(cert_chain); 216 sk_X509_free(signers); 217 X509_STORE_free(store); 218 219 PKCS7_free(p7); 220 BIO_free(in); 221 BIO_free(sig); 222 223 return status; 224} 225 226static int 227ssl_pass_cb(char *buf, int size, int rwflag, void *u) 228{ 229 230 if (UI_UTIL_read_pw_string(buf, size, "Passphrase: ", 0)) 231 return 0; 232 return strlen(buf); 233} 234 235int 236easy_pkcs7_sign(const char *content, size_t len, 237 char **signature, size_t *signature_len, 238 const char *key_file, const char *cert_file) 239{ 240 FILE *f; 241 X509 *certificate; 242 STACK_OF(X509) *c, *cert_chain; 243 EVP_PKEY *private_key; 244 char *tmp_sig; 245 BIO *out, *in; 246 PKCS7 *p7; 247 int status; 248 249 OpenSSL_add_all_algorithms(); 250 ERR_load_crypto_strings(); 251 252 status = -1; 253 private_key = NULL; 254 cert_chain = NULL; 255 in = NULL; 256 257 c = file_to_certs(cert_file); 258 259 if (sk_X509_num(c) != 1) { 260 warnx("More then one certificate in the certificate file"); 261 goto cleanup; 262 } 263 certificate = sk_X509_value(c, 0); 264 265 /* Compute ex_kusage */ 266 X509_check_purpose(certificate, -1, 0); 267 268 if (check_ca(certificate)) { 269 warnx("CA keys are not valid for signatures"); 270 goto cleanup; 271 } 272 273 if (certificate->ex_xkusage != pkg_key_usage) { 274 warnx("Certificate must have CODE SIGNING " 275 "and EMAIL PROTECTION property"); 276 goto cleanup; 277 } 278 279 if (cert_chain_file) 280 cert_chain = file_to_certs(cert_chain_file); 281 282 if ((f = fopen(key_file, "r")) == NULL) { 283 warn("Failed to open private key file %s", key_file); 284 goto cleanup; 285 } 286 private_key = PEM_read_PrivateKey(f, NULL, ssl_pass_cb, NULL); 287 fclose(f); 288 if (private_key == NULL) { 289 warnx("Can't read private key: %s", key_file); 290 goto cleanup; 291 } 292 293 if (X509_check_private_key(certificate, private_key) != 1) { 294 warnx("The private key %s doesn't match the certificate %s", 295 key_file, cert_file); 296 goto cleanup; 297 } 298 299 in = BIO_new_mem_buf(__UNCONST(content), len); 300 301 p7 = PKCS7_sign(certificate, private_key, cert_chain, in, 302 PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY); 303 if (p7 == NULL) { 304 warnx("Failed to create signature structure"); 305 goto cleanup; 306 } 307 308 out = BIO_new(BIO_s_mem()); 309 PEM_write_bio_PKCS7(out, p7); 310 *signature_len = BIO_get_mem_data(out, &tmp_sig); 311 *signature = xmalloc(*signature_len); 312 memcpy(*signature, tmp_sig, *signature_len); 313 BIO_free_all(out); 314 315 PKCS7_free(p7); 316 317 status = 0; 318 319cleanup: 320 sk_X509_free(c); 321 sk_X509_free(cert_chain); 322 EVP_PKEY_free(private_key); 323 BIO_free(in); 324 325 return status; 326} 327