2 * XML Security Library example: Verifying a simple SAML response with X509 certificate
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.
9 * This example was developed and tested with OpenSSL crypto library. The
10 * certificates management policies for another crypto library may break it.
13 * verify4 <signed-file> <trusted-cert-pem-file1> [<trusted-cert-pem-file2> [...]]
16 * ./verify4 verify4-res.xml rootcert.pem
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
23 * This is free software; see Copyright file in the source
24 * distribution for preciese wording.
26 * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
32 #include <libxml/tree.h>
33 #include <libxml/xmlmemory.h>
34 #include <libxml/parser.h>
36 #ifndef XMLSEC_NO_XSLT
37 #include <libxslt/xslt.h>
38 #endif /* XMLSEC_NO_XSLT */
40 #include <xmlsec/xmlsec.h>
41 #include <xmlsec/xmltree.h>
42 #include <xmlsec/xmldsig.h>
43 #include <xmlsec/crypto.h>
45 xmlSecKeysMngrPtr load_trusted_certs(char** files, int files_size);
46 int verify_file(xmlSecKeysMngrPtr mngr, const char* xml_file);
49 main(int argc, char **argv) {
50 xmlSecKeysMngrPtr mngr;
55 fprintf(stderr, "Error: wrong number of arguments.\n");
56 fprintf(stderr, "Usage: %s <xml-file> <cert-file1> [<cert-file2> [...]]\n", argv[0]);
60 /* Init libxml and libxslt libraries */
63 xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
64 xmlSubstituteEntitiesDefault(1);
65 #ifndef XMLSEC_NO_XSLT
66 xmlIndentTreeOutput = 1;
67 #endif /* XMLSEC_NO_XSLT */
69 /* Init xmlsec library */
70 if(xmlSecInit() < 0) {
71 fprintf(stderr, "Error: xmlsec initialization failed.\n");
75 /* Check loaded library version */
76 if(xmlSecCheckVersion() != 1) {
77 fprintf(stderr, "Error: loaded xmlsec library version is not compatible.\n");
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.
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");
93 #endif /* XMLSEC_CRYPTO_DYNAMIC_LOADING */
95 /* Init crypto library */
96 if(xmlSecCryptoAppInit(NULL) < 0) {
97 fprintf(stderr, "Error: crypto initialization failed.\n");
101 /* Init xmlsec-crypto library */
102 if(xmlSecCryptoInit() < 0) {
103 fprintf(stderr, "Error: xmlsec-crypto initialization failed.\n");
107 /* create keys manager and load trusted certificates */
108 mngr = load_trusted_certs(&(argv[2]), argc - 2);
114 if(verify_file(mngr, argv[1]) < 0) {
115 xmlSecKeysMngrDestroy(mngr);
119 /* destroy keys manager */
120 xmlSecKeysMngrDestroy(mngr);
122 /* Shutdown xmlsec-crypto library */
123 xmlSecCryptoShutdown();
125 /* Shutdown crypto library */
126 xmlSecCryptoAppShutdown();
128 /* Shutdown xmlsec library */
131 /* Shutdown libxslt/libxml */
132 #ifndef XMLSEC_NO_XSLT
133 xsltCleanupGlobals();
134 #endif /* XMLSEC_NO_XSLT */
141 * load_trusted_certs:
142 * @files: the list of filenames.
143 * @files_size: the number of filenames in #files.
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.
149 * Returns the pointer to newly created keys manager or NULL if an error
153 load_trusted_certs(char** files, int files_size) {
154 xmlSecKeysMngrPtr mngr;
158 assert(files_size > 0);
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
164 mngr = xmlSecKeysMngrCreate();
166 fprintf(stderr, "Error: failed to create keys manager.\n");
169 if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
170 fprintf(stderr, "Error: failed to initialize keys manager.\n");
171 xmlSecKeysMngrDestroy(mngr);
175 for(i = 0; i < files_size; ++i) {
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);
191 * @mngr: the pointer to keys manager.
192 * @xml_file: the signed XML file name.
194 * Verifies XML signature in #xml_file.
196 * Returns 0 on success or a negative value if an error occurs.
199 verify_file(xmlSecKeysMngrPtr mngr, const char* xml_file) {
200 xmlDocPtr doc = NULL;
201 xmlNodePtr node = NULL;
202 xmlSecDSigCtxPtr dsigCtx = NULL;
209 doc = xmlParseFile(xml_file);
210 if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
211 fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_file);
215 /* find start node */
216 node = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeSignature, xmlSecDSigNs);
218 fprintf(stderr, "Error: start node not found in \"%s\"\n", xml_file);
222 /* create signature context */
223 dsigCtx = xmlSecDSigCtxCreate(mngr);
224 if(dsigCtx == NULL) {
225 fprintf(stderr,"Error: failed to create signature context\n");
229 /* limit the Reference URI attributes to empty or NULL */
230 dsigCtx->enabledReferenceUris = xmlSecTransformUriTypeEmpty;
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)) {
238 fprintf(stderr,"Error: failed to limit allowed siganture transforms\n");
241 if((xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformInclC14NId) < 0) ||
242 (xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformExclC14NId) < 0) ||
243 (xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformSha1Id) < 0) ||
244 (xmlSecDSigCtxEnableReferenceTransform(dsigCtx, xmlSecTransformEnvelopedId) < 0)) {
246 fprintf(stderr,"Error: failed to limit allowed reference transforms\n");
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");
256 /* Verify signature */
257 if(xmlSecDSigCtxVerify(dsigCtx, node) < 0) {
258 fprintf(stderr,"Error: signature verify\n");
262 /* check that we have only one Reference */
263 if((dsigCtx->status == xmlSecDSigStatusSucceeded) &&
264 (xmlSecPtrListGetSize(&(dsigCtx->signedInfoReferences)) != 1)) {
266 fprintf(stderr,"Error: only one reference is allowed\n");
270 /* print verification result to stdout */
271 if(dsigCtx->status == xmlSecDSigStatusSucceeded) {
272 fprintf(stdout, "Signature is OK\n");
274 fprintf(stdout, "Signature is INVALID\n");
282 if(dsigCtx != NULL) {
283 xmlSecDSigCtxDestroy(dsigCtx);