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 
req_fix_data(int nid,int * type)110 static int req_fix_data(int nid, int *type /*IN-OUT*/)
111 {
112   switch (nid) {
113   case NID_pkcs9_emailAddress: *type=V_ASN1_IA5STRING; break;
114   case NID_commonName:
115   case NID_pkcs9_challengePassword:
116     if (*type == V_ASN1_IA5STRING)
117       *type=V_ASN1_T61STRING;
118     break;
119   case NID_pkcs9_unstructuredName:
120     if (*type == V_ASN1_T61STRING) {
121       GOTO_ERR("08 invalid characters in attribute value string");
122     } else
123       *type=V_ASN1_IA5STRING;
124     break;
125   }
126 
127   return 1;
128 err:
129   return 0;
130 }
131 
132 /* Add an entry to distinguished name */
133 
134 /* Called by:  populate_request */
add_DN_object(X509_NAME * n,int nid,unsigned char * val)135 static int add_DN_object(X509_NAME *n, int nid, unsigned char *val)
136 {
137   X509_NAME_ENTRY *ne=NULL;
138   int type = ASN1_PRINTABLE_type(val,-1 /* uses strlen() */);
139 
140   if (req_fix_data(nid, &type) == 0) goto err;
141 
142   if (!(ne=X509_NAME_ENTRY_create_by_NID(NULL, nid, type,
143 					 val, strlen((char*)val))))
144     GOTO_ERR("X509_NAME_ENTRY_create_by_NID");
145   if (!X509_NAME_add_entry(n,ne,X509_NAME_entry_count(n),0))
146     GOTO_ERR("X509_NAME_add_entry");
147 
148   X509_NAME_ENTRY_free(ne);
149   return 1;
150 err:
151   if (ne != NULL) X509_NAME_ENTRY_free(ne);
152   return 0;
153 }
154 
155 /* attribute objects are more complicated because they can be multivalued */
156 
157 /* Called by: */
add_attribute_object(STACK_OF (X509_ATTRIBUTE)* n,int nid,unsigned char * val)158 static int add_attribute_object(STACK_OF(X509_ATTRIBUTE) *n, int nid, unsigned char *val)
159 {
160   X509_ATTRIBUTE *xa=NULL;
161   ASN1_BIT_STRING *bs=NULL;
162   ASN1_TYPE *at=NULL;
163 
164   /* add object plus value */
165   if ((xa=X509_ATTRIBUTE_new()) == NULL) GOTO_ERR("no memory?");
166   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   if (xa->object != NULL) ASN1_OBJECT_free(xa->object);
170   xa->object=OBJ_nid2obj(nid);
171 
172   if ((bs=ASN1_BIT_STRING_new()) == NULL) GOTO_ERR("no memory?");
173   bs->type=ASN1_PRINTABLE_type(val,-1 /* use strlen() */);
174 
175   if (!req_fix_data(nid,&bs->type)) goto err;
176 
177   if (!ASN1_STRING_set(bs,val,strlen((char*)val)+1)) GOTO_ERR("no memory?");
178   if ((at=ASN1_TYPE_new()) == NULL) GOTO_ERR("no memory?");
179 
180   ASN1_TYPE_set(at,bs->type,(char *)bs);
181   sk_ASN1_TYPE_push(xa->value.set,at);
182   bs=NULL;
183   at=NULL;
184   /* only one item per attribute */
185 
186   if (!sk_X509_ATTRIBUTE_push(n,xa))
187     GOTO_ERR("sk_X509_ATTRIBUTE_push (no memory?)");
188   return 1;
189 err:
190   if (xa != NULL) X509_ATTRIBUTE_free(xa);
191   if (at != NULL) ASN1_TYPE_free(at);
192   if (bs != NULL) ASN1_BIT_STRING_free(bs);
193   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 */
populate_request(const unsigned char * dn,const unsigned char * attr)205 static X509_REQ* populate_request(const unsigned char* dn, const unsigned char* attr)
206 {
207   char* p = NULL;
208   char* t;  /* type, see objects.h LN macros for possibilities */
209   char* v;  /* value */
210   int nid;
211   X509_REQ* req = NULL;
212   X509_REQ_INFO* ri;
213 
214   LOG_PRINT("populate");
215   if (!dn) goto err;
216   if (!(req=X509_REQ_new())) GOTO_ERR("no memory?");
217   ri=req->req_info;
218 
219   LOG_PRINT("populate: set version");
220   /* setup version number */
221   if (!ASN1_INTEGER_set(ri->version,0L /*version 1*/))
222     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   if (!(p = strdup((const char*)dn))) GOTO_ERR("no memory?");
228 
229   /*Log_malloc = Log2;*/
230   LOG_PRINT("populate: distinguished name");
231 
232   for (t = strtok(p, LINESEP); t; t = strtok(NULL, LINESEP)) {
233     LOG_PRINT2("populate strtok returned '%s'",t);
234     if (!(v = strchr(t, '='))) GOTO_ERR("09 missing `=' in DN attribute spec");
235     /* *** assumes strtok() has already scanned past this char */
236     *(v++)='\0';
237     LOG_PRINT3("DN: %s=%s",t,v);
238     /* If OBJ not recognised ignore it */
239     if ((nid=OBJ_txt2nid(t)) == NID_undef)
240       GOTO_ERR("06 Unregistered DN attribute name (OBJ_txt2nid)");
241     LOG_PRINT2("NID %x",nid);
242     if (!add_DN_object(ri->subject,nid,(unsigned char*)v)) goto err;
243     LOG_PRINT("DN object added");
244   }
245   OPENSSL_free(p);
246   p = NULL;
247   if (!attr) return req;
248 
249   LOG_PRINT("populate: attributes");
250 
251   /* Add attribute fields */
252 
253   if (!(p = strdup((const char*)attr))) goto err;
254   for (t = strtok(p, LINESEP); t; t = strtok(NULL, LINESEP)) {
255     if (!(v = strchr(t, '='))) GOTO_ERR("09 missing `=' in attribute spec");
256     /* *** assumes strtok() has already scanned past this char */
257     *(v++)='\0';
258     /* If OBJ not recognised ignore it */
259     if ((nid=OBJ_txt2nid(t)) == NID_undef)
260       GOTO_ERR("07 Unregistered attribute name (OBJ_txt2nid)");
261     LOG_PRINT3("attr: %s=%s",t,v);
262     if (!add_attribute_object(ri->attributes, nid, (unsigned char*)v))
263       goto err;
264   }
265   OPENSSL_free(p);
266   LOG_PRINT("populate: done");
267   return req;
268 err:
269   if (p) OPENSSL_free(p);
270   if (req) X509_REQ_free(req);
271   LOG_PRINT("populate: error");
272   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 */
keygen(const char * dn,const char * attr,const char * comment,EVP_PKEY ** pkey_out,X509 ** x509ss_out,X509_REQ ** req_out)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 {
290   time_t t;
291   X509*     x509ss=NULL;
292   X509_REQ* req=NULL;
293   EVP_PKEY* pkey=NULL;
294   EVP_PKEY* tmp_pkey=NULL;
295   RSA*      rsa=NULL;
296   int ret = -1;
297 
298   if (pkey_out) *pkey_out = NULL;
299   if (x509ss_out) *x509ss_out = NULL;
300   if (req_out) *req_out = NULL;
301   X509V3_add_standard_extensions();
302 
303   LOG_PRINT("keygen start");
304 
305   t = time(NULL);
306   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   if (!(pkey=EVP_PKEY_new())) GOTO_ERR("no memory?");
314   LOG_PRINT("keygen preparing rsa key");
315   if (!(rsa = RSA_generate_key(1024 /*bits*/, 0x10001 /*65537*/,
316 			       NULL /*req_cb*/, NULL /*arg*/)))
317      GOTO_ERR("RSA_generate_key");
318   LOG_PRINT("keygen rsa key generated");
319   if (!EVP_PKEY_assign_RSA(pkey, rsa)) GOTO_ERR("EVP_PKEY_assign_RSA");
320   if (pkey_out) *pkey_out = pkey;
321 
322   t = time(NULL);
323   RAND_seed(&t,sizeof(t));
324   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   if (!(req=populate_request((const unsigned char*)dn,
335 			     (const unsigned char*)attr))) goto err;
336   LOG_PRINT("keygen request populated");
337   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   if (req_out) {
341     LOG_PRINT("keygen signing request");
342 #if 0
343   if (!(X509_REQ_sign(req, pkey, EVP_md5()))) GOTO_ERR("X509_REQ_sign");
344 #else
345   if (!(X509_REQ_sign(req, pkey, EVP_sha256()))) GOTO_ERR("X509_REQ_sign");
346 #endif
347     LOG_PRINT("keygen request signed");
348     *req_out = req;
349   }
350 
351 #if 1
352   /* -- X509 create self signed certificate */
353 
354   if (x509ss_out) {
355     LOG_PRINT("keygen making x509");
356     if (!(x509ss=X509_new())) GOTO_ERR("no memory?");
357 
358     /* Set version to V3 and serial number to zero */
359     if(!X509_set_version(x509ss, 2)) GOTO_ERR("X509_set_version");
360     ASN1_INTEGER_set(X509_get_serialNumber(x509ss),0L);
361     LOG_PRINT("keygen setting various x509 fields");
362 
363     X509_set_issuer_name(x509ss,
364 			 X509_REQ_get_subject_name(req));
365     X509_gmtime_adj(X509_get_notBefore(x509ss),0);
366     X509_gmtime_adj(X509_get_notAfter(x509ss),
367 		    (long)60*60*24*365 /*days*/);
368     X509_set_subject_name(x509ss,
369 			  X509_REQ_get_subject_name(req));
370 
371     LOG_PRINT("keygen setting x509 attributes");
372     if (!(tmp_pkey =X509_REQ_get_pubkey(req))) GOTO_ERR("X509_REQ_get_pubkey");
373     X509_set_pubkey(x509ss,tmp_pkey);
374     EVP_PKEY_free(tmp_pkey);
375     tmp_pkey = NULL;
376 
377     /* Set up V3 context struct and add certificate extensions. Note
378      * that we need to add (full) suite of CA extensions, otherwise
379      * our cert is not valid for signing itself.
380      */
381 
382     if (add_some_X509v3_extensions(x509ss,
383 				   "CA:TRUE,pathlen:3", /*basic_constraints*/
384 				   "client,server,email,objsign,sslCA,emailCA,objCA", /*cert_type*/
385 				   "digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign", /*key_usage*/
386 				   comment)==-1) goto err;
387 
388     LOG_PRINT("keygen signing x509");
389 #if 0
390     if (!(X509_sign(x509ss, pkey, EVP_md5()))) GOTO_ERR("X509_sign");
391 #else
392     if (!(X509_sign(x509ss, pkey, EVP_sha256()))) GOTO_ERR("X509_sign");
393 #endif
394     LOG_PRINT("keygen x509 ready");
395     *x509ss_out = x509ss;
396   }
397 #endif
398 
399   ret = 0;
400 
401 err:
402   /*if (tmp_pkey)            EVP_PKEY_free(tmp_pkey); never happens */
403   if (pkey   && !pkey_out)   EVP_PKEY_free(pkey);
404   if (req    && !req_out)    X509_REQ_free(req);
405   if (x509ss && !x509ss_out) X509_free(x509ss);
406   X509V3_EXT_cleanup();
407   OBJ_cleanup();
408   LOG_PRINT("keygen done.");
409   return ret;
410 }
411 
412 /* Called by:  main */
smime_keygen(const char * dn,const char * attr,const char * passwd,const char * comment,char ** priv_out,char ** x509ss_out,char ** request_out)413 int smime_keygen(const char* dn, const char* attr, const char* passwd, const char* comment, char** priv_out, char** x509ss_out, char** request_out)
414 {
415   X509*     x509ss=NULL;
416   X509_REQ* req=NULL;
417   EVP_PKEY* pkey=NULL;
418   int ret = -1;
419 
420   if (priv_out) *priv_out = NULL;
421   if (x509ss_out) *x509ss_out = NULL;
422   if (request_out) *request_out = NULL;
423 
424   if (keygen(dn, attr, comment, &pkey, &x509ss, &req) == -1) goto err;
425 
426   /* Write private key to file. While its being
427    * written, it will also get encrypted. */
428 
429   if (passwd && priv_out) {
430     if (write_private_key(pkey, passwd, priv_out) == -1) goto err;
431     EVP_PKEY_free(pkey);  /* free early so memory can be reused */
432     pkey = NULL;
433   }
434 
435   if (request_out) {
436     if (write_request(req, request_out) == -1) goto err;
437     X509_REQ_free(req);  /* free early so memory can be reused */
438     req = NULL;
439   }
440 
441   if (x509ss_out) {
442     if (write_certificate(x509ss, x509ss_out)==-1) goto err;
443   }
444 
445   ret = 0;
446 
447 err:
448   if (pkey)   EVP_PKEY_free(pkey);
449   if (req)    X509_REQ_free(req);
450   if (x509ss) X509_free(x509ss);
451   return ret;
452 }
453 
454 /* EOF  -  keygen.c */
455