1 /* $NetBSD: pkcs7.c,v 1.1.1.4 2009/08/06 16:55:27 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.1.4 2009/08/06 16:55:27 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 52 #include "lib.h" 53 54 #ifndef NS_ANY_CA 55 #define NS_ANY_CA (NS_SSL_CA|NS_SMIME_CA|NS_OBJSIGN_CA) 56 #endif 57 58 static const unsigned int pkg_key_usage = XKU_CODE_SIGN | XKU_SMIME; 59 60 static int 61 check_ca(X509 *cert) 62 { 63 if ((cert->ex_flags & EXFLAG_KUSAGE) != 0 && 64 (cert->ex_kusage & KU_KEY_CERT_SIGN) != KU_KEY_CERT_SIGN) 65 return 0; 66 if ((cert->ex_flags & EXFLAG_BCONS) != 0) 67 return (cert->ex_flags & EXFLAG_CA) == EXFLAG_CA; 68 if ((cert->ex_flags & (EXFLAG_V1|EXFLAG_SS)) == (EXFLAG_V1|EXFLAG_SS)) 69 return 1; 70 if ((cert->ex_flags & EXFLAG_KUSAGE) != 0) 71 return 1; 72 if ((cert->ex_flags & EXFLAG_NSCERT) != 0 && 73 (cert->ex_nscert & NS_ANY_CA) != 0) 74 return 1; 75 return 0; 76 } 77 78 static STACK_OF(X509) * 79 file_to_certs(const char *file) 80 { 81 unsigned long ret; 82 STACK_OF(X509) *certs; 83 FILE *f; 84 85 if ((f = fopen(file, "r")) == NULL) { 86 warn("open failed %s", file); 87 return NULL; 88 } 89 90 certs = sk_X509_new_null(); 91 for (;;) { 92 X509 *cert; 93 94 cert = PEM_read_X509(f, NULL, NULL, NULL); 95 if (cert == NULL) { 96 ret = ERR_GET_REASON(ERR_peek_error()); 97 if (ret == PEM_R_NO_START_LINE) { 98 /* End of file reached. no error */ 99 ERR_clear_error(); 100 break; 101 } 102 sk_X509_free(certs); 103 warnx("Can't read certificate in file: %s", file); 104 fclose(f); 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 121 int 122 easy_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 214 cleanup: 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 226 static int 227 ssl_pass_cb(char *buf, int size, int rwflag, void *u) 228 { 229 230 if (EVP_read_pw_string(buf, size, "Passphrase :", 0)) { 231 #if OPENSSL_VERSION >= 0x0090608fL 232 OPENSSL_cleanse(buf, size); 233 #else 234 memset(buf, 0, size); 235 #endif 236 return 0; 237 } 238 return strlen(buf); 239 } 240 241 int 242 easy_pkcs7_sign(const char *content, size_t len, 243 char **signature, size_t *signature_len, 244 const char *key_file, const char *cert_file) 245 { 246 FILE *f; 247 X509 *certificate; 248 STACK_OF(X509) *c, *cert_chain; 249 EVP_PKEY *private_key; 250 char *tmp_sig; 251 BIO *out, *in; 252 PKCS7 *p7; 253 int status; 254 255 OpenSSL_add_all_algorithms(); 256 ERR_load_crypto_strings(); 257 258 status = -1; 259 private_key = NULL; 260 cert_chain = NULL; 261 in = NULL; 262 263 c = file_to_certs(cert_file); 264 265 if (sk_X509_num(c) != 1) { 266 warnx("More then one certificate in the certificate file"); 267 goto cleanup; 268 } 269 certificate = sk_X509_value(c, 0); 270 271 /* Compute ex_kusage */ 272 X509_check_purpose(certificate, -1, 0); 273 274 if (check_ca(certificate)) { 275 warnx("CA keys are not valid for signatures"); 276 goto cleanup; 277 } 278 279 if (certificate->ex_xkusage != pkg_key_usage) { 280 warnx("Certificate must have CODE SIGNING " 281 "and EMAIL PROTECTION property"); 282 goto cleanup; 283 } 284 285 if (cert_chain_file) 286 cert_chain = file_to_certs(cert_chain_file); 287 288 if ((f = fopen(key_file, "r")) == NULL) { 289 warn("Failed to open private key file %s", key_file); 290 goto cleanup; 291 } 292 private_key = PEM_read_PrivateKey(f, NULL, ssl_pass_cb, NULL); 293 fclose(f); 294 if (private_key == NULL) { 295 warnx("Can't read private key: %s", key_file); 296 goto cleanup; 297 } 298 299 if (X509_check_private_key(certificate, private_key) != 1) { 300 warnx("The private key %s doesn't match the certificate %s", 301 key_file, cert_file); 302 goto cleanup; 303 } 304 305 in = BIO_new_mem_buf(__UNCONST(content), len); 306 307 p7 = PKCS7_sign(certificate, private_key, cert_chain, in, 308 PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY); 309 if (p7 == NULL) { 310 warnx("Failed to create signature structure"); 311 goto cleanup; 312 } 313 314 out = BIO_new(BIO_s_mem()); 315 PEM_write_bio_PKCS7(out, p7); 316 *signature_len = BIO_get_mem_data(out, &tmp_sig); 317 *signature = xmalloc(*signature_len); 318 memcpy(*signature, tmp_sig, *signature_len); 319 BIO_free_all(out); 320 321 PKCS7_free(p7); 322 323 status = 0; 324 325 cleanup: 326 sk_X509_free(c); 327 sk_X509_free(cert_chain); 328 EVP_PKEY_free(private_key); 329 BIO_free(in); 330 331 return status; 332 } 333