Branch data Line data Source code
1 : : /* keygen.c - Key generation utilities. See smime.c for user interface.
2 : : *
3 : : * Copyright (c) 1999 Sampo Kellomaki <sampo@iki.fi>, All Rights Reserved.
4 : : * License: This software may be distributed under the same license
5 : : * terms as openssl (i.e. free, but mandatory attribution).
6 : : *
7 : : * This borrows quite heavily ideas and control flow from openssl/apps/req.c
8 : : * by Eric A. Young. You could say this file is destillation of Eric's
9 : : * work with many of the parameters hard wired:
10 : : *
11 : : * - 1024 bit RSA only
12 : : * - 3DES for private key
13 : : * - MD5 hash
14 : : *
15 : : * 27.9.1999, Created. --Sampo
16 : : * 30.9.1999, added PKCS12 stuff, --Sampo
17 : : * 1.10.1999, improved error reporting, --Sampo
18 : : * 6.10.1999, divided into keygen.c, pkcs12.c, and smime-qry.c --Sampo
19 : : * 9.10.1999, reviewed for double frees, --Sampo
20 : : */
21 : :
22 : : /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
23 : : * All rights reserved.
24 : : *
25 : : * This package is an SSL implementation written
26 : : * by Eric Young (eay@cryptsoft.com).
27 : : * The implementation was written so as to conform with Netscapes SSL.
28 : : *
29 : : * This library is free for commercial and non-commercial use as long as
30 : : * the following conditions are aheared to. The following conditions
31 : : * apply to all code found in this distribution, be it the RC4, RSA,
32 : : * lhash, DES, etc., code; not just the SSL code. The SSL documentation
33 : : * included with this distribution is covered by the same copyright terms
34 : : * except that the holder is Tim Hudson (tjh@cryptsoft.com).
35 : : *
36 : : * Copyright remains Eric Young's, and as such any Copyright notices in
37 : : * the code are not to be removed.
38 : : * If this package is used in a product, Eric Young should be given attribution
39 : : * as the author of the parts of the library used.
40 : : * This can be in the form of a textual message at program startup or
41 : : * in documentation (online or textual) provided with the package.
42 : : *
43 : : * Redistribution and use in source and binary forms, with or without
44 : : * modification, are permitted provided that the following conditions
45 : : * are met:
46 : : * 1. Redistributions of source code must retain the copyright
47 : : * notice, this list of conditions and the following disclaimer.
48 : : * 2. Redistributions in binary form must reproduce the above copyright
49 : : * notice, this list of conditions and the following disclaimer in the
50 : : * documentation and/or other materials provided with the distribution.
51 : : * 3. All advertising materials mentioning features or use of this software
52 : : * must display the following acknowledgement:
53 : : * "This product includes cryptographic software written by
54 : : * Eric Young (eay@cryptsoft.com)"
55 : : * The word 'cryptographic' can be left out if the rouines from the library
56 : : * being used are not cryptographic related :-).
57 : : * 4. If you include any Windows specific code (or a derivative thereof) from
58 : : * the apps directory (application code) you must include an acknowledgement:
59 : : * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
60 : : *
61 : : * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
62 : : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
63 : : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64 : : * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
65 : : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
66 : : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
67 : : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
68 : : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
69 : : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
70 : : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
71 : : * SUCH DAMAGE.
72 : : *
73 : : * The licence and distribution terms for any publically available version or
74 : : * derivative of this code cannot be changed. i.e. this code cannot simply be
75 : : * copied and put under another distribution licence
76 : : * [including the GNU Public Licence.]
77 : : */
78 : :
79 : : #include "platform.h"
80 : : #include <stdio.h>
81 : : #include <string.h>
82 : : #include <time.h>
83 : :
84 : : #ifdef __MWERKS__
85 : : # include "macglue.h"
86 : : #endif
87 : :
88 : : #include "logprint.h"
89 : :
90 : : #include <openssl/crypto.h>
91 : : #include <openssl/buffer.h>
92 : : #include <openssl/err.h>
93 : : #include <openssl/rand.h>
94 : : #include <openssl/conf.h>
95 : : #include <openssl/bio.h>
96 : : #include <openssl/stack.h>
97 : : #include <openssl/objects.h>
98 : : #include <openssl/asn1.h>
99 : : #include <openssl/pem.h>
100 : : #include <openssl/evp.h>
101 : : #include <openssl/x509.h>
102 : : #include <openssl/x509v3.h>
103 : : #include <openssl/pkcs12.h>
104 : :
105 : : #define SMIME_INTERNALS /* we want also our internal helper functions */
106 : : #include "smimeutil.h"
107 : :
108 : : /* Enforce some rules about possible characters for different attributes */
109 : :
110 : : static int req_fix_data(int nid, int *type /*IN-OUT*/)
111 : 6 : {
112 [ + + - + ]: 6 : switch (nid) {
113 : 2 : case NID_pkcs9_emailAddress: *type=V_ASN1_IA5STRING; break;
114 : : case NID_commonName:
115 : : case NID_pkcs9_challengePassword:
116 [ - + ]: 2 : if (*type == V_ASN1_IA5STRING)
117 : 0 : *type=V_ASN1_T61STRING;
118 : 2 : break;
119 : : case NID_pkcs9_unstructuredName:
120 [ # # ]: 0 : if (*type == V_ASN1_T61STRING) {
121 : 0 : GOTO_ERR("08 invalid characters in attribute value string");
122 : : } else
123 : 0 : *type=V_ASN1_IA5STRING;
124 : : break;
125 : : }
126 : :
127 : 6 : return 1;
128 : 0 : err:
129 : 0 : return 0;
130 : : }
131 : :
132 : : /* Add an entry to distinguished name */
133 : :
134 : : /* Called by: populate_request */
135 : : static int add_DN_object(X509_NAME *n, int nid, unsigned char *val)
136 : 4 : {
137 : 4 : X509_NAME_ENTRY *ne=NULL;
138 : 4 : int type = ASN1_PRINTABLE_type(val,-1 /* uses strlen() */);
139 : :
140 [ + - ]: 4 : if (req_fix_data(nid, &type) == 0) goto err;
141 : :
142 [ - + ]: 4 : if (!(ne=X509_NAME_ENTRY_create_by_NID(NULL, nid, type,
143 : : val, strlen((char*)val))))
144 : 0 : GOTO_ERR("X509_NAME_ENTRY_create_by_NID");
145 [ - + ]: 4 : if (!X509_NAME_add_entry(n,ne,X509_NAME_entry_count(n),0))
146 : 0 : GOTO_ERR("X509_NAME_add_entry");
147 : :
148 : 4 : X509_NAME_ENTRY_free(ne);
149 : 4 : return 1;
150 : 0 : err:
151 [ # # ]: 0 : if (ne != NULL) X509_NAME_ENTRY_free(ne);
152 : 0 : return 0;
153 : : }
154 : :
155 : : /* attribute objects are more complicated because they can be multivalued */
156 : :
157 : : /* Called by: */
158 : : static int add_attribute_object(STACK_OF(X509_ATTRIBUTE) *n, int nid, unsigned char *val)
159 : 2 : {
160 : 2 : X509_ATTRIBUTE *xa=NULL;
161 : 2 : ASN1_BIT_STRING *bs=NULL;
162 : 2 : ASN1_TYPE *at=NULL;
163 : :
164 : : /* add object plus value */
165 [ - + ]: 2 : if ((xa=X509_ATTRIBUTE_new()) == NULL) GOTO_ERR("no memory?");
166 [ - + ]: 2 : if ((xa->value.set=sk_ASN1_TYPE_new_null()) == NULL) GOTO_ERR("no memory?");
167 : : /*xa->single = 1; **** this may also be set on some versions */
168 : :
169 [ + - ]: 2 : if (xa->object != NULL) ASN1_OBJECT_free(xa->object);
170 : 2 : xa->object=OBJ_nid2obj(nid);
171 : :
172 [ - + ]: 2 : if ((bs=ASN1_BIT_STRING_new()) == NULL) GOTO_ERR("no memory?");
173 : 2 : bs->type=ASN1_PRINTABLE_type(val,-1 /* use strlen() */);
174 : :
175 [ + - ]: 2 : if (!req_fix_data(nid,&bs->type)) goto err;
176 : :
177 [ - + ]: 2 : if (!ASN1_STRING_set(bs,val,strlen((char*)val)+1)) GOTO_ERR("no memory?");
178 [ - + ]: 2 : if ((at=ASN1_TYPE_new()) == NULL) GOTO_ERR("no memory?");
179 : :
180 : 2 : ASN1_TYPE_set(at,bs->type,(char *)bs);
181 : 2 : sk_ASN1_TYPE_push(xa->value.set,at);
182 : 2 : bs=NULL;
183 : 2 : at=NULL;
184 : : /* only one item per attribute */
185 : :
186 [ - + ]: 2 : if (!sk_X509_ATTRIBUTE_push(n,xa))
187 : 0 : GOTO_ERR("sk_X509_ATTRIBUTE_push (no memory?)");
188 : 2 : return 1;
189 : 0 : err:
190 [ # # ]: 0 : if (xa != NULL) X509_ATTRIBUTE_free(xa);
191 [ # # ]: 0 : if (at != NULL) ASN1_TYPE_free(at);
192 [ # # ]: 0 : if (bs != NULL) ASN1_BIT_STRING_free(bs);
193 : 0 : return 0;
194 : : }
195 : :
196 : : /* Construct req structure. Basically we expect dn and attr to
197 : : * be new line separated attribute lists, each line containing
198 : : * attribute=value pair (i.e. separated by first `='. Parse
199 : : * the strings and add items one by one.
200 : : */
201 : :
202 : : #define LINESEP "|\015\012"
203 : :
204 : : /* Called by: keygen */
205 : : static X509_REQ* populate_request(const unsigned char* dn, const unsigned char* attr)
206 : 2 : {
207 : 2 : char* p = NULL;
208 : : char* t; /* type, see objects.h LN macros for possibilities */
209 : : char* v; /* value */
210 : : int nid;
211 : 2 : X509_REQ* req = NULL;
212 : : X509_REQ_INFO* ri;
213 : :
214 : : LOG_PRINT("populate");
215 [ + - ]: 2 : if (!dn) goto err;
216 [ - + ]: 2 : if (!(req=X509_REQ_new())) GOTO_ERR("no memory?");
217 : 2 : ri=req->req_info;
218 : :
219 : : LOG_PRINT("populate: set version");
220 : : /* setup version number */
221 [ - + ]: 2 : if (!ASN1_INTEGER_set(ri->version,0L /*version 1*/))
222 : 0 : GOTO_ERR("ASN1_INTEGER_set");
223 : :
224 : : /* Add fields of distinguished name. strtok() alters the buffer,
225 : : * and so do I, so lets get some fresh memory to play with. */
226 : :
227 [ - + ]: 2 : if (!(p = strdup((const char*)dn))) GOTO_ERR("no memory?");
228 : :
229 : : /*Log_malloc = Log2;*/
230 : : LOG_PRINT("populate: distinguished name");
231 : :
232 [ + + ]: 6 : for (t = strtok(p, LINESEP); t; t = strtok(NULL, LINESEP)) {
233 : : LOG_PRINT2("populate strtok returned '%s'",t);
234 [ - + ]: 4 : if (!(v = strchr(t, '='))) GOTO_ERR("09 missing `=' in DN attribute spec");
235 : : /* *** assumes strtok() has already scanned past this char */
236 : 4 : *(v++)='\0';
237 : : LOG_PRINT3("DN: %s=%s",t,v);
238 : : /* If OBJ not recognised ignore it */
239 [ - + ]: 4 : if ((nid=OBJ_txt2nid(t)) == NID_undef)
240 : 0 : GOTO_ERR("06 Unregistered DN attribute name (OBJ_txt2nid)");
241 : : LOG_PRINT2("NID %x",nid);
242 [ + - ]: 4 : if (!add_DN_object(ri->subject,nid,(unsigned char*)v)) goto err;
243 : : LOG_PRINT("DN object added");
244 : : }
245 : 2 : OPENSSL_free(p);
246 : 2 : p = NULL;
247 [ - + ]: 2 : if (!attr) return req;
248 : :
249 : : LOG_PRINT("populate: attributes");
250 : :
251 : : /* Add attribute fields */
252 : :
253 [ + - ]: 2 : if (!(p = strdup((const char*)attr))) goto err;
254 [ + + ]: 4 : for (t = strtok(p, LINESEP); t; t = strtok(NULL, LINESEP)) {
255 [ - + ]: 2 : if (!(v = strchr(t, '='))) GOTO_ERR("09 missing `=' in attribute spec");
256 : : /* *** assumes strtok() has already scanned past this char */
257 : 2 : *(v++)='\0';
258 : : /* If OBJ not recognised ignore it */
259 [ - + ]: 2 : if ((nid=OBJ_txt2nid(t)) == NID_undef)
260 : 0 : GOTO_ERR("07 Unregistered attribute name (OBJ_txt2nid)");
261 : : LOG_PRINT3("attr: %s=%s",t,v);
262 [ + - ]: 2 : if (!add_attribute_object(ri->attributes, nid, (unsigned char*)v))
263 : 0 : goto err;
264 : : }
265 : 2 : OPENSSL_free(p);
266 : : LOG_PRINT("populate: done");
267 : 2 : return req;
268 : 0 : err:
269 [ # # ]: 0 : if (p) OPENSSL_free(p);
270 [ # # ]: 0 : if (req) X509_REQ_free(req);
271 : : LOG_PRINT("populate: error");
272 : 0 : return NULL;
273 : : }
274 : :
275 : : /* ============= K E Y G E N E R A T I O N ==============*/
276 : :
277 : : /* char** values are out parameters. They return malloced values.
278 : : * passowrd is the password used
279 : : * to encrypt the private key. identifiaction is a newline separated list
280 : : * of attributes to include in certification request. Each line has
281 : : * format: `name=value\n'
282 : : */
283 : :
284 : : /* Called by: smime_keygen */
285 : : int keygen(const char* dn, const char* attr, const char* comment,
286 : : EVP_PKEY** pkey_out,
287 : : X509** x509ss_out,
288 : : X509_REQ** req_out)
289 : 2 : {
290 : : time_t t;
291 : 2 : X509* x509ss=NULL;
292 : 2 : X509_REQ* req=NULL;
293 : 2 : EVP_PKEY* pkey=NULL;
294 : 2 : EVP_PKEY* tmp_pkey=NULL;
295 : 2 : RSA* rsa=NULL;
296 : 2 : int ret = -1;
297 : :
298 [ + - ]: 2 : if (pkey_out) *pkey_out = NULL;
299 [ + - ]: 2 : if (x509ss_out) *x509ss_out = NULL;
300 [ + - ]: 2 : if (req_out) *req_out = NULL;
301 : 2 : X509V3_add_standard_extensions();
302 : :
303 : : LOG_PRINT("keygen start");
304 : :
305 : 2 : t = time(NULL);
306 : 2 : RAND_seed(&t,sizeof(t));
307 : : #ifdef WINDOWS
308 : : RAND_screen(); /* Loading video display memory into random state */
309 : : #endif
310 : :
311 : : /* Here's the beef */
312 : :
313 [ - + ]: 2 : if (!(pkey=EVP_PKEY_new())) GOTO_ERR("no memory?");
314 : : LOG_PRINT("keygen preparing rsa key");
315 [ - + ]: 2 : if (!(rsa = RSA_generate_key(1024 /*bits*/, 0x10001 /*65537*/,
316 : : NULL /*req_cb*/, NULL /*arg*/)))
317 : 0 : GOTO_ERR("RSA_generate_key");
318 : : LOG_PRINT("keygen rsa key generated");
319 [ - + ]: 2 : if (!EVP_PKEY_assign_RSA(pkey, rsa)) GOTO_ERR("EVP_PKEY_assign_RSA");
320 [ + - ]: 2 : if (pkey_out) *pkey_out = pkey;
321 : :
322 : 2 : t = time(NULL);
323 : 2 : RAND_seed(&t,sizeof(t));
324 : 2 : RAND_write_file(randomfile); /* Key generation is a big operation. Write
325 : : in the new random state. */
326 : :
327 : : /* ============================================================
328 : : * Now handle the public key part, i.e. create self signed and
329 : : * certificate request. This starts by making a request that
330 : : * contains all relevant fields.
331 : : */
332 : :
333 : : LOG_PRINT3("keygen populating request '%s' '%s'", dn, attr);
334 [ - + ]: 2 : if (!(req=populate_request((const unsigned char*)dn,
335 : 0 : (const unsigned char*)attr))) goto err;
336 : : LOG_PRINT("keygen request populated");
337 : 2 : X509_REQ_set_pubkey(req,pkey);
338 : : /*req->req_info->req_kludge=0; / * no asn1 kludge *** filed deleted as of 0.9.7b?!? */
339 : :
340 [ + - ]: 2 : if (req_out) {
341 : : LOG_PRINT("keygen signing request");
342 [ - + ]: 2 : if (!(X509_REQ_sign(req, pkey, EVP_md5()))) GOTO_ERR("X509_REQ_sign");
343 : : LOG_PRINT("keygen request signed");
344 : 2 : *req_out = req;
345 : : }
346 : :
347 : : #if 1
348 : : /* -- X509 create self signed certificate */
349 : :
350 [ + - ]: 2 : if (x509ss_out) {
351 : : LOG_PRINT("keygen making x509");
352 [ - + ]: 2 : if (!(x509ss=X509_new())) GOTO_ERR("no memory?");
353 : :
354 : : /* Set version to V3 and serial number to zero */
355 [ - + ]: 2 : if(!X509_set_version(x509ss, 2)) GOTO_ERR("X509_set_version");
356 : 2 : ASN1_INTEGER_set(X509_get_serialNumber(x509ss),0L);
357 : : LOG_PRINT("keygen setting various x509 fields");
358 : :
359 : 2 : X509_set_issuer_name(x509ss,
360 : : X509_REQ_get_subject_name(req));
361 : 2 : X509_gmtime_adj(X509_get_notBefore(x509ss),0);
362 : 2 : X509_gmtime_adj(X509_get_notAfter(x509ss),
363 : : (long)60*60*24*365 /*days*/);
364 : 2 : X509_set_subject_name(x509ss,
365 : : X509_REQ_get_subject_name(req));
366 : :
367 : : LOG_PRINT("keygen setting x509 attributes");
368 [ - + ]: 2 : if (!(tmp_pkey =X509_REQ_get_pubkey(req))) GOTO_ERR("X509_REQ_get_pubkey");
369 : 2 : X509_set_pubkey(x509ss,tmp_pkey);
370 : 2 : EVP_PKEY_free(tmp_pkey);
371 : 2 : tmp_pkey = NULL;
372 : :
373 : : /* Set up V3 context struct and add certificate extensions. Note
374 : : * that we need to add (full) suite of CA extensions, otherwise
375 : : * our cert is not valid for signing itself.
376 : : */
377 : :
378 [ - + ]: 2 : if (add_some_X509v3_extensions(x509ss,
379 : : "CA:TRUE,pathlen:3", /*basic_constraints*/
380 : : "client,server,email,objsign,sslCA,emailCA,objCA", /*cert_type*/
381 : : "digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign", /*key_usage*/
382 : 0 : comment)==-1) goto err;
383 : :
384 : : LOG_PRINT("keygen signing x509");
385 [ - + ]: 2 : if (!(X509_sign(x509ss, pkey, EVP_md5()))) GOTO_ERR("X509_sign");
386 : : LOG_PRINT("keygen x509 ready");
387 : 2 : *x509ss_out = x509ss;
388 : : }
389 : : #endif
390 : :
391 : 2 : ret = 0;
392 : :
393 : 2 : err:
394 : : /*if (tmp_pkey) EVP_PKEY_free(tmp_pkey); never happens */
395 [ + - - + ]: 2 : if (pkey && !pkey_out) EVP_PKEY_free(pkey);
396 [ + - - + ]: 2 : if (req && !req_out) X509_REQ_free(req);
397 [ + - - + ]: 2 : if (x509ss && !x509ss_out) X509_free(x509ss);
398 : 2 : X509V3_EXT_cleanup();
399 : 2 : OBJ_cleanup();
400 : : LOG_PRINT("keygen done.");
401 : 2 : return ret;
402 : : }
403 : :
404 : : /* Called by: main */
405 : : int smime_keygen(const char* dn, const char* attr, const char* passwd, const char* comment, char** priv_out, char** x509ss_out, char** request_out)
406 : 2 : {
407 : 2 : X509* x509ss=NULL;
408 : 2 : X509_REQ* req=NULL;
409 : 2 : EVP_PKEY* pkey=NULL;
410 : 2 : int ret = -1;
411 : :
412 [ + - ]: 2 : if (priv_out) *priv_out = NULL;
413 [ + - ]: 2 : if (x509ss_out) *x509ss_out = NULL;
414 [ + - ]: 2 : if (request_out) *request_out = NULL;
415 : :
416 [ + - ]: 2 : if (keygen(dn, attr, comment, &pkey, &x509ss, &req) == -1) goto err;
417 : :
418 : : /* Write private key to file. While its being
419 : : * written, it will also get encrypted. */
420 : :
421 [ + - + - ]: 2 : if (passwd && priv_out) {
422 [ + - ]: 2 : if (write_private_key(pkey, passwd, priv_out) == -1) goto err;
423 : 2 : EVP_PKEY_free(pkey); /* free early so memory can be reused */
424 : 2 : pkey = NULL;
425 : : }
426 : :
427 [ + - ]: 2 : if (request_out) {
428 [ + - ]: 2 : if (write_request(req, request_out) == -1) goto err;
429 : 2 : X509_REQ_free(req); /* free early so memory can be reused */
430 : 2 : req = NULL;
431 : : }
432 : :
433 [ + - ]: 2 : if (x509ss_out) {
434 [ + - ]: 2 : if (write_certificate(x509ss, x509ss_out)==-1) goto err;
435 : : }
436 : :
437 : 2 : ret = 0;
438 : :
439 : 2 : err:
440 [ - + ]: 2 : if (pkey) EVP_PKEY_free(pkey);
441 [ - + ]: 2 : if (req) X509_REQ_free(req);
442 [ + - ]: 2 : if (x509ss) X509_free(x509ss);
443 : 2 : return ret;
444 : : }
445 : :
446 : : /* EOF - keygen.c */
|