]> git.ipfire.org Git - thirdparty/openssl.git/blob - apps/smime.c
Merge in my S/MIME library and utility.
[thirdparty/openssl.git] / apps / smime.c
1 /* smime.c */
2 /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL
3 * project 1999.
4 */
5 /* ====================================================================
6 * Copyright (c) 1999 The OpenSSL Project. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. All advertising materials mentioning features or use of this
21 * software must display the following acknowledgment:
22 * "This product includes software developed by the OpenSSL Project
23 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24 *
25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26 * endorse or promote products derived from this software without
27 * prior written permission. For written permission, please contact
28 * licensing@OpenSSL.org.
29 *
30 * 5. Products derived from this software may not be called "OpenSSL"
31 * nor may "OpenSSL" appear in their names without prior written
32 * permission of the OpenSSL Project.
33 *
34 * 6. Redistributions of any form whatsoever must retain the following
35 * acknowledgment:
36 * "This product includes software developed by the OpenSSL Project
37 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50 * OF THE POSSIBILITY OF SUCH DAMAGE.
51 * ====================================================================
52 *
53 * This product includes cryptographic software written by Eric Young
54 * (eay@cryptsoft.com). This product includes software written by Tim
55 * Hudson (tjh@cryptsoft.com).
56 *
57 */
58
59 /* S/MIME utility function */
60
61 #include <stdio.h>
62 #include <string.h>
63 #include <openssl/pem.h>
64 #include <openssl/err.h>
65 #include "apps.h"
66
67 #undef PROG
68 #define PROG smime_main
69 static X509 *load_cert(char *file);
70 static EVP_PKEY *load_key(char *file);
71 static STACK_OF(X509) *load_certs(char *file);
72 static X509_STORE *setup_verify(char *CAfile, char *CApath);
73
74 #define SMIME_OP 0x10
75 #define SMIME_ENCRYPT (1 | SMIME_OP)
76 #define SMIME_DECRYPT 2
77 #define SMIME_SIGN (3 | SMIME_OP)
78 #define SMIME_VERIFY 4
79 #define SMIME_PK7OUT 5
80
81 int MAIN(int argc, char **argv)
82 {
83 int operation = 0;
84 int ret = 0;
85 char **args;
86 char *inmode = "r", *outmode = "w";
87 char *infile = NULL, *outfile = NULL;
88 char *signerfile = NULL, *recipfile = NULL;
89 char *certfile = NULL, *keyfile = NULL;
90 EVP_CIPHER *cipher = NULL;
91 PKCS7 *p7 = NULL;
92 X509_STORE *store = NULL;
93 X509 *cert = NULL, *recip = NULL, *signer = NULL;
94 EVP_PKEY *key = NULL;
95 STACK_OF(X509) *encerts = NULL, *other = NULL;
96 BIO *in = NULL, *out = NULL, *indata = NULL;
97 int badarg = 0;
98 int flags = PKCS7_DETACHED;
99 char *to = NULL, *from = NULL, *subject = NULL;
100 char *CAfile = NULL, *CApath = NULL;
101
102 args = argv + 1;
103
104 ret = 1;
105
106 while (!badarg && *args && *args[0] == '-') {
107 if (!strcmp (*args, "-encrypt")) operation = SMIME_ENCRYPT;
108 else if (!strcmp (*args, "-decrypt")) operation = SMIME_DECRYPT;
109 else if (!strcmp (*args, "-sign")) operation = SMIME_SIGN;
110 else if (!strcmp (*args, "-verify")) operation = SMIME_VERIFY;
111 else if (!strcmp (*args, "-pk7out")) operation = SMIME_PK7OUT;
112 else if (!strcmp (*args, "-des3"))
113 cipher = EVP_des_ede3_cbc();
114 else if (!strcmp (*args, "-des"))
115 cipher = EVP_des_cbc();
116 else if (!strcmp (*args, "-rc2-40"))
117 cipher = EVP_rc2_40_cbc();
118 else if (!strcmp (*args, "-rc2-128"))
119 cipher = EVP_rc2_cbc();
120 else if (!strcmp (*args, "-rc2-64"))
121 cipher = EVP_rc2_64_cbc();
122 else if (!strcmp (*args, "-text"))
123 flags |= PKCS7_TEXT;
124 else if (!strcmp (*args, "-nointern"))
125 flags |= PKCS7_NOINTERN;
126 else if (!strcmp (*args, "-noverify"))
127 flags |= PKCS7_NOVERIFY;
128 else if (!strcmp (*args, "-nochain"))
129 flags |= PKCS7_NOCHAIN;
130 else if (!strcmp (*args, "-nocerts"))
131 flags |= PKCS7_NOCERTS;
132 else if (!strcmp (*args, "-noattr"))
133 flags |= PKCS7_NOATTR;
134 else if (!strcmp (*args, "-nodetach"))
135 flags &= ~PKCS7_DETACHED;
136 else if (!strcmp (*args, "-binary"))
137 flags |= PKCS7_BINARY;
138 else if (!strcmp (*args, "-to")) {
139 if (args[1]) {
140 args++;
141 to = *args;
142 } else badarg = 1;
143 } else if (!strcmp (*args, "-from")) {
144 if (args[1]) {
145 args++;
146 from = *args;
147 } else badarg = 1;
148 } else if (!strcmp (*args, "-subject")) {
149 if (args[1]) {
150 args++;
151 subject = *args;
152 } else badarg = 1;
153 } else if (!strcmp (*args, "-signer")) {
154 if (args[1]) {
155 args++;
156 signerfile = *args;
157 } else badarg = 1;
158 } else if (!strcmp (*args, "-recip")) {
159 if (args[1]) {
160 args++;
161 recipfile = *args;
162 } else badarg = 1;
163 } else if (!strcmp (*args, "-inkey")) {
164 if (args[1]) {
165 args++;
166 keyfile = *args;
167 } else badarg = 1;
168 } else if (!strcmp (*args, "-certfile")) {
169 if (args[1]) {
170 args++;
171 certfile = *args;
172 } else badarg = 1;
173 } else if (!strcmp (*args, "-CAfile")) {
174 if (args[1]) {
175 args++;
176 CAfile = *args;
177 } else badarg = 1;
178 } else if (!strcmp (*args, "-CApath")) {
179 if (args[1]) {
180 args++;
181 CApath = *args;
182 } else badarg = 1;
183 } else if (!strcmp (*args, "-in")) {
184 if (args[1]) {
185 args++;
186 infile = *args;
187 } else badarg = 1;
188 } else if (!strcmp (*args, "-out")) {
189 if (args[1]) {
190 args++;
191 outfile = *args;
192 } else badarg = 1;
193 } else badarg = 1;
194 args++;
195 }
196
197 if(operation == SMIME_SIGN) {
198 if(!signerfile) {
199 BIO_printf(bio_err, "No signer certificate specified\n");
200 badarg = 1;
201 }
202 } else if(operation == SMIME_DECRYPT) {
203 if(!recipfile) {
204 BIO_printf(bio_err, "No recipient certificate and key specified\n");
205 badarg = 1;
206 }
207 } else if(operation == SMIME_ENCRYPT) {
208 if(!*args) {
209 BIO_printf(bio_err, "No recipient(s) certificate(s) specified\n");
210 badarg = 1;
211 }
212 } else if(!operation) badarg = 1;
213
214 if (badarg) {
215 BIO_printf (bio_err, "Usage smime [options] cert.pem ...\n");
216 BIO_printf (bio_err, "where options are\n");
217 BIO_printf (bio_err, "-encrypt encrypt message\n");
218 BIO_printf (bio_err, "-decrypt decrypt encrypted message\n");
219 BIO_printf (bio_err, "-sign sign message\n");
220 BIO_printf (bio_err, "-verify verify signed message\n");
221 BIO_printf (bio_err, "-pk7out output PKCS#7 structure\n");
222 BIO_printf (bio_err, "-des3 encrypt with triple DES\n");
223 BIO_printf (bio_err, "-rc2-40 encrypt with RC2-40\n");
224 BIO_printf (bio_err, "-rc2-64 encrypt with RC2-64\n");
225 BIO_printf (bio_err, "-rc2-128 encrypt with RC2-128\n");
226 BIO_printf (bio_err, "-in file input file\n");
227 BIO_printf (bio_err, "-certfile file other certificates file\n");
228 BIO_printf (bio_err, "-signer file signer certificate file\n");
229 BIO_printf (bio_err, "-recip file recipient certificate file\n");
230 BIO_printf (bio_err, "-in file input file\n");
231 BIO_printf (bio_err, "-inkey file input private key (if not signer or recipient)\n");
232 BIO_printf (bio_err, "-out file output file\n");
233 BIO_printf (bio_err, "-to addr to address\n");
234 BIO_printf (bio_err, "-from ad from address\n");
235 BIO_printf (bio_err, "-subject s subject\n");
236 BIO_printf (bio_err, "-text include or delete text MIME headers\n");
237 BIO_printf (bio_err, "cert.pem recipient certificate(s)\n");
238 goto end;
239 }
240
241 ret = 2;
242
243 if(operation != SMIME_SIGN) flags &= ~PKCS7_DETACHED;
244
245 if(flags & PKCS7_BINARY) {
246 if(operation & SMIME_OP) inmode = "rb";
247 else outmode = "rb";
248 }
249
250 if(operation == SMIME_ENCRYPT) {
251 if (!cipher) cipher = EVP_rc2_40_cbc();
252 while (*args) {
253 encerts = sk_X509_new_null();
254 if(!(cert = load_cert(*args))) {
255 BIO_printf(bio_err, "Can't read recipent certificate file %s\n", *args);
256 goto end;
257 }
258 sk_X509_push (encerts, cert);
259 cert = NULL;
260 args++;
261 }
262 }
263
264 if(signerfile) {
265 if(!(signer = load_cert(signerfile))) {
266 BIO_printf(bio_err, "Can't read signer certificate file %s\n", signerfile);
267 goto end;
268 }
269 }
270
271 if(certfile) {
272 if(!(other = load_certs(certfile))) {
273 BIO_printf(bio_err, "Can't read certificate file %s\n", certfile);
274 ERR_print_errors(bio_err);
275 goto end;
276 }
277 }
278
279 if(recipfile) {
280 if(!(recip = load_cert(recipfile))) {
281 BIO_printf(bio_err, "Can't read recipient certificate file %s\n", recipfile);
282 ERR_print_errors(bio_err);
283 goto end;
284 }
285 }
286
287 if(operation == SMIME_DECRYPT) {
288 if(!keyfile) keyfile = recipfile;
289 } else if(operation == SMIME_SIGN) {
290 if(!keyfile) keyfile = signerfile;
291 } else keyfile = NULL;
292
293 if(keyfile) {
294 if(!(key = load_key(keyfile))) {
295 BIO_printf(bio_err, "Can't read recipient certificate file %s\n", keyfile);
296 ERR_print_errors(bio_err);
297 goto end;
298 }
299 }
300
301 if (infile) {
302 if (!(in = BIO_new_file(infile, inmode))) {
303 BIO_printf (bio_err,
304 "Can't open input file %s\n", infile);
305 goto end;
306 }
307 } else in = BIO_new_fp(stdin, BIO_NOCLOSE);
308
309 if (outfile) {
310 if (!(out = BIO_new_file(outfile, outmode))) {
311 BIO_printf (bio_err,
312 "Can't open output file %s\n", outfile);
313 goto end;
314 }
315 } else out = BIO_new_fp(stdout, BIO_NOCLOSE);
316
317 if(operation == SMIME_VERIFY)
318 if(!(store = setup_verify(CAfile, CApath))) goto end;
319
320 ret = 3;
321 if(operation == SMIME_ENCRYPT) {
322 p7 = PKCS7_encrypt(encerts, in, cipher, flags);
323 } else if(operation == SMIME_SIGN) {
324 p7 = PKCS7_sign(signer, key, other, in, flags);
325 BIO_reset(in);
326 } else {
327 if(!(p7 = SMIME_read_PKCS7(in, &indata))) {
328 BIO_printf(bio_err, "Error reading S/MIME message\n");
329 ret = 4;
330 goto end;
331 }
332 }
333
334 if(!p7) {
335 BIO_printf(bio_err, "Error creating PKCS#7 structure\n");
336 goto end;
337 }
338
339 if(operation == SMIME_DECRYPT) {
340 if(!PKCS7_decrypt(p7, key, recip, out, flags))
341 BIO_printf(bio_err, "Error decrypting PKCS#7 structure\n");
342 else ret = 0;
343 } else if(operation == SMIME_VERIFY) {
344 if(PKCS7_verify(p7, other, store, indata, out, flags)) {
345 BIO_printf(bio_err, "Verification Successful\n");
346 ret = 0;
347 } else {
348 BIO_printf(bio_err, "Verification Failure\n");
349 ret = 5;
350 }
351 } else if(operation == SMIME_PK7OUT) {
352 PEM_write_bio_PKCS7(out, p7);
353 } else {
354 if(to) BIO_printf(out, "To: %s\n", to);
355 if(from) BIO_printf(out, "From: %s\n", from);
356 if(subject) BIO_printf(out, "Subject: %s\n", subject);
357 SMIME_write_PKCS7(out, p7, in, flags);
358 }
359 end:
360 if(ret) ERR_print_errors(bio_err);
361 sk_X509_pop_free(encerts, X509_free);
362 sk_X509_pop_free(other, X509_free);
363 X509_STORE_free(store);
364 X509_free(cert);
365 X509_free(recip);
366 X509_free(signer);
367 EVP_PKEY_free(key);
368 PKCS7_free(p7);
369 BIO_free(in);
370 BIO_free(indata);
371 BIO_free(out);
372 return (ret);
373 }
374
375 static X509 *load_cert(char *file)
376 {
377 BIO *in;
378 X509 *cert;
379 if(!(in = BIO_new_file(file, "r"))) return NULL;
380 cert = PEM_read_bio_X509(in, NULL, NULL,NULL);
381 BIO_free(in);
382 return cert;
383 }
384
385 static EVP_PKEY *load_key(char *file)
386 {
387 BIO *in;
388 EVP_PKEY *key;
389 if(!(in = BIO_new_file(file, "r"))) return NULL;
390 key = PEM_read_bio_PrivateKey(in, NULL, NULL,NULL);
391 BIO_free(in);
392 return key;
393 }
394
395 static STACK_OF(X509) *load_certs(char *file)
396 {
397 BIO *in;
398 int i;
399 STACK_OF(X509) *othercerts;
400 STACK_OF(X509_INFO) *allcerts;
401 X509_INFO *xi;
402 if(!(in = BIO_new_file(file, "r"))) return NULL;
403 othercerts = sk_X509_new(NULL);
404 if(!othercerts) return NULL;
405 allcerts = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
406 for(i = 0; i < sk_X509_INFO_num(allcerts); i++) {
407 xi = sk_X509_INFO_value (allcerts, i);
408 if (xi->x509) {
409 sk_X509_push(othercerts, xi->x509);
410 xi->x509 = NULL;
411 }
412 }
413 sk_X509_INFO_pop_free(allcerts, X509_INFO_free);
414 BIO_free(in);
415 return othercerts;
416 }
417
418 static X509_STORE *setup_verify(char *CAfile, char *CApath)
419 {
420 X509_STORE *store;
421 X509_LOOKUP *lookup;
422 if(!(store = X509_STORE_new())) goto end;
423 lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
424 if (lookup == NULL) goto end;
425 if (CAfile) {
426 if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
427 BIO_printf(bio_err, "Error loading file %s\n", CAfile);
428 goto end;
429 }
430 } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
431
432 lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
433 if (lookup == NULL) goto end;
434 if (CApath) {
435 if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
436 BIO_printf(bio_err, "Error loading directory %s\n", CApath);
437 goto end;
438 }
439 } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
440
441 ERR_clear_error();
442 return store;
443 end:
444 X509_STORE_free(store);
445 return NULL;
446 }