e0ab213ea24427ac5681f0e5284d5e5837c4e613
[strongswan.git] / src / pki / commands / req.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Copyright (C) 2009-2015 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * HSR Hochschule fuer Technik Rapperswil
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include <time.h>
20 #include <errno.h>
21
22 #include "pki.h"
23
24 #include <collections/linked_list.h>
25 #include <credentials/certificates/certificate.h>
26
27 /**
28 * Create a self-signed PKCS#10 certificate requesst.
29 */
30 static int req()
31 {
32 cred_encoding_type_t form = CERT_ASN1_DER;
33 key_type_t type = KEY_ANY;
34 hash_algorithm_t digest = HASH_UNKNOWN;
35 certificate_t *cert = NULL;
36 private_key_t *private = NULL;
37 char *file = NULL, *keyid = NULL, *dn = NULL, *error = NULL;
38 identification_t *id = NULL;
39 linked_list_t *san;
40 chunk_t encoding = chunk_empty;
41 chunk_t challenge_password = chunk_empty;
42 char *arg;
43
44 san = linked_list_create();
45
46 while (TRUE)
47 {
48 switch (command_getopt(&arg))
49 {
50 case 'h':
51 goto usage;
52 case 't':
53 if (streq(arg, "rsa"))
54 {
55 type = KEY_RSA;
56 }
57 else if (streq(arg, "ecdsa"))
58 {
59 type = KEY_ECDSA;
60 }
61 else if (streq(arg, "bliss"))
62 {
63 type = KEY_BLISS;
64 }
65 else if (streq(arg, "priv"))
66 {
67 type = KEY_ANY;
68 }
69 else
70 {
71 error = "invalid input type";
72 goto usage;
73 }
74 continue;
75 case 'g':
76 if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
77 {
78 error = "invalid --digest type";
79 goto usage;
80 }
81 continue;
82 case 'i':
83 file = arg;
84 continue;
85 case 'd':
86 dn = arg;
87 continue;
88 case 'a':
89 san->insert_last(san, identification_create_from_string(arg));
90 continue;
91 case 'p':
92 challenge_password = chunk_create(arg, strlen(arg));
93 continue;
94 case 'f':
95 if (!get_form(arg, &form, CRED_CERTIFICATE))
96 {
97 error = "invalid output format";
98 goto usage;
99 }
100 continue;
101 case 'x':
102 keyid = arg;
103 continue;
104 case EOF:
105 break;
106 default:
107 error = "invalid --req option";
108 goto usage;
109 }
110 break;
111 }
112
113 if (!dn)
114 {
115 error = "--dn is required";
116 goto usage;
117 }
118 id = identification_create_from_string(dn);
119 if (id->get_type(id) != ID_DER_ASN1_DN)
120 {
121 error = "supplied --dn is not a distinguished name";
122 goto end;
123 }
124 if (file)
125 {
126 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
127 BUILD_FROM_FILE, file, BUILD_END);
128 }
129 else if (keyid)
130 {
131 chunk_t chunk;
132
133 chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
134 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
135 BUILD_PKCS11_KEYID, chunk, BUILD_END);
136 free(chunk.ptr);
137 }
138 else
139 {
140 chunk_t chunk;
141
142 set_file_mode(stdin, CERT_ASN1_DER);
143 if (!chunk_from_fd(0, &chunk))
144 {
145 fprintf(stderr, "reading private key failed: %s\n", strerror(errno));
146 error = "";
147 goto end;
148 }
149 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
150 BUILD_BLOB, chunk, BUILD_END);
151 free(chunk.ptr);
152 }
153 if (!private)
154 {
155 error = "parsing private key failed";
156 goto end;
157 }
158 if (digest == HASH_UNKNOWN)
159 {
160 digest = get_default_digest(private);
161 }
162 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_PKCS10_REQUEST,
163 BUILD_SIGNING_KEY, private,
164 BUILD_SUBJECT, id,
165 BUILD_SUBJECT_ALTNAMES, san,
166 BUILD_CHALLENGE_PWD, challenge_password,
167 BUILD_DIGEST_ALG, digest,
168 BUILD_END);
169 if (!cert)
170 {
171 error = "generating certificate request failed";
172 goto end;
173 }
174 if (!cert->get_encoding(cert, form, &encoding))
175 {
176 error = "encoding certificate request failed";
177 goto end;
178 }
179 set_file_mode(stdout, form);
180 if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
181 {
182 error = "writing certificate request failed";
183 goto end;
184 }
185
186 end:
187 DESTROY_IF(id);
188 DESTROY_IF(cert);
189 DESTROY_IF(private);
190 san->destroy_offset(san, offsetof(identification_t, destroy));
191 free(encoding.ptr);
192
193 if (error)
194 {
195 fprintf(stderr, "%s\n", error);
196 return 1;
197 }
198 return 0;
199
200 usage:
201 san->destroy_offset(san, offsetof(identification_t, destroy));
202 return command_usage(error);
203 }
204
205 /**
206 * Register the command.
207 */
208 static void __attribute__ ((constructor))reg()
209 {
210 command_register((command_t) {
211 req, 'r', "req",
212 "create a PKCS#10 certificate request",
213 {" [--in file|--keyid hex] [--type rsa|ecdsa|bliss|priv] --dn distinguished-name",
214 "[--san subjectAltName]+ [--password challengePassword]",
215 "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
216 "[--outform der|pem]"},
217 {
218 {"help", 'h', 0, "show usage information"},
219 {"in", 'i', 1, "private key input file, default: stdin"},
220 {"keyid", 'x', 1, "keyid on smartcard of private key"},
221 {"type", 't', 1, "type of input key, default: priv"},
222 {"dn", 'd', 1, "subject distinguished name"},
223 {"san", 'a', 1, "subjectAltName to include in cert request"},
224 {"password",'p', 1, "challengePassword to include in cert request"},
225 {"digest", 'g', 1, "digest for signature creation, default: key-specific"},
226 {"outform", 'f', 1, "encoding of generated request, default: der"},
227 }
228 });
229 }