upload source
[external/xmlsec1.git] / examples / verify4.c
1 /** 
2  * XML Security Library example: Verifying a simple SAML response with X509 certificate
3  *
4  * Verifies a simple SAML response. In addition to refular verification
5  * we ensure that the signature has only one <dsig:Reference/> element
6  * with an empty or NULL URI attribute and one enveloped signature transform
7  * as it is required by SAML specification.
8  * 
9  * This example was developed and tested with OpenSSL crypto library. The 
10  * certificates management policies for another crypto library may break it.
11  *
12  * Usage: 
13  *      verify4 <signed-file> <trusted-cert-pem-file1> [<trusted-cert-pem-file2> [...]]
14  *
15  * Example (sucecess):
16  *      ./verify4 verify4-res.xml rootcert.pem
17  *
18  * Example (failure):
19  *      ./verify4 verify4-bad-res.xml rootcert.pem
20  * In the same time, verify3 example successfuly verifies this signature:
21  *      ./verify3 verify4-bad-res.xml rootcert.pem
22  *
23  * This is free software; see Copyright file in the source
24  * distribution for preciese wording.
25  * 
26  * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
27  */
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31
32 #include <libxml/tree.h>
33 #include <libxml/xmlmemory.h>
34 #include <libxml/parser.h>
35
36 #ifndef XMLSEC_NO_XSLT
37 #include <libxslt/xslt.h>
38 #endif /* XMLSEC_NO_XSLT */
39
40 #include <xmlsec/xmlsec.h>
41 #include <xmlsec/xmltree.h>
42 #include <xmlsec/xmldsig.h>
43 #include <xmlsec/crypto.h>
44
45 xmlSecKeysMngrPtr load_trusted_certs(char** files, int files_size);
46 int verify_file(xmlSecKeysMngrPtr mngr, const char* xml_file);
47
48 int 
49 main(int argc, char **argv) {
50     xmlSecKeysMngrPtr mngr;
51     
52     assert(argv);
53
54     if(argc < 3) {
55         fprintf(stderr, "Error: wrong number of arguments.\n");
56         fprintf(stderr, "Usage: %s <xml-file> <cert-file1> [<cert-file2> [...]]\n", argv[0]);
57         return(1);
58     }
59
60     /* Init libxml and libxslt libraries */
61     xmlInitParser();
62     LIBXML_TEST_VERSION
63     xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
64     xmlSubstituteEntitiesDefault(1);
65 #ifndef XMLSEC_NO_XSLT
66     xmlIndentTreeOutput = 1; 
67 #endif /* XMLSEC_NO_XSLT */
68                 
69     /* Init xmlsec library */
70     if(xmlSecInit() < 0) {
71         fprintf(stderr, "Error: xmlsec initialization failed.\n");
72         return(-1);
73     }
74
75     /* Check loaded library version */
76     if(xmlSecCheckVersion() != 1) {
77         fprintf(stderr, "Error: loaded xmlsec library version is not compatible.\n");
78         return(-1);
79     }
80
81     /* Load default crypto engine if we are supporting dynamic
82      * loading for xmlsec-crypto libraries. Use the crypto library
83      * name ("openssl", "nss", etc.) to load corresponding 
84      * xmlsec-crypto library.
85      */
86 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
87     if(xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
88         fprintf(stderr, "Error: unable to load default xmlsec-crypto library. Make sure\n"
89                         "that you have it installed and check shared libraries path\n"
90                         "(LD_LIBRARY_PATH) envornment variable.\n");
91         return(-1);     
92     }
93 #endif /* XMLSEC_CRYPTO_DYNAMIC_LOADING */
94
95     /* Init crypto library */
96     if(xmlSecCryptoAppInit(NULL) < 0) {
97         fprintf(stderr, "Error: crypto initialization failed.\n");
98         return(-1);
99     }
100
101     /* Init xmlsec-crypto library */
102     if(xmlSecCryptoInit() < 0) {
103         fprintf(stderr, "Error: xmlsec-crypto initialization failed.\n");
104         return(-1);
105     }
106
107     /* create keys manager and load trusted certificates */
108     mngr = load_trusted_certs(&(argv[2]), argc - 2);
109     if(mngr == NULL) {
110         return(-1);
111     }
112     
113     /* verify file */
114     if(verify_file(mngr, argv[1]) < 0) {
115         xmlSecKeysMngrDestroy(mngr);    
116         return(-1);
117     }    
118     
119     /* destroy keys manager */
120     xmlSecKeysMngrDestroy(mngr);
121     
122     /* Shutdown xmlsec-crypto library */
123     xmlSecCryptoShutdown();
124     
125     /* Shutdown crypto library */
126     xmlSecCryptoAppShutdown();
127     
128     /* Shutdown xmlsec library */
129     xmlSecShutdown();
130
131     /* Shutdown libxslt/libxml */
132 #ifndef XMLSEC_NO_XSLT
133     xsltCleanupGlobals();            
134 #endif /* XMLSEC_NO_XSLT */
135     xmlCleanupParser();
136     
137     return(0);
138 }
139
140 /**
141  * load_trusted_certs:
142  * @files:              the list of filenames.
143  * @files_size:         the number of filenames in #files.
144  *
145  * Creates simple keys manager and load trusted certificates from PEM #files.
146  * The caller is responsible for destroing returned keys manager using
147  * @xmlSecKeysMngrDestroy.
148  *
149  * Returns the pointer to newly created keys manager or NULL if an error
150  * occurs.
151  */
152 xmlSecKeysMngrPtr 
153 load_trusted_certs(char** files, int files_size) {
154     xmlSecKeysMngrPtr mngr;
155     int i;
156         
157     assert(files);
158     assert(files_size > 0);
159     
160     /* create and initialize keys manager, we use a simple list based
161      * keys manager, implement your own xmlSecKeysStore klass if you need
162      * something more sophisticated 
163      */
164     mngr = xmlSecKeysMngrCreate();
165     if(mngr == NULL) {
166         fprintf(stderr, "Error: failed to create keys manager.\n");
167         return(NULL);
168     }
169     if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
170         fprintf(stderr, "Error: failed to initialize keys manager.\n");
171         xmlSecKeysMngrDestroy(mngr);
172         return(NULL);
173     }    
174     
175     for(i = 0; i < files_size; ++i) {
176         assert(files[i]);
177
178         /* load trusted cert */
179         if(xmlSecCryptoAppKeysMngrCertLoad(mngr, files[i], xmlSecKeyDataFormatPem, xmlSecKeyDataTypeTrusted) < 0) {
180             fprintf(stderr,"Error: failed to load pem certificate from \"%s\"\n", files[i]);
181             xmlSecKeysMngrDestroy(mngr);
182             return(NULL);
183         }
184     }
185
186     return(mngr);
187 }
188
189 /** 
190  * verify_file:
191  * @mngr:               the pointer to keys manager.
192  * @xml_file:           the signed XML file name.
193  *
194  * Verifies XML signature in #xml_file.
195  *
196  * Returns 0 on success or a negative value if an error occurs.
197  */
198 int 
199 verify_file(xmlSecKeysMngrPtr mngr, const char* xml_file) {
200     xmlDocPtr doc = NULL;
201     xmlNodePtr node = NULL;
202     xmlSecDSigCtxPtr dsigCtx = NULL;
203     int res = -1;
204     
205     assert(mngr);
206     assert(xml_file);
207
208     /* load file */
209     doc = xmlParseFile(xml_file);
210     if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
211         fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_file);
212         goto done;      
213     }
214     
215     /* find start node */
216     node = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeSignature, xmlSecDSigNs);
217     if(node == NULL) {
218         fprintf(stderr, "Error: start node not found in \"%s\"\n", xml_file);
219         goto done;      
220     }
221
222     /* create signature context */
223     dsigCtx = xmlSecDSigCtxCreate(mngr);
224     if(dsigCtx == NULL) {
225         fprintf(stderr,"Error: failed to create signature context\n");
226         goto done;
227     }
228
229     /* limit the Reference URI attributes to empty or NULL */
230     dsigCtx->enabledReferenceUris = xmlSecTransformUriTypeEmpty;
231     
232     /* limit allowed transforms for siganture and reference processing */
233     if((xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14NId) < 0) ||
234        (xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformExclC14NId) < 0) ||
235        (xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformSha1Id) < 0) ||
236        (xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformRsaSha1Id) < 0)) {
237
238         fprintf(stderr,"Error: failed to limit allowed siganture transforms\n");
239         goto done;
240     }
241     if((xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformInclC14NId) < 0) ||
242        (xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformExclC14NId) < 0) ||
243        (xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformSha1Id) < 0) ||
244        (xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformEnvelopedId) < 0)) {
245
246         fprintf(stderr,"Error: failed to limit allowed reference transforms\n");
247         goto done;
248     }
249
250     /* in addition, limit possible key data to valid X509 certificates only */
251     if(xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataX509Id) < 0) {
252         fprintf(stderr,"Error: failed to limit allowed key data\n");
253         goto done;
254     }
255     
256     /* Verify signature */
257     if(xmlSecDSigCtxVerify(dsigCtx, node) < 0) {
258         fprintf(stderr,"Error: signature verify\n");
259         goto done;
260     }
261
262     /* check that we have only one Reference */
263     if((dsigCtx->status == xmlSecDSigStatusSucceeded) && 
264         (xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences)) != 1)) {
265         
266         fprintf(stderr,"Error: only one reference is allowed\n");
267         goto done;
268     }
269         
270     /* print verification result to stdout */
271     if(dsigCtx->status == xmlSecDSigStatusSucceeded) {
272         fprintf(stdout, "Signature is OK\n");
273     } else {
274         fprintf(stdout, "Signature is INVALID\n");
275     }    
276
277     /* success */
278     res = 0;
279
280 done:    
281     /* cleanup */
282     if(dsigCtx != NULL) {
283         xmlSecDSigCtxDestroy(dsigCtx);
284     }
285     
286     if(doc != NULL) {
287         xmlFreeDoc(doc); 
288     }
289     return(res);
290 }
291
292