pk7_smime.c revision 296465
1/* pk7_smime.c */ 2/* 3 * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 4 * project. 5 */ 6/* ==================================================================== 7 * Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. All advertising materials mentioning features or use of this 22 * software must display the following acknowledgment: 23 * "This product includes software developed by the OpenSSL Project 24 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 25 * 26 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 27 * endorse or promote products derived from this software without 28 * prior written permission. For written permission, please contact 29 * licensing@OpenSSL.org. 30 * 31 * 5. Products derived from this software may not be called "OpenSSL" 32 * nor may "OpenSSL" appear in their names without prior written 33 * permission of the OpenSSL Project. 34 * 35 * 6. Redistributions of any form whatsoever must retain the following 36 * acknowledgment: 37 * "This product includes software developed by the OpenSSL Project 38 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 39 * 40 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 41 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 49 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 51 * OF THE POSSIBILITY OF SUCH DAMAGE. 52 * ==================================================================== 53 * 54 * This product includes cryptographic software written by Eric Young 55 * (eay@cryptsoft.com). This product includes software written by Tim 56 * Hudson (tjh@cryptsoft.com). 57 * 58 */ 59 60/* Simple PKCS#7 processing functions */ 61 62#include <stdio.h> 63#include "cryptlib.h" 64#include <openssl/x509.h> 65#include <openssl/x509v3.h> 66 67PKCS7 *PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs, 68 BIO *data, int flags) 69{ 70 PKCS7 *p7 = NULL; 71 PKCS7_SIGNER_INFO *si; 72 BIO *p7bio = NULL; 73 STACK_OF(X509_ALGOR) *smcap = NULL; 74 int i; 75 76 if (!X509_check_private_key(signcert, pkey)) { 77 PKCS7err(PKCS7_F_PKCS7_SIGN, 78 PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); 79 return NULL; 80 } 81 82 if (!(p7 = PKCS7_new())) { 83 PKCS7err(PKCS7_F_PKCS7_SIGN, ERR_R_MALLOC_FAILURE); 84 return NULL; 85 } 86 87 if (!PKCS7_set_type(p7, NID_pkcs7_signed)) 88 goto err; 89 90 if (!PKCS7_content_new(p7, NID_pkcs7_data)) 91 goto err; 92 93 if (!(si = PKCS7_add_signature(p7, signcert, pkey, EVP_sha1()))) { 94 PKCS7err(PKCS7_F_PKCS7_SIGN, PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR); 95 goto err; 96 } 97 98 if (!(flags & PKCS7_NOCERTS)) { 99 if (!PKCS7_add_certificate(p7, signcert)) 100 goto err; 101 if (certs) 102 for (i = 0; i < sk_X509_num(certs); i++) 103 if (!PKCS7_add_certificate(p7, sk_X509_value(certs, i))) 104 goto err; 105 } 106 107 if (!(flags & PKCS7_NOATTR)) { 108 if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, 109 V_ASN1_OBJECT, 110 OBJ_nid2obj(NID_pkcs7_data))) 111 goto err; 112 /* Add SMIMECapabilities */ 113 if (!(flags & PKCS7_NOSMIMECAP)) { 114 if (!(smcap = sk_X509_ALGOR_new_null())) { 115 PKCS7err(PKCS7_F_PKCS7_SIGN, ERR_R_MALLOC_FAILURE); 116 goto err; 117 } 118#ifndef OPENSSL_NO_DES 119 if (!PKCS7_simple_smimecap(smcap, NID_des_ede3_cbc, -1)) 120 goto err; 121#endif 122#ifndef OPENSSL_NO_RC2 123 if (!PKCS7_simple_smimecap(smcap, NID_rc2_cbc, 128)) 124 goto err; 125 if (!PKCS7_simple_smimecap(smcap, NID_rc2_cbc, 64)) 126 goto err; 127#endif 128#ifndef OPENSSL_NO_DES 129 if (!PKCS7_simple_smimecap(smcap, NID_des_cbc, -1)) 130 goto err; 131#endif 132#ifndef OPENSSL_NO_RC2 133 if (!PKCS7_simple_smimecap(smcap, NID_rc2_cbc, 40)) 134 goto err; 135#endif 136 if (!PKCS7_add_attrib_smimecap(si, smcap)) 137 goto err; 138 sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free); 139 smcap = NULL; 140 } 141 } 142 143 if (flags & PKCS7_DETACHED) 144 PKCS7_set_detached(p7, 1); 145 146 if (flags & PKCS7_STREAM) 147 return p7; 148 149 if (!(p7bio = PKCS7_dataInit(p7, NULL))) { 150 PKCS7err(PKCS7_F_PKCS7_SIGN, ERR_R_MALLOC_FAILURE); 151 goto err; 152 } 153 154 SMIME_crlf_copy(data, p7bio, flags); 155 156 if (!PKCS7_dataFinal(p7, p7bio)) { 157 PKCS7err(PKCS7_F_PKCS7_SIGN, PKCS7_R_PKCS7_DATASIGN); 158 goto err; 159 } 160 161 BIO_free_all(p7bio); 162 return p7; 163 err: 164 sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free); 165 BIO_free_all(p7bio); 166 PKCS7_free(p7); 167 return NULL; 168} 169 170int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, 171 BIO *indata, BIO *out, int flags) 172{ 173 STACK_OF(X509) *signers; 174 X509 *signer; 175 STACK_OF(PKCS7_SIGNER_INFO) *sinfos; 176 PKCS7_SIGNER_INFO *si; 177 X509_STORE_CTX cert_ctx; 178 char buf[4096]; 179 int i, j = 0, k, ret = 0; 180 BIO *p7bio; 181 BIO *tmpin, *tmpout; 182 183 if (!p7) { 184 PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_INVALID_NULL_POINTER); 185 return 0; 186 } 187 188 if (!PKCS7_type_is_signed(p7)) { 189 PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_WRONG_CONTENT_TYPE); 190 return 0; 191 } 192 193 /* Check for no data and no content: no data to verify signature */ 194 if (PKCS7_get_detached(p7) && !indata) { 195 PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_NO_CONTENT); 196 return 0; 197 } 198#if 0 199 /* 200 * NB: this test commented out because some versions of Netscape 201 * illegally include zero length content when signing data. 202 */ 203 204 /* Check for data and content: two sets of data */ 205 if (!PKCS7_get_detached(p7) && indata) { 206 PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_CONTENT_AND_DATA_PRESENT); 207 return 0; 208 } 209#endif 210 211 sinfos = PKCS7_get_signer_info(p7); 212 213 if (!sinfos || !sk_PKCS7_SIGNER_INFO_num(sinfos)) { 214 PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_NO_SIGNATURES_ON_DATA); 215 return 0; 216 } 217 218 signers = PKCS7_get0_signers(p7, certs, flags); 219 220 if (!signers) 221 return 0; 222 223 /* Now verify the certificates */ 224 225 if (!(flags & PKCS7_NOVERIFY)) 226 for (k = 0; k < sk_X509_num(signers); k++) { 227 signer = sk_X509_value(signers, k); 228 if (!(flags & PKCS7_NOCHAIN)) { 229 if (!X509_STORE_CTX_init(&cert_ctx, store, signer, 230 p7->d.sign->cert)) { 231 PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_X509_LIB); 232 sk_X509_free(signers); 233 return 0; 234 } 235 X509_STORE_CTX_set_default(&cert_ctx, "smime_sign"); 236 } else if (!X509_STORE_CTX_init(&cert_ctx, store, signer, NULL)) { 237 PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_X509_LIB); 238 sk_X509_free(signers); 239 return 0; 240 } 241 if (!(flags & PKCS7_NOCRL)) 242 X509_STORE_CTX_set0_crls(&cert_ctx, p7->d.sign->crl); 243 i = X509_verify_cert(&cert_ctx); 244 if (i <= 0) 245 j = X509_STORE_CTX_get_error(&cert_ctx); 246 X509_STORE_CTX_cleanup(&cert_ctx); 247 if (i <= 0) { 248 PKCS7err(PKCS7_F_PKCS7_VERIFY, 249 PKCS7_R_CERTIFICATE_VERIFY_ERROR); 250 ERR_add_error_data(2, "Verify error:", 251 X509_verify_cert_error_string(j)); 252 sk_X509_free(signers); 253 return 0; 254 } 255 /* Check for revocation status here */ 256 } 257 258 /* 259 * Performance optimization: if the content is a memory BIO then store 260 * its contents in a temporary read only memory BIO. This avoids 261 * potentially large numbers of slow copies of data which will occur when 262 * reading from a read write memory BIO when signatures are calculated. 263 */ 264 265 if (indata && (BIO_method_type(indata) == BIO_TYPE_MEM)) { 266 char *ptr; 267 long len; 268 len = BIO_get_mem_data(indata, &ptr); 269 tmpin = BIO_new_mem_buf(ptr, len); 270 if (tmpin == NULL) { 271 PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_MALLOC_FAILURE); 272 return 0; 273 } 274 } else 275 tmpin = indata; 276 277 if (!(p7bio = PKCS7_dataInit(p7, tmpin))) 278 goto err; 279 280 if (flags & PKCS7_TEXT) { 281 if (!(tmpout = BIO_new(BIO_s_mem()))) { 282 PKCS7err(PKCS7_F_PKCS7_VERIFY, ERR_R_MALLOC_FAILURE); 283 goto err; 284 } 285 BIO_set_mem_eof_return(tmpout, 0); 286 } else 287 tmpout = out; 288 289 /* We now have to 'read' from p7bio to calculate digests etc. */ 290 for (;;) { 291 i = BIO_read(p7bio, buf, sizeof(buf)); 292 if (i <= 0) 293 break; 294 if (tmpout) 295 BIO_write(tmpout, buf, i); 296 } 297 298 if (flags & PKCS7_TEXT) { 299 if (!SMIME_text(tmpout, out)) { 300 PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_SMIME_TEXT_ERROR); 301 BIO_free(tmpout); 302 goto err; 303 } 304 BIO_free(tmpout); 305 } 306 307 /* Now Verify All Signatures */ 308 if (!(flags & PKCS7_NOSIGS)) 309 for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++) { 310 si = sk_PKCS7_SIGNER_INFO_value(sinfos, i); 311 signer = sk_X509_value(signers, i); 312 j = PKCS7_signatureVerify(p7bio, p7, si, signer); 313 if (j <= 0) { 314 PKCS7err(PKCS7_F_PKCS7_VERIFY, PKCS7_R_SIGNATURE_FAILURE); 315 goto err; 316 } 317 } 318 319 ret = 1; 320 321 err: 322 323 if (tmpin == indata) { 324 if (indata) 325 BIO_pop(p7bio); 326 } 327 BIO_free_all(p7bio); 328 329 sk_X509_free(signers); 330 331 return ret; 332} 333 334STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs, 335 int flags) 336{ 337 STACK_OF(X509) *signers; 338 STACK_OF(PKCS7_SIGNER_INFO) *sinfos; 339 PKCS7_SIGNER_INFO *si; 340 PKCS7_ISSUER_AND_SERIAL *ias; 341 X509 *signer; 342 int i; 343 344 if (!p7) { 345 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, PKCS7_R_INVALID_NULL_POINTER); 346 return NULL; 347 } 348 349 if (!PKCS7_type_is_signed(p7)) { 350 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, PKCS7_R_WRONG_CONTENT_TYPE); 351 return NULL; 352 } 353 354 /* Collect all the signers together */ 355 356 sinfos = PKCS7_get_signer_info(p7); 357 358 if (sk_PKCS7_SIGNER_INFO_num(sinfos) <= 0) { 359 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, PKCS7_R_NO_SIGNERS); 360 return NULL; 361 } 362 363 if (!(signers = sk_X509_new_null())) { 364 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, ERR_R_MALLOC_FAILURE); 365 return NULL; 366 } 367 368 for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++) { 369 si = sk_PKCS7_SIGNER_INFO_value(sinfos, i); 370 ias = si->issuer_and_serial; 371 signer = NULL; 372 /* If any certificates passed they take priority */ 373 if (certs) 374 signer = X509_find_by_issuer_and_serial(certs, 375 ias->issuer, ias->serial); 376 if (!signer && !(flags & PKCS7_NOINTERN) 377 && p7->d.sign->cert) 378 signer = 379 X509_find_by_issuer_and_serial(p7->d.sign->cert, 380 ias->issuer, ias->serial); 381 if (!signer) { 382 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS, 383 PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND); 384 sk_X509_free(signers); 385 return NULL; 386 } 387 388 if (!sk_X509_push(signers, signer)) { 389 sk_X509_free(signers); 390 return NULL; 391 } 392 } 393 return signers; 394} 395 396/* Build a complete PKCS#7 enveloped data */ 397 398PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher, 399 int flags) 400{ 401 PKCS7 *p7; 402 BIO *p7bio = NULL; 403 int i; 404 X509 *x509; 405 if (!(p7 = PKCS7_new())) { 406 PKCS7err(PKCS7_F_PKCS7_ENCRYPT, ERR_R_MALLOC_FAILURE); 407 return NULL; 408 } 409 410 if (!PKCS7_set_type(p7, NID_pkcs7_enveloped)) 411 goto err; 412 if (!PKCS7_set_cipher(p7, cipher)) { 413 PKCS7err(PKCS7_F_PKCS7_ENCRYPT, PKCS7_R_ERROR_SETTING_CIPHER); 414 goto err; 415 } 416 417 for (i = 0; i < sk_X509_num(certs); i++) { 418 x509 = sk_X509_value(certs, i); 419 if (!PKCS7_add_recipient(p7, x509)) { 420 PKCS7err(PKCS7_F_PKCS7_ENCRYPT, PKCS7_R_ERROR_ADDING_RECIPIENT); 421 goto err; 422 } 423 } 424 425 if (!(p7bio = PKCS7_dataInit(p7, NULL))) { 426 PKCS7err(PKCS7_F_PKCS7_ENCRYPT, ERR_R_MALLOC_FAILURE); 427 goto err; 428 } 429 430 SMIME_crlf_copy(in, p7bio, flags); 431 432 (void)BIO_flush(p7bio); 433 434 if (!PKCS7_dataFinal(p7, p7bio)) { 435 PKCS7err(PKCS7_F_PKCS7_ENCRYPT, PKCS7_R_PKCS7_DATAFINAL_ERROR); 436 goto err; 437 } 438 BIO_free_all(p7bio); 439 440 return p7; 441 442 err: 443 444 BIO_free_all(p7bio); 445 PKCS7_free(p7); 446 return NULL; 447 448} 449 450int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags) 451{ 452 BIO *tmpmem; 453 int ret, i; 454 char buf[4096]; 455 456 if (!p7) { 457 PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_INVALID_NULL_POINTER); 458 return 0; 459 } 460 461 if (!PKCS7_type_is_enveloped(p7)) { 462 PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_WRONG_CONTENT_TYPE); 463 return 0; 464 } 465 466 if (cert && !X509_check_private_key(cert, pkey)) { 467 PKCS7err(PKCS7_F_PKCS7_DECRYPT, 468 PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); 469 return 0; 470 } 471 472 if (!(tmpmem = PKCS7_dataDecode(p7, pkey, NULL, cert))) { 473 PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_DECRYPT_ERROR); 474 return 0; 475 } 476 477 if (flags & PKCS7_TEXT) { 478 BIO *tmpbuf, *bread; 479 /* Encrypt BIOs can't do BIO_gets() so add a buffer BIO */ 480 if (!(tmpbuf = BIO_new(BIO_f_buffer()))) { 481 PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE); 482 BIO_free_all(tmpmem); 483 return 0; 484 } 485 if (!(bread = BIO_push(tmpbuf, tmpmem))) { 486 PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE); 487 BIO_free_all(tmpbuf); 488 BIO_free_all(tmpmem); 489 return 0; 490 } 491 ret = SMIME_text(bread, data); 492 if (ret > 0 && BIO_method_type(tmpmem) == BIO_TYPE_CIPHER) { 493 if (!BIO_get_cipher_status(tmpmem)) 494 ret = 0; 495 } 496 BIO_free_all(bread); 497 return ret; 498 } else { 499 for (;;) { 500 i = BIO_read(tmpmem, buf, sizeof(buf)); 501 if (i <= 0) { 502 ret = 1; 503 if (BIO_method_type(tmpmem) == BIO_TYPE_CIPHER) { 504 if (!BIO_get_cipher_status(tmpmem)) 505 ret = 0; 506 } 507 508 break; 509 } 510 if (BIO_write(data, buf, i) != i) { 511 ret = 0; 512 break; 513 } 514 } 515 BIO_free_all(tmpmem); 516 return ret; 517 } 518} 519