pkcs7.c revision 1.3
1/* $NetBSD: pkcs7.c,v 1.3 2018/02/04 09:00:51 maya 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.3 2018/02/04 09:00:51 maya 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#if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L) 59#define X509_get_extended_key_usage(x) x->ex_xkusage 60#define X509_get_extension_flags(x) x->ex_flags 61#endif 62 63static const unsigned int pkg_key_usage = XKU_CODE_SIGN | XKU_SMIME; 64 65static STACK_OF(X509) * 66file_to_certs(const char *file) 67{ 68 unsigned long ret; 69 STACK_OF(X509) *certs; 70 FILE *f; 71 72 if ((f = fopen(file, "r")) == NULL) { 73 warn("open failed %s", file); 74 return NULL; 75 } 76 77 certs = sk_X509_new_null(); 78 for (;;) { 79 X509 *cert; 80 81 cert = PEM_read_X509(f, NULL, NULL, NULL); 82 if (cert == NULL) { 83 ret = ERR_GET_REASON(ERR_peek_error()); 84 if (ret == PEM_R_NO_START_LINE) { 85 /* End of file reached. no error */ 86 ERR_clear_error(); 87 break; 88 } 89 sk_X509_free(certs); 90 warnx("Can't read certificate in file: %s", file); 91 fclose(f); 92 return NULL; 93 } 94 sk_X509_insert(certs, cert, sk_X509_num(certs)); 95 } 96 97 fclose(f); 98 99 if (sk_X509_num(certs) == 0) { 100 sk_X509_free(certs); 101 certs = NULL; 102 warnx("No certificate found in file %s", file); 103 } 104 105 return certs; 106} 107 108int 109easy_pkcs7_verify(const char *content, size_t len, 110 const char *signature, size_t signature_len, 111 const char *anchor, int is_pkg) 112{ 113 STACK_OF(X509) *cert_chain, *signers; 114 X509_STORE *store; 115 BIO *sig, *in; 116 PKCS7 *p7; 117 int i, status; 118 X509_NAME *name; 119 char *subject; 120 121 OpenSSL_add_all_algorithms(); 122 ERR_load_crypto_strings(); 123 124 status = -1; 125 126 if (cert_chain_file) 127 cert_chain = file_to_certs(cert_chain_file); 128 else 129 cert_chain = NULL; 130 131 store = X509_STORE_new(); 132 if (store == NULL) { 133 sk_X509_free(cert_chain); 134 warnx("Failed to create certificate store"); 135 return -1; 136 } 137 138 X509_STORE_load_locations(store, anchor, NULL); 139 140 in = BIO_new_mem_buf(__UNCONST(content), len); 141 sig = BIO_new_mem_buf(__UNCONST(signature), signature_len); 142 signers = NULL; 143 144 p7 = PEM_read_bio_PKCS7(sig, NULL, NULL, NULL); 145 if (p7 == NULL) { 146 warnx("Failed to parse the signature"); 147 goto cleanup; 148 } 149 150 if (PKCS7_verify(p7, cert_chain, store, in, NULL, 0) != 1) { 151 warnx("Failed to verify signature"); 152 goto cleanup; 153 } 154 155 signers = PKCS7_get0_signers(p7, NULL, 0); 156 if (signers == NULL) { 157 warnx("Failed to get signers"); 158 goto cleanup; 159 } 160 161 if (sk_X509_num(signers) == 0) { 162 warnx("No signers found"); 163 goto cleanup; 164 } 165 166 for (i = 0; i < sk_X509_num(signers); i++) { 167 /* Compute ex_xkusage */ 168 X509_check_purpose(sk_X509_value(signers, i), -1, -1); 169 170 if (X509_check_ca(sk_X509_value(signers, i))) { 171 warnx("CA keys are not valid for signatures"); 172 goto cleanup; 173 } 174 if (is_pkg) { 175 if (X509_get_extended_key_usage(sk_X509_value(signers, i)) != pkg_key_usage) { 176 warnx("Certificate must have CODE SIGNING " 177 "and EMAIL PROTECTION property"); 178 goto cleanup; 179 } 180 } else { 181 if (X509_get_extension_flags(sk_X509_value(signers, i)) & EXFLAG_XKUSAGE) { 182 warnx("Certificate must not have any property"); 183 goto cleanup; 184 } 185 } 186 } 187 188 printf("Sigature ok, signed by:\n"); 189 190 for (i = 0; i < sk_X509_num(signers); i++) { 191 name = X509_get_subject_name(sk_X509_value(signers, i)); 192 subject = X509_NAME_oneline(name, NULL, 0); 193 194 printf("\t%s\n", subject); 195 196 OPENSSL_free(subject); 197 } 198 199 status = 0; 200 201cleanup: 202 sk_X509_free(cert_chain); 203 sk_X509_free(signers); 204 X509_STORE_free(store); 205 206 PKCS7_free(p7); 207 BIO_free(in); 208 BIO_free(sig); 209 210 return status; 211} 212 213static int 214ssl_pass_cb(char *buf, int size, int rwflag, void *u) 215{ 216 217 if (EVP_read_pw_string(buf, size, "Passphrase :", 0)) { 218#if OPENSSL_VERSION >= 0x0090608fL 219 OPENSSL_cleanse(buf, size); 220#else 221 memset(buf, 0, size); 222#endif 223 return 0; 224 } 225 return strlen(buf); 226} 227 228int 229easy_pkcs7_sign(const char *content, size_t len, 230 char **signature, size_t *signature_len, 231 const char *key_file, const char *cert_file) 232{ 233 FILE *f; 234 X509 *certificate; 235 STACK_OF(X509) *c, *cert_chain; 236 EVP_PKEY *private_key; 237 char *tmp_sig; 238 BIO *out, *in; 239 PKCS7 *p7; 240 int status; 241 242 OpenSSL_add_all_algorithms(); 243 ERR_load_crypto_strings(); 244 245 status = -1; 246 private_key = NULL; 247 cert_chain = NULL; 248 in = NULL; 249 250 c = file_to_certs(cert_file); 251 252 if (sk_X509_num(c) != 1) { 253 warnx("More then one certificate in the certificate file"); 254 goto cleanup; 255 } 256 certificate = sk_X509_value(c, 0); 257 258 /* Compute ex_kusage */ 259 X509_check_purpose(certificate, -1, 0); 260 261 if (X509_check_ca(certificate)) { 262 warnx("CA keys are not valid for signatures"); 263 goto cleanup; 264 } 265 266 if (X509_get_extended_key_usage(certificate) != pkg_key_usage) { 267 warnx("Certificate must have CODE SIGNING " 268 "and EMAIL PROTECTION property"); 269 goto cleanup; 270 } 271 272 if (cert_chain_file) 273 cert_chain = file_to_certs(cert_chain_file); 274 275 if ((f = fopen(key_file, "r")) == NULL) { 276 warn("Failed to open private key file %s", key_file); 277 goto cleanup; 278 } 279 private_key = PEM_read_PrivateKey(f, NULL, ssl_pass_cb, NULL); 280 fclose(f); 281 if (private_key == NULL) { 282 warnx("Can't read private key: %s", key_file); 283 goto cleanup; 284 } 285 286 if (X509_check_private_key(certificate, private_key) != 1) { 287 warnx("The private key %s doesn't match the certificate %s", 288 key_file, cert_file); 289 goto cleanup; 290 } 291 292 in = BIO_new_mem_buf(__UNCONST(content), len); 293 294 p7 = PKCS7_sign(certificate, private_key, cert_chain, in, 295 PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY); 296 if (p7 == NULL) { 297 warnx("Failed to create signature structure"); 298 goto cleanup; 299 } 300 301 out = BIO_new(BIO_s_mem()); 302 PEM_write_bio_PKCS7(out, p7); 303 *signature_len = BIO_get_mem_data(out, &tmp_sig); 304 *signature = xmalloc(*signature_len); 305 memcpy(*signature, tmp_sig, *signature_len); 306 BIO_free_all(out); 307 308 PKCS7_free(p7); 309 310 status = 0; 311 312cleanup: 313 sk_X509_free(c); 314 sk_X509_free(cert_chain); 315 EVP_PKEY_free(private_key); 316 BIO_free(in); 317 318 return status; 319} 320