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