Extract PKCS#5 handling from pkcs8 plugin to separate helper class
[strongswan.git] / src / libstrongswan / plugins / pkcs8 / pkcs8_builder.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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 "pkcs8_builder.h"
17
18 #include <utils/debug.h>
19 #include <asn1/oid.h>
20 #include <asn1/asn1.h>
21 #include <asn1/asn1_parser.h>
22 #include <crypto/pkcs5.h>
23 #include <credentials/keys/private_key.h>
24
25 /**
26 * ASN.1 definition of a privateKeyInfo structure
27 */
28 static const asn1Object_t pkinfoObjects[] = {
29 { 0, "privateKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
30 { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */
31 { 1, "privateKeyAlgorithm", ASN1_EOC, ASN1_RAW }, /* 2 */
32 { 1, "privateKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */
33 { 1, "attributes", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 4 */
34 { 1, "end opt", ASN1_EOC, ASN1_END }, /* 5 */
35 { 0, "exit", ASN1_EOC, ASN1_EXIT }
36 };
37 #define PKINFO_PRIVATE_KEY_ALGORITHM 2
38 #define PKINFO_PRIVATE_KEY 3
39
40 /**
41 * Load a generic private key from an ASN.1 encoded blob
42 */
43 static private_key_t *parse_private_key(chunk_t blob)
44 {
45 asn1_parser_t *parser;
46 chunk_t object, params = chunk_empty;
47 int objectID;
48 private_key_t *key = NULL;
49 key_type_t type = KEY_ANY;
50
51 parser = asn1_parser_create(pkinfoObjects, blob);
52 parser->set_flags(parser, FALSE, TRUE);
53
54 while (parser->iterate(parser, &objectID, &object))
55 {
56 switch (objectID)
57 {
58 case PKINFO_PRIVATE_KEY_ALGORITHM:
59 {
60 int oid = asn1_parse_algorithmIdentifier(object,
61 parser->get_level(parser) + 1, &params);
62
63 switch (oid)
64 {
65 case OID_RSA_ENCRYPTION:
66 type = KEY_RSA;
67 break;
68 case OID_EC_PUBLICKEY:
69 type = KEY_ECDSA;
70 break;
71 default:
72 /* key type not supported */
73 goto end;
74 }
75 break;
76 }
77 case PKINFO_PRIVATE_KEY:
78 {
79 DBG2(DBG_ASN, "-- > --");
80 if (params.ptr)
81 {
82 key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
83 type, BUILD_BLOB_ALGID_PARAMS,
84 params, BUILD_BLOB_ASN1_DER,
85 object, BUILD_END);
86 }
87 else
88 {
89 key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
90 type, BUILD_BLOB_ASN1_DER, object,
91 BUILD_END);
92 }
93 DBG2(DBG_ASN, "-- < --");
94 break;
95 }
96 }
97 }
98
99 end:
100 parser->destroy(parser);
101 return key;
102 }
103
104 /**
105 * Try to decrypt the given blob with multiple passwords using the given
106 * pkcs5 object.
107 */
108 static private_key_t *decrypt_private_key(pkcs5_t *pkcs5, chunk_t blob)
109 {
110 enumerator_t *enumerator;
111 shared_key_t *shared;
112 private_key_t *private_key = NULL;
113
114 enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
115 SHARED_PRIVATE_KEY_PASS, NULL, NULL);
116 while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
117 {
118 chunk_t decrypted;
119
120 if (!pkcs5->decrypt(pkcs5, shared->get_key(shared), blob, &decrypted))
121 {
122 continue;
123 }
124 private_key = parse_private_key(decrypted);
125 if (private_key)
126 {
127 chunk_clear(&decrypted);
128 break;
129 }
130 chunk_free(&decrypted);
131 }
132 enumerator->destroy(enumerator);
133
134 return private_key;
135 }
136
137 /**
138 * ASN.1 definition of an encryptedPrivateKeyInfo structure
139 */
140 static const asn1Object_t encryptedPKIObjects[] = {
141 { 0, "encryptedPrivateKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
142 { 1, "encryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 1 */
143 { 1, "encryptedData", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */
144 { 0, "exit", ASN1_EOC, ASN1_EXIT }
145 };
146 #define EPKINFO_ENCRYPTION_ALGORITHM 1
147 #define EPKINFO_ENCRYPTED_DATA 2
148
149 /**
150 * Load an encrypted private key from an ASN.1 encoded blob
151 * Schemes per PKCS#5 (RFC 2898)
152 */
153 static private_key_t *parse_encrypted_private_key(chunk_t blob)
154 {
155 asn1_parser_t *parser;
156 chunk_t object;
157 int objectID;
158 private_key_t *key = NULL;
159 pkcs5_t *pkcs5 = NULL;
160
161 parser = asn1_parser_create(encryptedPKIObjects, blob);
162
163 while (parser->iterate(parser, &objectID, &object))
164 {
165 switch (objectID)
166 {
167 case EPKINFO_ENCRYPTION_ALGORITHM:
168 {
169 pkcs5 = pkcs5_from_algorithmIdentifier(object,
170 parser->get_level(parser) + 1);
171 if (!pkcs5)
172 {
173 goto end;
174 }
175 break;
176 }
177 case EPKINFO_ENCRYPTED_DATA:
178 {
179 key = decrypt_private_key(pkcs5, object);
180 break;
181 }
182 }
183 }
184
185 end:
186 DESTROY_IF(pkcs5);
187 parser->destroy(parser);
188 return key;
189 }
190
191 /**
192 * See header.
193 */
194 private_key_t *pkcs8_private_key_load(key_type_t type, va_list args)
195 {
196 chunk_t blob = chunk_empty;
197 private_key_t *key;
198
199 while (TRUE)
200 {
201 switch (va_arg(args, builder_part_t))
202 {
203 case BUILD_BLOB_ASN1_DER:
204 blob = va_arg(args, chunk_t);
205 continue;
206 case BUILD_END:
207 break;
208 default:
209 return NULL;
210 }
211 break;
212 }
213 /* we don't know whether it is encrypted or not, try both ways */
214 key = parse_encrypted_private_key(blob);
215 if (!key)
216 {
217 key = parse_private_key(blob);
218 }
219 return key;
220 }
221