1 /**
2 * XML Security Library example: Decrypting an encrypted file using a custom keys manager.
3 *
4 * Decrypts encrypted XML file using a custom files based keys manager.
5 * We assume that key's name in <dsig:KeyName/> element is just
6 * key's file name in the current folder.
7 *
8 * Usage:
9 * ./decrypt3 <xml-enc>
10 *
11 * Example:
12 * ./decrypt3 encrypt1-res.xml
13 * ./decrypt3 encrypt2-res.xml
14 *
15 * This is free software; see Copyright file in the source
16 * distribution for preciese wording.
17 *
18 * Copyright (C) 2002-2016 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved.
19 */
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <assert.h>
24
25 #include <libxml/tree.h>
26 #include <libxml/xmlmemory.h>
27 #include <libxml/parser.h>
28
29 #ifndef XMLSEC_NO_XSLT
30 #include <libxslt/xslt.h>
31 #include <libxslt/security.h>
32 #endif /* XMLSEC_NO_XSLT */
33
34 #include <xmlsec/xmlsec.h>
35 #include <xmlsec/xmltree.h>
36 #include <xmlsec/xmlenc.h>
37 #include <xmlsec/crypto.h>
38
39 xmlSecKeyStoreId files_keys_store_get_klass(void);
40 xmlSecKeysMngrPtr create_files_keys_mngr(void);
41 int decrypt_file(xmlSecKeysMngrPtr mngr, const char* enc_file);
42
43 int
main(int argc,char ** argv)44 main(int argc, char **argv) {
45 xmlSecKeysMngrPtr mngr;
46 #ifndef XMLSEC_NO_XSLT
47 xsltSecurityPrefsPtr xsltSecPrefs = NULL;
48 #endif /* XMLSEC_NO_XSLT */
49
50 assert(argv);
51
52 if(argc != 2) {
53 fprintf(stderr, "Error: wrong number of arguments.\n");
54 fprintf(stderr, "Usage: %s <enc-file>\n", argv[0]);
55 return(1);
56 }
57
58 /* Init libxml and libxslt libraries */
59 xmlInitParser();
60 LIBXML_TEST_VERSION
61 xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
62 xmlSubstituteEntitiesDefault(1);
63 #ifndef XMLSEC_NO_XSLT
64 xmlIndentTreeOutput = 1;
65 #endif /* XMLSEC_NO_XSLT */
66
67 /* Init libxslt */
68 #ifndef XMLSEC_NO_XSLT
69 /* disable everything */
70 xsltSecPrefs = xsltNewSecurityPrefs();
71 xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityForbid);
72 xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid);
73 xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid);
74 xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid);
75 xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid);
76 xsltSetDefaultSecurityPrefs(xsltSecPrefs);
77 #endif /* XMLSEC_NO_XSLT */
78
79 /* Init xmlsec library */
80 if(xmlSecInit() < 0) {
81 fprintf(stderr, "Error: xmlsec initialization failed.\n");
82 return(-1);
83 }
84
85 /* Check loaded library version */
86 if(xmlSecCheckVersion() != 1) {
87 fprintf(stderr, "Error: loaded xmlsec library version is not compatible.\n");
88 return(-1);
89 }
90
91 /* Load default crypto engine if we are supporting dynamic
92 * loading for xmlsec-crypto libraries. Use the crypto library
93 * name ("openssl", "nss", etc.) to load corresponding
94 * xmlsec-crypto library.
95 */
96 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
97 if(xmlSecCryptoDLLoadLibrary(NULL) < 0) {
98 fprintf(stderr, "Error: unable to load default xmlsec-crypto library. Make sure\n"
99 "that you have it installed and check shared libraries path\n"
100 "(LD_LIBRARY_PATH and/or LTDL_LIBRARY_PATH) environment variables.\n");
101 return(-1);
102 }
103 #endif /* XMLSEC_CRYPTO_DYNAMIC_LOADING */
104
105 /* Init crypto library */
106 if(xmlSecCryptoAppInit(NULL) < 0) {
107 fprintf(stderr, "Error: crypto initialization failed.\n");
108 return(-1);
109 }
110
111 /* Init xmlsec-crypto library */
112 if(xmlSecCryptoInit() < 0) {
113 fprintf(stderr, "Error: xmlsec-crypto initialization failed.\n");
114 return(-1);
115 }
116
117 /* create keys manager and load keys */
118 mngr = create_files_keys_mngr();
119 if(mngr == NULL) {
120 return(-1);
121 }
122
123 if(decrypt_file(mngr, argv[1]) < 0) {
124 xmlSecKeysMngrDestroy(mngr);
125 return(-1);
126 }
127
128 /* destroy keys manager */
129 xmlSecKeysMngrDestroy(mngr);
130
131 /* Shutdown xmlsec-crypto library */
132 xmlSecCryptoShutdown();
133
134 /* Shutdown crypto library */
135 xmlSecCryptoAppShutdown();
136
137 /* Shutdown xmlsec library */
138 xmlSecShutdown();
139
140 /* Shutdown libxslt/libxml */
141 #ifndef XMLSEC_NO_XSLT
142 xsltFreeSecurityPrefs(xsltSecPrefs);
143 xsltCleanupGlobals();
144 #endif /* XMLSEC_NO_XSLT */
145 xmlCleanupParser();
146
147 return(0);
148 }
149
150 /**
151 * decrypt_file:
152 * @mngr: the pointer to keys manager.
153 * @enc_file: the encrypted XML file name.
154 *
155 * Decrypts the XML file #enc_file using DES key from #key_file and
156 * prints results to stdout.
157 *
158 * Returns 0 on success or a negative value if an error occurs.
159 */
160 int
decrypt_file(xmlSecKeysMngrPtr mngr,const char * enc_file)161 decrypt_file(xmlSecKeysMngrPtr mngr, const char* enc_file) {
162 xmlDocPtr doc = NULL;
163 xmlNodePtr node = NULL;
164 xmlSecEncCtxPtr encCtx = NULL;
165 int res = -1;
166
167 assert(mngr);
168 assert(enc_file);
169
170 /* load template */
171 doc = xmlParseFile(enc_file);
172 if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
173 fprintf(stderr, "Error: unable to parse file \"%s\"\n", enc_file);
174 goto done;
175 }
176
177 /* find start node */
178 node = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeEncryptedData, xmlSecEncNs);
179 if(node == NULL) {
180 fprintf(stderr, "Error: start node not found in \"%s\"\n", enc_file);
181 goto done;
182 }
183
184 /* create encryption context */
185 encCtx = xmlSecEncCtxCreate(mngr);
186 if(encCtx == NULL) {
187 fprintf(stderr,"Error: failed to create encryption context\n");
188 goto done;
189 }
190
191 /* decrypt the data */
192 if((xmlSecEncCtxDecrypt(encCtx, node) < 0) || (encCtx->result == NULL)) {
193 fprintf(stderr,"Error: decryption failed\n");
194 goto done;
195 }
196
197 /* print decrypted data to stdout */
198 if(encCtx->resultReplaced != 0) {
199 fprintf(stdout, "Decrypted XML data:\n");
200 xmlDocDump(stdout, doc);
201 } else {
202 fprintf(stdout, "Decrypted binary data (%d bytes):\n", xmlSecBufferGetSize(encCtx->result));
203 if(xmlSecBufferGetData(encCtx->result) != NULL) {
204 fwrite(xmlSecBufferGetData(encCtx->result),
205 1,
206 xmlSecBufferGetSize(encCtx->result),
207 stdout);
208 }
209 }
210 fprintf(stdout, "\n");
211
212 /* success */
213 res = 0;
214
215 done:
216 /* cleanup */
217 if(encCtx != NULL) {
218 xmlSecEncCtxDestroy(encCtx);
219 }
220
221 if(doc != NULL) {
222 xmlFreeDoc(doc);
223 }
224 return(res);
225 }
226
227 /**
228 * create_files_keys_mngr:
229 *
230 * Creates a files based keys manager: we assume that key name is
231 * the key file name,
232 *
233 * Returns pointer to newly created keys manager or NULL if an error occurs.
234 */
235 xmlSecKeysMngrPtr
create_files_keys_mngr(void)236 create_files_keys_mngr(void) {
237 xmlSecKeyStorePtr keysStore;
238 xmlSecKeysMngrPtr mngr;
239
240 /* create files based keys store */
241 keysStore = xmlSecKeyStoreCreate(files_keys_store_get_klass());
242 if(keysStore == NULL) {
243 fprintf(stderr, "Error: failed to create keys store.\n");
244 return(NULL);
245 }
246
247 /* create keys manager */
248 mngr = xmlSecKeysMngrCreate();
249 if(mngr == NULL) {
250 fprintf(stderr, "Error: failed to create keys manager.\n");
251 xmlSecKeyStoreDestroy(keysStore);
252 return(NULL);
253 }
254
255 /* add store to keys manager, from now on keys manager destroys the store if needed */
256 if(xmlSecKeysMngrAdoptKeysStore(mngr, keysStore) < 0) {
257 fprintf(stderr, "Error: failed to add keys store to keys manager.\n");
258 xmlSecKeyStoreDestroy(keysStore);
259 xmlSecKeysMngrDestroy(mngr);
260 return(NULL);
261 }
262
263 /* initialize crypto library specific data in keys manager */
264 if(xmlSecCryptoKeysMngrInit(mngr) < 0) {
265 fprintf(stderr, "Error: failed to initialize crypto data in keys manager.\n");
266 xmlSecKeysMngrDestroy(mngr);
267 return(NULL);
268 }
269
270 /* set the get key callback */
271 mngr->getKey = xmlSecKeysMngrGetKey;
272 return(mngr);
273 }
274
275 /****************************************************************************
276 *
277 * Files Keys Store: we assume that key's name (content of the
278 * <dsig:KeyName/> element is a name of the file with a key (in the
279 * current folder).
280 * Attention: this probably not a good solution for high traffic systems.
281 *
282 ***************************************************************************/
283 static xmlSecKeyPtr files_keys_store_find_key (xmlSecKeyStorePtr store,
284 const xmlChar* name,
285 xmlSecKeyInfoCtxPtr keyInfoCtx);
286 static xmlSecKeyStoreKlass files_keys_store_klass = {
287 sizeof(xmlSecKeyStoreKlass),
288 sizeof(xmlSecKeyStore),
289 BAD_CAST "files-based-keys-store", /* const xmlChar* name; */
290 NULL, /* xmlSecKeyStoreInitializeMethod initialize; */
291 NULL, /* xmlSecKeyStoreFinalizeMethod finalize; */
292 files_keys_store_find_key, /* xmlSecKeyStoreFindKeyMethod findKey; */
293
294 /* reserved for the future */
295 NULL, /* void* reserved0; */
296 NULL, /* void* reserved1; */
297 };
298
299 /**
300 * files_keys_store_get_klass:
301 *
302 * The files based keys store klass: we assume that key name is the
303 * key file name,
304 *
305 * Returns files based keys store klass.
306 */
307 xmlSecKeyStoreId
files_keys_store_get_klass(void)308 files_keys_store_get_klass(void) {
309 return(&files_keys_store_klass);
310 }
311
312 /**
313 * files_keys_store_find_key:
314 * @store: the pointer to simple keys store.
315 * @name: the desired key name.
316 * @keyInfoCtx: the pointer to <dsig:KeyInfo/> node processing context.
317 *
318 * Lookups key in the @store. The caller is responsible for destroying
319 * returned key with #xmlSecKeyDestroy function.
320 *
321 * Returns pointer to key or NULL if key not found or an error occurs.
322 */
323 static xmlSecKeyPtr
files_keys_store_find_key(xmlSecKeyStorePtr store,const xmlChar * name,xmlSecKeyInfoCtxPtr keyInfoCtx)324 files_keys_store_find_key(xmlSecKeyStorePtr store, const xmlChar* name, xmlSecKeyInfoCtxPtr keyInfoCtx) {
325 xmlSecKeyPtr key;
326 const xmlChar* p;
327
328 assert(store);
329 assert(keyInfoCtx);
330
331 /* it's possible to do not have the key name or desired key type
332 * but we could do nothing in this case */
333 if((name == NULL) || (keyInfoCtx->keyReq.keyId == xmlSecKeyDataIdUnknown)){
334 return(NULL);
335 }
336
337 /* we don't want to open files in a folder other than "current";
338 * to prevent it limit the characters in the key name to alpha/digit,
339 * '.', '-' or '_'.
340 */
341 for(p = name; (*p) != '\0'; ++p) {
342 if(!isalnum((*p)) && ((*p) != '.') && ((*p) != '-') && ((*p) != '_')) {
343 return(NULL);
344 }
345 }
346
347 if((keyInfoCtx->keyReq.keyId == xmlSecKeyDataDsaId) || (keyInfoCtx->keyReq.keyId == xmlSecKeyDataRsaId)) {
348 /* load key from a pem file, if key is not found then it's an error (is it?) */
349 key = xmlSecCryptoAppKeyLoad(name, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
350 if(key == NULL) {
351 fprintf(stderr,"Error: failed to load public pem key from \"%s\"\n", name);
352 return(NULL);
353 }
354 } else {
355 /* otherwise it's a binary key, if key is not found then it's an error (is it?) */
356 key = xmlSecKeyReadBinaryFile(keyInfoCtx->keyReq.keyId, name);
357 if(key == NULL) {
358 fprintf(stderr,"Error: failed to load key from binary file \"%s\"\n", name);
359 return(NULL);
360 }
361 }
362
363 /* set key name */
364 if(xmlSecKeySetName(key, name) < 0) {
365 fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", name);
366 xmlSecKeyDestroy(key);
367 return(NULL);
368 }
369
370 return(key);
371 }
372
373