Update SoftHSM v2.0 to the latest version
[aaf/sshsm.git] / SoftHSMv2 / src / bin / keyconv / softhsm2-keyconv.cpp
1 /*
2  * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 /************************************************************
28 *
29 * softhsm2-keyconv
30 *
31 * This program is for converting from BIND .private-key
32 * format to PKCS#8 key file format. So that keys can be
33 * imported from BIND to SoftHSM.
34 *
35 * Some of the design/code is from keyconv.c written by
36 * Hakan Olsson and Jakob Schlyter in 2000 and 2001.
37 *
38 ************************************************************/
39
40 #include <config.h>
41 #include "softhsm2-keyconv.h"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <getopt.h>
46 #include <string.h>
47 #ifndef _WIN32
48 #include <unistd.h>
49 #else
50 #include <io.h>
51 #define S_IRUSR 0400
52 #define S_IWUSR 0200
53 #define open _open
54 #define close _close
55 #endif
56 #include <iostream>
57 #include <fstream>
58 #include <stdint.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <fcntl.h>
63
64 void usage()
65 {
66         printf("Converting from BIND .private-key format to PKCS#8 key file format.\n");
67         printf("Usage: softhsm2-keyconv [OPTIONS]\n");
68         printf("Options:\n");
69         printf("  -h                  Shows this help screen.\n");
70         printf("  --help              Shows this help screen.\n");
71         printf("  --in <path>         The path to the input file.\n");
72         printf("  --out <path>        The path to the output file.\n");
73         printf("  --pin <PIN>         To encrypt PKCS#8 file. Optional.\n");
74         printf("  -v                  Show version info.\n");
75         printf("  --version           Show version info.\n");
76 }
77
78 // Give a number to each option
79 enum {
80         OPT_HELP = 0x100,
81         OPT_IN,
82         OPT_OUT,
83         OPT_PIN,
84         OPT_VERSION
85 };
86
87 // Define the options
88 static const struct option long_options[] = {
89         { "help",    0, NULL, OPT_HELP },
90         { "in",      1, NULL, OPT_IN },
91         { "out",     1, NULL, OPT_OUT },
92         { "pin",     1, NULL, OPT_PIN },
93         { "version", 0, NULL, OPT_VERSION },
94         { NULL,      0, NULL, 0 }
95 };
96
97 int main(int argc, char* argv[])
98 {
99         int option_index = 0;
100         int opt, result;
101
102         char* in_path = NULL;
103         char* out_path = NULL;
104         char* file_pin = NULL;
105
106         if (argc == 1)
107         {
108                 usage();
109                 exit(0);
110         }
111
112         while ((opt = getopt_long(argc, argv, "hv", long_options, &option_index)) != -1)
113         {
114                 switch (opt)
115                 {
116                         case OPT_IN:
117                                 in_path = optarg;
118                                 break;
119                         case OPT_OUT:
120                                 out_path = optarg;
121                                 break;
122                         case OPT_PIN:
123                                 file_pin = optarg;
124                                 break;
125                         case OPT_VERSION:
126                         case 'v':
127                                 printf("%s\n", PACKAGE_VERSION);
128                                 exit(0);
129                                 break;
130                         case OPT_HELP:
131                         case 'h':
132                         default:
133                                 usage();
134                                 exit(0);
135                                 break;
136                 }
137         }
138
139         // We should convert to PKCS#8
140         result = to_pkcs8(in_path, out_path, file_pin);
141
142         return result;
143 }
144
145 // Convert from BIND to PKCS#8
146 int to_pkcs8(char* in_path, char* out_path, char* file_pin)
147 {
148         FILE* file_pointer = NULL;
149         char line[MAX_LINE], data[MAX_LINE];
150         char* value_pointer = NULL;
151         int lineno = 0, m, n, error = 0, found, algorithm = DNS_KEYALG_ERROR, data_length;
152         uint32_t bitfield = 0;
153         key_material_t pkey[TAG_MAX];
154
155         if (in_path == NULL)
156         {
157                 fprintf(stderr, "ERROR: A path to the input file must be supplied. Use --in <path>\n");
158                 return 1;
159         }
160
161         if (out_path == NULL)
162         {
163                 fprintf(stderr, "ERROR: A path to the output file must be supplied. Use --out <path>\n");
164                 return 1;
165         }
166
167         file_pointer = fopen(in_path, "r");
168         if (file_pointer == NULL)
169         {
170                 fprintf(stderr, "ERROR: Could not open input file %.100s for reading.\n", in_path);
171                 return 1;
172         }
173
174         // Loop over all of the lines
175         while (fgets(line, MAX_LINE, file_pointer) != NULL)
176         {
177                 lineno++;
178
179                 // Find the current text field in the BIND file.
180                 for (m = 0, found = -1; found == -1 && file_tags[m]; m++)
181                 {
182                         if (strncasecmp(line, file_tags[m], strlen(file_tags[m])) == 0)
183                         {
184                                 found = m;
185                         }
186                 }
187
188                 // The text files is not recognized.
189                 if (found == -1)
190                 {
191                         fprintf(stderr, "ERROR: Unrecognized input line %i\n", lineno);
192                         fprintf(stderr, "ERROR: --> %s", line);
193                         continue;
194                 }
195
196                 // Point to the data for this text field.
197                 value_pointer = line + strlen(file_tags[found]) + 1;
198
199                 // Continue if we are at the end of the string
200                 if (*value_pointer == 0)
201                 {
202                         continue;
203                 }
204
205                 // Check that we do not get duplicates.
206                 if (bitfield & (1 << found))
207                 {
208                         fprintf(stderr, "ERROR: Duplicate \"%s\" field, line %i - ignored\n",
209                                         file_tags[found], lineno);
210                         continue;
211                 }
212                 bitfield |= (1 << found);
213
214                 // Handle the data for this text field.
215                 switch (found)
216                 {
217                         case TAG_VERSION:
218                                 if (sscanf(value_pointer, "v%i.%i", &m, &n) != 2)
219                                 {
220                                         fprintf(stderr, "ERROR: Invalid/unknown version string "
221                                                         "(%.100s).\n", value_pointer);
222                                         error = 1;
223                                         break;
224                                 }
225                                 if (m > FILE_MAJOR_VERSION || (m == FILE_MAJOR_VERSION && n > FILE_MINOR_VERSION))
226                                 {
227                                         fprintf(stderr, "ERROR: Cannot parse this version of file format, "
228                                                         "v%i.%i.\n", m, n);
229                                         error = 1;
230                                 }
231                                 break;
232                         case TAG_ALGORITHM:
233                                 algorithm = strtol(value_pointer, NULL, 10);
234                                 break;
235                         // RSA
236                         case TAG_MODULUS:
237                         case TAG_PUBEXP:
238                         case TAG_PRIVEXP:
239                         case TAG_PRIME1:
240                         case TAG_PRIME2:
241                         case TAG_EXP1:
242                         case TAG_EXP2:
243                         case TAG_COEFF:
244                         // DSA
245                         case TAG_PRIME:
246                         case TAG_SUBPRIME:
247                         case TAG_BASE:
248                         case TAG_PRIVVAL:
249                         case TAG_PUBVAL:
250                                 data_length = b64_pton(value_pointer, (unsigned char*)data, MAX_LINE);
251                                 if (data_length == -1)
252                                 {
253                                         error = 1;
254                                         fprintf(stderr, "ERROR: Could not parse the base64 string on line %i.\n", lineno);
255                                 }
256                                 else
257                                 {
258                                         pkey[found].big = malloc(data_length);
259                                         if (!pkey[found].big)
260                                         {
261                                                 fprintf(stderr, "ERROR: Could not allocate memory.\n");
262                                                 error = 1;
263                                                 break;
264                                         }
265                                         memcpy(pkey[found].big, data, data_length);
266                                         pkey[found].size = data_length;
267                                 }
268                                 break;
269                         // Do not need these
270                         case TAG_CREATED:
271                         case TAG_PUBLISH:
272                         case TAG_ACTIVATE:
273                         default:
274                                 break;
275                 }
276         }
277
278         fclose(file_pointer);
279
280         // Something went wrong. Clean up and quit.
281         if (error)
282         {
283                 free_key_material(pkey);
284                 return error;
285         }
286
287         // Create and set file permissions if the file does not exist.
288         int fd = open(out_path, O_CREAT, S_IRUSR | S_IWUSR);
289         if (fd == -1)
290         {
291                 fprintf(stderr, "ERROR: Could not open the output file: %s (errno %i)\n",
292                         out_path, errno);
293                 free_key_material(pkey);
294                 return 1;
295         }
296         ::close(fd);
297
298         crypto_init();
299
300         // Save the the key to the disk
301         switch (algorithm)
302         {
303                 case DNS_KEYALG_ERROR:
304                         fprintf(stderr, "ERROR: The algorithm %i was not given in the file.\n",
305                                         algorithm);
306                         error = 1;
307                         break;
308                 case DNS_KEYALG_RSAMD5:
309                 case DNS_KEYALG_RSASHA1:
310                 case DNS_KEYALG_RSASHA1_NSEC3_SHA1:
311                 case DNS_KEYALG_RSASHA256:
312                 case DNS_KEYALG_RSASHA512:
313                         error = save_rsa_pkcs8(out_path, file_pin, pkey);
314                         break;
315                 case DNS_KEYALG_DSA:
316                 case DNS_KEYALG_DSA_NSEC3_SHA1:
317                         error = save_dsa_pkcs8(out_path, file_pin, pkey);
318                         break;
319                 case DNS_KEYALG_ECC:
320                 case DNS_KEYALG_ECC_GOST:
321                 default:
322                         fprintf(stderr, "ERROR: The algorithm %i is not supported.\n",
323                                         algorithm);
324                         error = 1;
325                         break;
326         }
327
328         crypto_final();
329         free_key_material(pkey);
330
331         return error;
332 }
333
334 // Free allocated memory
335 void free_key_material(key_material_t* pkey)
336 {
337         int i;
338
339         if (!pkey)
340         {
341                 return;
342         }
343
344         for (i = 0; i < TAG_MAX; i++)
345         {
346                 if (pkey[i].big)
347                 {
348                         free(pkey[i].big);
349                 }
350         }
351 }