d4dc94926224783d0cc42e428de50a3bb6e0dbb0
[strongswan.git] / src / pki / commands / pkcs7.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "pki.h"
17
18 #include <credentials/containers/pkcs7.h>
19 #include <credentials/sets/mem_cred.h>
20
21 /**
22 * Read input data as chunk
23 */
24 static chunk_t read_from_stream(FILE *stream)
25 {
26 char buf[8096];
27 size_t len, total = 0;
28
29 while (TRUE)
30 {
31 len = fread(buf + total, 1, sizeof(buf) - total, stream);
32 if (len < 0)
33 {
34 return chunk_empty;
35 }
36 if (len == 0)
37 {
38 return chunk_clone(chunk_create(buf, total));
39 }
40 total += len;
41 if (total == sizeof(buf))
42 {
43 fprintf(stderr, "buffer too small to read input!\n");
44 return chunk_empty;
45 }
46 }
47 }
48
49 /**
50 * Write output data from chunk to stream
51 */
52 static bool write_to_stream(FILE *stream, chunk_t data)
53 {
54 size_t len, total = 0;
55
56 while (total < data.len)
57 {
58 len = fwrite(data.ptr + total, 1, data.len - total, stream);
59 if (len <= 0)
60 {
61 return FALSE;
62 }
63 total += len;
64 }
65 return TRUE;
66 }
67
68 /**
69 * Verify PKCS#7 signed-data
70 */
71 static int verify(chunk_t chunk)
72 {
73 container_t *container;
74 enumerator_t *enumerator;
75 certificate_t *cert;
76 auth_cfg_t *auth;
77 chunk_t data;
78 bool verified = FALSE;
79
80 container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
81 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
82 if (!container)
83 {
84 return 1;
85 }
86
87 if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA)
88 {
89 fprintf(stderr, "verification failed, container is %N\n",
90 container_type_names, container->get_type(container));
91 container->destroy(container);
92 return 1;
93 }
94
95 enumerator = container->create_signature_enumerator(container);
96 while (enumerator->enumerate(enumerator, &auth))
97 {
98 verified = TRUE;
99 cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
100 if (cert)
101 {
102 fprintf(stderr, "signed by '%Y'\n", cert->get_subject(cert));
103 }
104 }
105 enumerator->destroy(enumerator);
106
107 if (!verified)
108 {
109 fprintf(stderr, "no trusted signature found\n");
110 }
111
112 if (verified)
113 {
114 if (container->get_data(container, &data))
115 {
116 write_to_stream(stdout, data);
117 free(data.ptr);
118 }
119 else
120 {
121 verified = FALSE;
122 }
123 }
124 container->destroy(container);
125
126 return verified ? 0 : 1;
127 }
128
129 /**
130 * Sign data into PKCS#7 signed-data
131 */
132 static int sign(chunk_t chunk, certificate_t *cert, private_key_t *key)
133 {
134 container_t *container;
135 chunk_t encoding;
136 int res = 1;
137
138 container = lib->creds->create(lib->creds,
139 CRED_CONTAINER, CONTAINER_PKCS7_SIGNED_DATA,
140 BUILD_BLOB, chunk,
141 BUILD_SIGNING_CERT, cert,
142 BUILD_SIGNING_KEY, key,
143 BUILD_END);
144 if (container)
145 {
146 if (container->get_encoding(container, &encoding))
147 {
148 write_to_stream(stdout, encoding);
149 free(encoding.ptr);
150 }
151 container->destroy(container);
152 }
153 return res;
154 }
155
156 /**
157 * Encrypt data to a PKCS#7 enveloped-data
158 */
159 static int encrypt(chunk_t chunk, certificate_t *cert)
160 {
161 container_t *container;
162 chunk_t encoding;
163 int res = 1;
164
165 container = lib->creds->create(lib->creds,
166 CRED_CONTAINER, CONTAINER_PKCS7_ENVELOPED_DATA,
167 BUILD_BLOB, chunk, BUILD_CERT, cert,
168 BUILD_END);
169 if (container)
170 {
171 if (container->get_encoding(container, &encoding))
172 {
173 write_to_stream(stdout, encoding);
174 free(encoding.ptr);
175 }
176 container->destroy(container);
177 }
178 return res;
179 }
180
181 /**
182 * Decrypt PKCS#7 enveloped-data
183 */
184 static int decrypt(chunk_t chunk)
185 {
186 container_t *container;
187 chunk_t data;
188
189 container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
190 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
191 if (!container)
192 {
193 return 1;
194 }
195 if (container->get_type(container) != CONTAINER_PKCS7_ENVELOPED_DATA)
196 {
197 fprintf(stderr, "decryption failed, container is %N\n",
198 container_type_names, container->get_type(container));
199 container->destroy(container);
200 return 1;
201 }
202 if (!container->get_data(container, &data))
203 {
204 fprintf(stderr, "PKCS#7 decryption failed\n");
205 container->destroy(container);
206 return 1;
207 }
208
209 write_to_stream(stdout, data);
210 free(data.ptr);
211
212 return 0;
213 }
214
215 /**
216 * Wrap/Unwrap PKCs#7 containers
217 */
218 static int pkcs7()
219 {
220 char *arg, *file = NULL;
221 private_key_t *key = NULL;
222 certificate_t *cert = NULL;
223 chunk_t data = chunk_empty;
224 mem_cred_t *creds;
225 int res = 1;
226 FILE *in;
227 enum {
228 OP_NONE,
229 OP_SIGN,
230 OP_VERIFY,
231 OP_ENCRYPT,
232 OP_DECRYPT,
233 } op = OP_NONE;
234
235 creds = mem_cred_create();
236
237 while (TRUE)
238 {
239 switch (command_getopt(&arg))
240 {
241 case 'h':
242 return command_usage(NULL);
243 case 'i':
244 file = arg;
245 continue;
246 case 's':
247 if (op != OP_NONE)
248 {
249 goto invalid;
250 }
251 op = OP_SIGN;
252 continue;
253 case 'u':
254 if (op != OP_NONE)
255 {
256 goto invalid;
257 }
258 op = OP_VERIFY;
259 continue;
260 case 'e':
261 if (op != OP_NONE)
262 {
263 goto invalid;
264 }
265 op = OP_ENCRYPT;
266 continue;
267 case 'd':
268 if (op != OP_NONE)
269 {
270 goto invalid;
271 }
272 op = OP_DECRYPT;
273 continue;
274 case 'k':
275 key = lib->creds->create(lib->creds,
276 CRED_PRIVATE_KEY, KEY_RSA,
277 BUILD_FROM_FILE, arg, BUILD_END);
278 if (!key)
279 {
280 fprintf(stderr, "parsing private key failed\n");
281 goto end;
282 }
283 creds->add_key(creds, key);
284 continue;
285 case 'c':
286 cert = lib->creds->create(lib->creds,
287 CRED_CERTIFICATE, CERT_X509,
288 BUILD_FROM_FILE, arg, BUILD_END);
289 if (!cert)
290 {
291 fprintf(stderr, "parsing certificate failed\n");
292 goto end;
293 }
294 creds->add_cert(creds, TRUE, cert);
295 continue;
296 case EOF:
297 break;
298 default:
299 invalid:
300 creds->destroy(creds);
301 return command_usage("invalid --pkcs7 option");
302 }
303 break;
304 }
305
306 if (file)
307 {
308 in = fopen(file, "r");
309 if (in)
310 {
311 data = read_from_stream(in);
312 fclose(in);
313 }
314 }
315 else
316 {
317 data = read_from_stream(stdin);
318 }
319
320 if (!data.len)
321 {
322 fprintf(stderr, "reading input failed!\n");
323 goto end;
324 }
325 if (!cert)
326 {
327 fprintf(stderr, "requiring a certificate!\n");
328 goto end;
329 }
330
331 lib->credmgr->add_local_set(lib->credmgr, &creds->set, FALSE);
332
333 switch (op)
334 {
335 case OP_SIGN:
336 if (!key)
337 {
338 fprintf(stderr, "signing requires a private key\n");
339 res = 1;
340 break;
341 }
342 res = sign(data, cert, key);
343 break;
344 case OP_VERIFY:
345 res = verify(data);
346 break;
347 case OP_ENCRYPT:
348 res = encrypt(data, cert);
349 break;
350 case OP_DECRYPT:
351 if (!key)
352 {
353 fprintf(stderr, "decryption requires a private key\n");
354 res = 1;
355 break;
356 }
357 res = decrypt(data);
358 break;
359 default:
360 res = 1;
361 break;
362 }
363 lib->credmgr->remove_local_set(lib->credmgr, &creds->set);
364
365 end:
366 creds->destroy(creds);
367 free(data.ptr);
368 return res;
369 }
370
371 /**
372 * Register the command.
373 */
374 static void __attribute__ ((constructor))reg()
375 {
376 command_register((command_t) {
377 pkcs7, '7', "pkcs7", "PKCS#7 wrap/unwrap functions",
378 {"--sign | --verify | --encrypt | --decrypt",
379 "--certificate+ [--key]"},
380 {
381 {"help", 'h', 0, "show usage information"},
382 {"sign", 's', 0, "create PKCS#7 signed-data"},
383 {"verify", 'u', 0, "verify PKCS#7 signed-data"},
384 {"encrypt", 'e', 0, "create PKCS#7 enveloped-data"},
385 {"decrypt", 'd', 0, "decrypt PKCS#7 enveloped-data"},
386 {"in", 'i', 1, "input file, default: stdin"},
387 {"key", 'k', 1, "path to private key for sign/decryp"},
388 {"cert", 'c', 1, "path to certificate for sign/verify/encryp"},
389 }
390 });
391 }