xsec/tools/checksig/checksig.cpp (434 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * XSEC * * checkSig := (Very ugly) tool to check a signature embedded in an XML file * * $Id$ * */ #include "AnonymousResolver.hpp" #include "InteropResolver.hpp" // XSEC #include <xsec/utils/XSECPlatformUtils.hpp> #include <xsec/framework/XSECProvider.hpp> #include <xsec/canon/XSECC14n20010315.hpp> #include <xsec/dsig/DSIGSignature.hpp> #include <xsec/framework/XSECException.hpp> #include <xsec/enc/XSECCryptoException.hpp> #include <xsec/enc/XSECKeyInfoResolverDefault.hpp> #include "../../utils/XSECDOMUtils.hpp" // General #include <memory.h> #include <string.h> #include <iostream> #include <stdlib.h> #if defined(HAVE_UNISTD_H) # include <unistd.h> #else # if defined(HAVE_DIRECT_H) # include <direct.h> # endif #endif #if defined (_DEBUG) && defined (_MSC_VER) #include <crtdbg.h> #endif #include <xercesc/util/PlatformUtils.hpp> #include <xercesc/util/XMLString.hpp> #include <xercesc/dom/DOM.hpp> #include <xercesc/parsers/XercesDOMParser.hpp> #include <xercesc/util/XMLException.hpp> #include <xercesc/util/XMLUri.hpp> #include <xercesc/util/Janitor.hpp> XERCES_CPP_NAMESPACE_USE using std::cerr; using std::cout; using std::endl; #ifdef XSEC_HAVE_XALAN // XALAN #include <xalanc/XPath/XPathEvaluator.hpp> #include <xalanc/XalanTransformer/XalanTransformer.hpp> // If this isn't defined, we're on Xalan 1.12+ and require modern C++ #ifndef XALAN_USING_XALAN # define XALAN_USING_XALAN(NAME) using xalanc :: NAME; #endif XALAN_USING_XALAN(XPathEvaluator) XALAN_USING_XALAN(XalanTransformer) #endif #if defined (XSEC_HAVE_OPENSSL) // OpenSSL # include <xsec/enc/OpenSSL/OpenSSLCryptoKeyHMAC.hpp> # include <openssl/err.h> #endif #if defined (XSEC_HAVE_WINCAPI) # include <xsec/enc/WinCAPI/WinCAPICryptoProvider.hpp> # include <xsec/enc/WinCAPI/WinCAPICryptoKeyHMAC.hpp> #endif #if defined (XSEC_HAVE_NSS) # include <xsec/enc/NSS/NSSCryptoProvider.hpp> # include <xsec/enc/NSS/NSSCryptoKeyHMAC.hpp> #endif #include <time.h> #ifndef XSEC_HAVE_XALAN std::ostream& operator<< (std::ostream& target, const XMLCh * s) { char *p = XMLString::transcode(s); target << p; XSEC_RELEASE_XMLCH(p); return target; } #endif // ---------------------------------------------------------------------------- // Checksig // ---------------------------------------------------------------------------- void printUsage(void) { cerr << "\nUsage: checksig [options] <input file name>\n\n"; cerr << " Where options are :\n\n"; cerr << " --skiprefs/-s\n"; cerr << " Skip checking references - check signature only\n\n"; cerr << " --hmackey/-h <string>\n"; cerr << " Set an hmac key using the <string>\n\n"; cerr << " --xsecresolver/-x\n"; cerr << " Use the xml-security test XMLDSig URI resolver\n\n"; cerr << " --id <name>\n"; cerr << " Define an attribute Id by name\n\n"; cerr << " --idns/-d <ns uri> <name>\n"; cerr << " Define an attribute Id by namespace URI and name\n\n"; #if defined (XSEC_HAVE_OPENSSL) cerr << " --interop/-i\n"; cerr << " Use the interop resolver for Baltimore interop examples\n\n"; #endif #if defined(XSEC_HAVE_WINCAPI) # if defined (XSEC_HAVE_OPENSSL) cerr << " --wincapi/-w\n"; cerr << " Use the Windows CAPI crypto Provider\n\n"; # endif cerr << " --winhmackey/-wh <string>\n"; cerr << " Use the Windows CAPI crypto provider and hash the <string>\n"; cerr << " into a Windows key using SHA-1\n\n"; #endif cerr << " Exits with codes :\n"; cerr << " 0 = Signature OK\n"; cerr << " 1 = Signature Bad\n"; cerr << " 2 = Processing error\n"; } int evaluate(int argc, char ** argv) { char * filename = NULL; char * hmacKeyStr = NULL; char * useIdAttributeNS = NULL; char * useIdAttributeName = NULL; XSECCryptoKey * key = NULL; bool useXSECURIResolver = false; bool useAnonymousResolver = false; bool useInteropResolver = false; #if defined (XSEC_HAVE_WINCAPI) HCRYPTPROV win32CSP = 0; #endif bool skipRefs = false; if (argc < 2) { printUsage(); return 2; } // Run through parameters int paramCount = 1; while (paramCount < argc - 1) { if (_stricmp(argv[paramCount], "--hmackey") == 0 || _stricmp(argv[paramCount], "-h") == 0) { paramCount++; hmacKeyStr = argv[paramCount++]; } else if (_stricmp(argv[paramCount], "--skiprefs") == 0 || _stricmp(argv[paramCount], "-s") == 0) { skipRefs = true; paramCount++; } else if (_stricmp(argv[paramCount], "--xsecresolver") == 0 || _stricmp(argv[paramCount], "-x") == 0) { useXSECURIResolver = true; paramCount++; } else if (_stricmp(argv[paramCount], "--id") == 0) { if (paramCount +1 >= argc) { printUsage(); return 2; } paramCount++; useIdAttributeName = argv[paramCount++]; } else if (_stricmp(argv[paramCount], "--idns") == 0 || _stricmp(argv[paramCount], "-d") == 0) { if (paramCount +2 >= argc) { printUsage(); return 2; } paramCount++; useIdAttributeNS = argv[paramCount++]; useIdAttributeName = argv[paramCount++]; } #if defined (XSEC_HAVE_OPENSSL) else if (_stricmp(argv[paramCount], "--interop") == 0 || _stricmp(argv[paramCount], "-i") == 0) { // Use the interop key resolver useInteropResolver = true; paramCount++; } #endif else if (_stricmp(argv[paramCount], "--anonymousresolver") == 0 || _stricmp(argv[paramCount], "-a") ==0) { useAnonymousResolver = true; paramCount++; } #if defined (XSEC_HAVE_WINCAPI) else if (_stricmp(argv[paramCount], "--wincapi") == 0 || _stricmp(argv[paramCount], "-w") == 0 || _stricmp(argv[paramCount], "--winhmackey") == 0 || _stricmp(argv[paramCount], "-wh") == 0) { WinCAPICryptoProvider * cp = new WinCAPICryptoProvider(); XSECPlatformUtils::SetCryptoProvider(cp); if (_stricmp(argv[paramCount], "--winhmackey") == 0 || _stricmp(argv[paramCount], "-wh") == 0) { // Create a SHA-1 based key based on the <string> parameter paramCount++; if (!CryptAcquireContext(&win32CSP, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { cerr << "Error obtaining default RSA_PROV" << endl; return 2; } HCRYPTKEY k; HCRYPTHASH h; BOOL fResult = CryptCreateHash( win32CSP, CALG_SHA, 0, 0, &h); if (fResult == 0) { cerr << "Error creating hash to create windows hmac key from password" << endl; return 2; } fResult = CryptHashData( h, (unsigned char *) argv[paramCount], (DWORD) strlen(argv[paramCount]), 0); if (fResult == 0) { cerr << "Error hashing password to create windows hmac key" << endl; return 2; } // Now create a key fResult = CryptDeriveKey( win32CSP, CALG_RC2, h, CRYPT_EXPORTABLE, &k); if (fResult == 0) { cerr << "Error deriving key from hash value" << endl; return 2; } // Wrap in a WinCAPI object WinCAPICryptoKeyHMAC * hk; hk = new WinCAPICryptoKeyHMAC(win32CSP); hk->setWinKey(k); key = hk; CryptDestroyHash(h); } paramCount++; } #endif else { cerr << "Unknown option: " << argv[paramCount] << endl << endl; printUsage(); return 2; } } #if defined (XSEC_HAVE_WINCAPI) && !defined(XSEC_HAVE_OPENSSL) // Use default DSS provider WinCAPICryptoProvider * cp = new WinCAPICryptoProvider(); XSECPlatformUtils::SetCryptoProvider(cp); #endif if (paramCount >= argc) { printUsage(); return 2; } filename = argv[paramCount]; // Create and set up the parser XercesDOMParser * parser = new XercesDOMParser; Janitor<XercesDOMParser> j_parser(parser); parser->setDoNamespaces(true); parser->setCreateEntityReferenceNodes(true); // Now parse out file bool errorsOccured = false; XMLSize_t errorCount = 0; try { parser->parse(filename); errorCount = parser->getErrorCount(); if (errorCount > 0) errorsOccured = true; } catch (const XMLException& e) { cerr << "An error occurred during parsing\n Message: " << e.getMessage() << endl; errorsOccured = true; } catch (const DOMException& e) { cerr << "A DOM error occurred during parsing\n DOMException code: " << e.code << endl; errorsOccured = true; } if (errorsOccured) { cout << "Errors during parse" << endl; return (2); } /* Now that we have the parsed file, get the DOM document and start looking at it */ DOMNode *doc; // The document that we parsed doc = parser->getDocument(); DOMDocument *theDOM = parser->getDocument(); // Find the signature node DOMNode *sigNode = findDSIGNode(doc, "Signature"); // Create the signature checker if (sigNode == 0) { cerr << "Could not find <Signature> node in " << argv[argc-1] << endl; return 2; } XSECProvider prov; XSECKeyInfoResolverDefault theKeyInfoResolver; DSIGSignature * sig = prov.newSignatureFromDOM(theDOM, sigNode); // The only way we can verify is using keys read directly from the KeyInfo list, // so we add a KeyInfoResolverDefault to the Signature. sig->setKeyInfoResolver(&theKeyInfoResolver); // Register defined attribute name if (useIdAttributeName != NULL) { sig->setIdByAttributeName(true); if (useIdAttributeNS != NULL) { sig->registerIdAttributeNameNS(MAKE_UNICODE_STRING(useIdAttributeNS), MAKE_UNICODE_STRING(useIdAttributeName)); } else { sig->registerIdAttributeName(MAKE_UNICODE_STRING(useIdAttributeName)); } } // Check whether we should use the internal resolver if (useXSECURIResolver == true || useAnonymousResolver == true || useInteropResolver == true) { AnonymousResolver theAnonymousResolver; // Map out base path of the file #if XSEC_HAVE_GETCWD_DYN char *path = getcwd(NULL, 0); char *baseURI = (char*)malloc(strlen(path) + 8 + 1 + strlen(filename) + 1); #else char path[PATH_MAX]; char baseURI[(PATH_MAX * 2) + 10]; getcwd(path, PATH_MAX); #endif strcpy(baseURI, "file:///"); // Ugly and nasty but quick if (filename[0] != '\\' && filename[0] != '/' && filename[1] != ':') { strcat(baseURI, path); strcat(baseURI, "/"); } else if (path[1] == ':') { path[2] = '\0'; strcat(baseURI, path); } strcat(baseURI, filename); // Find any ':' and "\" characters int lastSlash = 0; for (unsigned int i = 8; i < strlen(baseURI); ++i) { if (baseURI[i] == '\\') { lastSlash = i; baseURI[i] = '/'; } else if (baseURI[i] == '/') lastSlash = i; } // The last "\\" must prefix the filename baseURI[lastSlash + 1] = '\0'; XMLCh * baseURIXMLCh = XMLString::transcode(baseURI); XMLUri uri(MAKE_UNICODE_STRING(baseURI)); #if XSEC_HAVE_GETCWD_DYN free(path); free(baseURI); #endif if (useAnonymousResolver == true) { sig->setURIResolver(&theAnonymousResolver); } sig->getURIResolver()->setBaseURI(baseURIXMLCh); #if defined (XSEC_HAVE_OPENSSL) if (useInteropResolver == true) { InteropResolver ires(&(baseURIXMLCh[8])); sig->setKeyInfoResolver(&ires); } #endif XSEC_RELEASE_XMLCH(baseURIXMLCh); } bool result; try { // Load a key if necessary if (hmacKeyStr != NULL) { XSECCryptoKeyHMAC * hmacKey = XSECPlatformUtils::g_cryptoProvider->keyHMAC(); hmacKey->setKey((unsigned char *) hmacKeyStr, (unsigned int) strlen(hmacKeyStr)); sig->setSigningKey(hmacKey); } else if (key != NULL) { sig->setSigningKey(key); } sig->load(); if (skipRefs) result = sig->verifySignatureOnly(); else result = sig->verify(); } catch (const XSECException &e) { char * msg = XMLString::transcode(e.getMsg()); cerr << "An error occurred during signature verification\n Message: " << msg << endl; XSEC_RELEASE_XMLCH(msg); errorsOccured = true; return 2; } catch (const XSECCryptoException &e) { cerr << "An error occurred during signature verification\n Message: " << e.getMsg() << endl; errorsOccured = true; #if defined (XSEC_HAVE_OPENSSL) ERR_load_crypto_strings(); BIO * bio_err; if ((bio_err=BIO_new(BIO_s_file())) != NULL) BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT); ERR_print_errors(bio_err); #endif return 2; } #if 0 catch (...) { cerr << "Unknown Exception type occurred. Cleaning up and exiting\n" << endl; return 2; } #endif int retResult; if (result) { cout << "Signature verified OK!" << endl; retResult = 0; } else { cout << "Signature failed verification" << endl; char * e = XMLString::transcode(sig->getErrMsgs()); cout << e << endl; XSEC_RELEASE_XMLCH(e); retResult = 1; } #if defined (XSEC_HAVE_WINCAPI) // Clean up the handle to the CSP if (win32CSP != 0) CryptReleaseContext(win32CSP, 0); #endif // Janitor will clean up the parser return retResult; } int main(int argc, char **argv) { int retResult; #if defined (_DEBUG) && defined (_MSC_VER) // Do some memory debugging under Visual C++ _CrtMemState s1, s2, s3; // At this point we are about to start really using XSEC, so // Take a "before" checkpoing _CrtMemCheckpoint( &s1 ); #endif // Initialise the XML system try { XMLPlatformUtils::Initialize(); #ifdef XSEC_HAVE_XALAN XPathEvaluator::initialize(); XalanTransformer::initialize(); #endif XSECPlatformUtils::Initialise(); } catch (const XMLException &e) { cerr << "Error during initialisation of Xerces" << endl; cerr << "Error Message = : " << e.getMessage() << endl; } retResult = evaluate(argc, argv); XSECPlatformUtils::Terminate(); #ifdef XSEC_HAVE_XALAN XalanTransformer::terminate(); XPathEvaluator::terminate(); #endif XMLPlatformUtils::Terminate(); #if defined (_DEBUG) && defined (_MSC_VER) _CrtMemCheckpoint( &s2 ); if ( _CrtMemDifference( &s3, &s1, &s2 ) && ( s3.lCounts[0] > 0 || s3.lCounts[1] > 1 || // s3.lCounts[2] > 2 || We don't worry about C Runtime s3.lCounts[3] > 0 || s3.lCounts[4] > 0)) { // Note that there is generally 1 Normal and 1 CRT block // still taken. 1 is from Xalan and 1 from stdio // Send all reports to STDOUT _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT ); _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT ); _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT ); // Dumpy memory stats _CrtMemDumpAllObjectsSince( &s3 ); _CrtMemDumpStatistics( &s3 ); } // Now turn off memory leak checking and end as there are some // Globals that are allocated that get seen as leaks (Xalan?) int dbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); dbgFlag &= ~(_CRTDBG_LEAK_CHECK_DF); _CrtSetDbgFlag( dbgFlag ); #endif return retResult; }