blob: 4c47989b7806a4d9091e370402c1b0e3ecf8fefb [file] [log] [blame]
/**
* 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
*
* templatesign := tool to sign a template XML signature file
*
* Author(s): Berin Lautenbach
*
* $Id$
*
*/
//XSEC includes
// XSEC
#include <xsec/utils/XSECPlatformUtils.hpp>
#include <xsec/framework/XSECProvider.hpp>
#include <xsec/canon/XSECC14n20010315.hpp>
#include <xsec/dsig/DSIGSignature.hpp>
#include <xsec/dsig/DSIGKeyInfoX509.hpp>
#include <xsec/framework/XSECException.hpp>
#include <xsec/framework/XSECURIResolver.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
#if defined (XSEC_HAVE_OPENSSL)
# include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
# include <xsec/enc/OpenSSL/OpenSSLCryptoKeyEC.hpp>
# include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
# include <xsec/enc/OpenSSL/OpenSSLCryptoKeyHMAC.hpp>
# include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
# include <xsec/enc/OpenSSL/OpenSSLSupport.hpp>
# include <openssl/bio.h>
# include <openssl/dsa.h>
# include <openssl/err.h>
# include <openssl/evp.h>
# include <openssl/pem.h>
#endif
#if defined(XSEC_HAVE_WINCAPI)
# include <xsec/enc/WinCAPI/WinCAPICryptoProvider.hpp>
# include <xsec/enc/WinCAPI/WinCAPICryptoKeyDSA.hpp>
# include <xsec/enc/WinCAPI/WinCAPICryptoKeyRSA.hpp>
# include <xsec/enc/WinCAPI/WinCAPICryptoKeyHMAC.hpp>
#endif
#include "../../utils/XSECDOMUtils.hpp"
#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
#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/XMLUniDefs.hpp>
#include <xercesc/util/XMLNetAccessor.hpp>
#include <xercesc/util/XMLUri.hpp>
#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
using std::ostream;
using std::cout;
using std::cerr;
using std::endl;
using std::flush;
// Uplift entire program into Xerces namespace
XERCES_CPP_NAMESPACE_USE
// --------------------------------------------------------------------------------
// Much code taken from the DOMPrint Xerces example
// --------------------------------------------------------------------------------
static XMLFormatter* gFormatter = 0;
static XMLCh* gEncodingName = 0;
static XMLFormatter::UnRepFlags gUnRepFlags = XMLFormatter::UnRep_CharRef;
static const XMLCh gEndElement[] = { chOpenAngle, chForwardSlash, chNull };
static const XMLCh gEndPI[] = { chQuestion, chCloseAngle, chNull};
static const XMLCh gStartPI[] = { chOpenAngle, chQuestion, chNull };
static const XMLCh gStartCDATA[] =
{
chOpenAngle, chBang, chOpenSquare, chLatin_C, chLatin_D,
chLatin_A, chLatin_T, chLatin_A, chOpenSquare, chNull
};
static const XMLCh gEndCDATA[] =
{
chCloseSquare, chCloseSquare, chCloseAngle, chNull
};
static const XMLCh gStartComment[] =
{
chOpenAngle, chBang, chDash, chDash, chNull
};
static const XMLCh gEndComment[] =
{
chDash, chDash, chCloseAngle, chNull
};
static const XMLCh gStartDoctype[] =
{
chOpenAngle, chBang, chLatin_D, chLatin_O, chLatin_C, chLatin_T,
chLatin_Y, chLatin_P, chLatin_E, chSpace, chNull
};
static const XMLCh gPublic[] =
{
chLatin_P, chLatin_U, chLatin_B, chLatin_L, chLatin_I,
chLatin_C, chSpace, chDoubleQuote, chNull
};
static const XMLCh gSystem[] =
{
chLatin_S, chLatin_Y, chLatin_S, chLatin_T, chLatin_E,
chLatin_M, chSpace, chDoubleQuote, chNull
};
static const XMLCh gStartEntity[] =
{
chOpenAngle, chBang, chLatin_E, chLatin_N, chLatin_T, chLatin_I,
chLatin_T, chLatin_Y, chSpace, chNull
};
static const XMLCh gNotation[] =
{
chLatin_N, chLatin_D, chLatin_A, chLatin_T, chLatin_A,
chSpace, chDoubleQuote, chNull
};
// ---------------------------------------------------------------------------
// Local classes
// ---------------------------------------------------------------------------
class DOMPrintFormatTarget : public XMLFormatTarget
{
public:
DOMPrintFormatTarget() {};
~DOMPrintFormatTarget() {};
// -----------------------------------------------------------------------
// Implementations of the format target interface
// -----------------------------------------------------------------------
void writeChars(const XMLByte* const toWrite,
const XMLSize_t count,
XMLFormatter * const formatter)
{
// Surprisingly, Solaris was the only platform on which
// required the char* cast to print out the string correctly.
// Without the cast, it was printing the pointer value in hex.
// Quite annoying, considering every other platform printed
// the string with the explicit cast to char* below.
cout.write((char *) toWrite, (int) count);
};
private:
// -----------------------------------------------------------------------
// Unimplemented methods.
// -----------------------------------------------------------------------
DOMPrintFormatTarget(const DOMPrintFormatTarget& other);
void operator=(const DOMPrintFormatTarget& rhs);
};
// ---------------------------------------------------------------------------
// ostream << DOMNode
//
// Stream out a DOM node, and, recursively, all of its children. This
// function is the heart of writing a DOM tree out as XML source. Give it
// a document node and it will do the whole thing.
// ---------------------------------------------------------------------------
ostream& operator<<(ostream& target, DOMNode* toWrite)
{
// Get the name and value out for convenience
const XMLCh* nodeName = toWrite->getNodeName();
const XMLCh* nodeValue = toWrite->getNodeValue();
XMLSize_t lent = XMLString::stringLen(nodeValue);
switch (toWrite->getNodeType())
{
case DOMNode::TEXT_NODE:
{
gFormatter->formatBuf(nodeValue,
lent, XMLFormatter::CharEscapes);
break;
}
case DOMNode::PROCESSING_INSTRUCTION_NODE :
{
*gFormatter << XMLFormatter::NoEscapes << gStartPI << nodeName;
if (lent > 0)
{
*gFormatter << chSpace << nodeValue;
}
*gFormatter << XMLFormatter::NoEscapes << gEndPI;
break;
}
case DOMNode::DOCUMENT_NODE :
{
DOMNode *child = toWrite->getFirstChild();
while( child != 0)
{
target << child;
// add linefeed in requested output encoding
*gFormatter << chLF;
target << flush;
child = child->getNextSibling();
}
break;
}
case DOMNode::ELEMENT_NODE :
{
// The name has to be representable without any escapes
*gFormatter << XMLFormatter::NoEscapes
<< chOpenAngle << nodeName;
// Output the element start tag.
// Output any attributes on this element
DOMNamedNodeMap *attributes = toWrite->getAttributes();
XMLSize_t attrCount = attributes->getLength();
for (XMLSize_t i = 0; i < attrCount; i++)
{
DOMNode *attribute = attributes->item(i);
//
// Again the name has to be completely representable. But the
// attribute can have refs and requires the attribute style
// escaping.
//
*gFormatter << XMLFormatter::NoEscapes
<< chSpace << attribute->getNodeName()
<< chEqual << chDoubleQuote
<< XMLFormatter::AttrEscapes
<< attribute->getNodeValue()
<< XMLFormatter::NoEscapes
<< chDoubleQuote;
}
//
// Test for the presence of children, which includes both
// text content and nested elements.
//
DOMNode *child = toWrite->getFirstChild();
if (child != 0)
{
// There are children. Close start-tag, and output children.
// No escapes are legal here
*gFormatter << XMLFormatter::NoEscapes << chCloseAngle;
while( child != 0)
{
target << child;
child = child->getNextSibling();
}
//
// Done with children. Output the end tag.
//
*gFormatter << XMLFormatter::NoEscapes << gEndElement
<< nodeName << chCloseAngle;
}
else
{
//
// There were no children. Output the short form close of
// the element start tag, making it an empty-element tag.
//
*gFormatter << XMLFormatter::NoEscapes << chForwardSlash << chCloseAngle;
}
break;
}
case DOMNode::ENTITY_REFERENCE_NODE:
{
//DOMNode *child;
#if 0
for (child = toWrite.getFirstChild();
child != 0;
child = child.getNextSibling())
{
target << child;
}
#else
//
// Instead of printing the refernece tree
// we'd output the actual text as it appeared in the xml file.
// This would be the case when -e option was chosen
//
*gFormatter << XMLFormatter::NoEscapes << chAmpersand
<< nodeName << chSemiColon;
#endif
break;
}
case DOMNode::CDATA_SECTION_NODE:
{
*gFormatter << XMLFormatter::NoEscapes << gStartCDATA
<< nodeValue << gEndCDATA;
break;
}
case DOMNode::COMMENT_NODE:
{
*gFormatter << XMLFormatter::NoEscapes << gStartComment
<< nodeValue << gEndComment;
break;
}
case DOMNode::DOCUMENT_TYPE_NODE:
{
DOMDocumentType *doctype = (DOMDocumentType *)toWrite;;
*gFormatter << XMLFormatter::NoEscapes << gStartDoctype
<< nodeName;
const XMLCh* id = doctype->getPublicId();
if (id != 0)
{
*gFormatter << XMLFormatter::NoEscapes << chSpace << gPublic
<< id << chDoubleQuote;
id = doctype->getSystemId();
if (id != 0)
{
*gFormatter << XMLFormatter::NoEscapes << chSpace
<< chDoubleQuote << id << chDoubleQuote;
}
}
else
{
id = doctype->getSystemId();
if (id != 0)
{
*gFormatter << XMLFormatter::NoEscapes << chSpace << gSystem
<< id << chDoubleQuote;
}
}
id = doctype->getInternalSubset();
if (id !=0)
*gFormatter << XMLFormatter::NoEscapes << chOpenSquare
<< id << chCloseSquare;
*gFormatter << XMLFormatter::NoEscapes << chCloseAngle;
break;
}
case DOMNode::ENTITY_NODE:
{
*gFormatter << XMLFormatter::NoEscapes << gStartEntity
<< nodeName;
const XMLCh * id = ((DOMEntity *)toWrite)->getPublicId();
if (id != 0)
*gFormatter << XMLFormatter::NoEscapes << gPublic
<< id << chDoubleQuote;
id = ((DOMEntity *)toWrite)->getSystemId();
if (id != 0)
*gFormatter << XMLFormatter::NoEscapes << gSystem
<< id << chDoubleQuote;
id = ((DOMEntity *)toWrite)->getNotationName();
if (id != 0)
*gFormatter << XMLFormatter::NoEscapes << gNotation
<< id << chDoubleQuote;
*gFormatter << XMLFormatter::NoEscapes << chCloseAngle << chLF;
break;
}
default:
cerr << "Unrecognized node type = "
<< (long)toWrite->getNodeType() << endl;
}
return target;
}
// --------------------------------------------------------------------------------
// End of outputter
// --------------------------------------------------------------------------------
class DOMMemFormatTarget : public XMLFormatTarget
{
public:
unsigned char * buffer; // Buffer to write to
DOMMemFormatTarget() : buffer(NULL) {};
~DOMMemFormatTarget() {};
void setBuffer (unsigned char * toSet) {buffer = toSet;};
// -----------------------------------------------------------------------
// Implementations of the format target interface
// -----------------------------------------------------------------------
void writeChars(const XMLByte* const toWrite,
const XMLSize_t count,
XMLFormatter * const formatter)
{
// Surprisingly, Solaris was the only platform on which
// required the char* cast to print out the string correctly.
// Without the cast, it was printing the pointer value in hex.
// Quite annoying, considering every other platform printed
// the string with the explicit cast to char* below.
memcpy(buffer, (char *) toWrite, (int) count);
buffer[count] = '\0';
};
private:
// -----------------------------------------------------------------------
// Unimplemented methods.
// -----------------------------------------------------------------------
DOMMemFormatTarget(const DOMMemFormatTarget& other);
void operator=(const DOMMemFormatTarget& rhs);
};
// ---------------------------------------------------------------------------
// ostream << DOMString
//
// Stream out a DOM string. Doing this requires that we first transcode
// to char * form in the default code page for the system
// ---------------------------------------------------------------------------
DOMPrintFormatTarget *DOMtarget;
DOMMemFormatTarget *MEMtarget;
XMLFormatter *formatter, *MEMformatter;
unsigned char *charBuffer;
void printUsage(void) {
cerr << "\nUsage: templatesign <key options> <file to sign>\n\n";
#if defined (XSEC_HAVE_OPENSSL)
cerr << " Where <key options> are one of :\n\n";
cerr << " --x509subjectname/-s <distinguished name>\n";
cerr << " <distinguished name> will be set as SubjectName in x509\n";
cerr << " --dsakey/-d <dsa private key file> <password>\n";
cerr << " <dsa private key file> contains a PEM encoded private key\n";
cerr << " <password> is the password used to decrypt the key file\n";
# if defined (XSEC_HAVE_WINCAPI)
cerr << " NOTE: Not usable if --wincapi previously set\n";
# endif
# if defined(XSEC_OPENSSL_HAVE_EC)
cerr << " --eckey/-e <ec private key file> <password>\n";
cerr << " <ec private key file> contains a PEM encoded private key\n";
cerr << " <password> is the password used to decrypt the key file\n";
# endif
# if defined (XSEC_HAVE_WINCAPI)
cerr << " NOTE: Not usable if --wincapi previously set\n";
# endif
cerr << " --rsakey/-r <rsa private key file> <password>\n";
cerr << " <rsa privatekey file> contains a PEM encoded private key\n";
cerr << " <password> is the password used to decrypt the key file\n";
cerr << " --x509cert/-x <filename>\n";
cerr << " <filename> contains a PEM certificate to be added as a KeyInfo\n";
#endif
cerr << " --hmackey/-h <string>\n";
cerr << " <string> is the hmac key to set\n";
cerr << " --clearkeys/-c\n";
cerr << " Clears out any current KeyInfo elements in the file\n";
#if defined(XSEC_HAVE_WINCAPI)
cerr << " --windss/-wd\n";
cerr << " Use the default user AT_SIGNATURE key from default\n";
cerr << " Windows DSS CSP\n";
cerr << " --winrsa/-wr\n";
cerr << " Use the default user AT_SIGNATURE key from default\n";
cerr << " Windows RSA CSP\n";
cerr << " --winhmac/-wh <string>\n";
cerr << " Create a windows HMAC key using <string> as the password.\n";
cerr << " Uses a SHA-1 hash of the password to derive a key\n";
#if defined (CRYPT_ACQUIRE_CACHE_FLAG)
cerr << " --wincer/-wc <Subject Name>\n";
cerr << " Use the private key associated with the named certificate in the Windows certificate store\n";
#endif /* CRYPT_ACQUIRE_CACHE_FLAG */
cerr << " --windsskeyinfo/-wdi\n";
cerr << " Clear KeyInfo elements and insert DSS parameters from windows key\n";
cerr << " --winrsakeyinfo/-wri\n";
cerr << " Clear KeyInfo elements and insert RSA parameters from windows key\n";
#endif
}
int main(int argc, char **argv) {
XSECCryptoKey * key = NULL;
DSIGKeyInfoX509 * keyInfoX509 = NULL;
const char * x509SubjectName = NULL;
#if defined (XSEC_HAVE_OPENSSL)
OpenSSLCryptoX509 * certs[128];
#endif
int certCount = 0;
int paramCount;
bool clearKeyInfo = false;
#if defined(XSEC_HAVE_WINCAPI)
HCRYPTPROV win32DSSCSP = 0; // Crypto Provider
HCRYPTPROV win32RSACSP = 0; // Crypto Provider
bool winDssKeyInfo = false;
bool winRsaKeyInfo = false;
WinCAPICryptoKeyDSA * winKeyDSA = NULL;
WinCAPICryptoKeyRSA * winKeyRSA = NULL;
#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;
}
#if defined (XSEC_HAVE_OPENSSL)
// Initialize 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);
#endif
if (argc < 2) {
printUsage();
exit (1);
}
paramCount = 1;
while (paramCount < argc - 1) {
// Run through all parameters
if (_stricmp(argv[paramCount], "--x509subjectname") == 0 || _stricmp(argv[paramCount], "-s") == 0) {
if (paramCount +2 >= argc) {
printUsage();
exit(1);
}
// Get the subject name
x509SubjectName = argv[paramCount + 1];
paramCount += 2;
}
#if defined (XSEC_HAVE_OPENSSL)
else if (_stricmp(argv[paramCount], "--dsakey") == 0 || _stricmp(argv[paramCount], "-d") == 0
|| _stricmp(argv[paramCount], "--rsakey") == 0 || _stricmp(argv[paramCount], "-r") == 0
# if defined(XSEC_OPENSSL_HAVE_EC)
|| _stricmp(argv[paramCount], "--eckey") == 0 || _stricmp(argv[paramCount], "-e") == 0
# endif
) {
// OpenSSL Key
if (paramCount + 3 >= argc) {
printUsage();
exit (1);
}
if (key != 0) {
cerr << "\nError loading private key - another key already loaded\n\n";
printUsage();
exit(1);
}
// Load the signing key
// For now just read a particular file
BIO * bioKey;
if ((bioKey = BIO_new(BIO_s_file())) == NULL) {
cerr << "Error opening private key file\n\n";
exit (1);
}
if (BIO_read_filename(bioKey, argv[paramCount + 1]) <= 0) {
cerr << "Error opening private key file\n\n";
exit (1);
}
EVP_PKEY * pkey;
pkey = PEM_read_bio_PrivateKey(bioKey,NULL,NULL,argv[paramCount + 2]);
if (pkey == NULL) {
cerr << "Error loading private key\n\n";
ERR_print_errors(bio_err);
exit (1);
}
if (_stricmp(argv[paramCount], "--dsakey") == 0 || _stricmp(argv[paramCount], "-d") == 0) {
// Check type is correct
if (EVP_PKEY_id(pkey) != EVP_PKEY_DSA) {
cerr << "DSA Key requested, but OpenSSL loaded something else\n";
exit (1);
}
// Create the XSEC OpenSSL interface
key = new OpenSSLCryptoKeyDSA(pkey);
}
#if defined(XSEC_OPENSSL_HAVE_EC)
else if (_stricmp(argv[paramCount], "--eckey") == 0 || _stricmp(argv[paramCount], "-e") == 0) {
// Check type is correct
if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) {
cerr << "EC Key requested, but OpenSSL loaded something else\n";
exit (1);
}
// Create the XSEC OpenSSL interface
key = new OpenSSLCryptoKeyEC(pkey);
}
#endif
else {
if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
cerr << "RSA Key requested, but OpenSSL loaded something else\n";
exit (1);
}
key = new OpenSSLCryptoKeyRSA(pkey);
}
EVP_PKEY_free(pkey);
BIO_free(bioKey);
paramCount += 3;
} /* argv[1] = "dsa/rsa/ec" */
else if (_stricmp(argv[paramCount], "--x509cert") == 0 || _stricmp(argv[paramCount], "-x") == 0) {
// X509Data keyInfo
if (paramCount + 2 >= argc) {
printUsage();
exit (1);
}
// Load the cert.
// For now just read a particular file
BIO * bioX509;
if ((bioX509 = BIO_new(BIO_s_file())) == NULL) {
cerr << "Error opening file\n\n";
exit (1);
}
if (BIO_read_filename(bioX509, argv[paramCount + 1]) <= 0) {
cerr << "Error opening X509 Certificate " << argv[paramCount + 1] << "\n\n";
exit (1);
}
X509 * x
;
x = PEM_read_bio_X509_AUX(bioX509,NULL,NULL,NULL);
if (x == NULL) {
cerr << "Error loading certificate\n\n";
ERR_print_errors(bio_err);
exit (1);
}
// Create the XSEC OpenSSL interface - used only to translate to Base64
certs[certCount++] = new OpenSSLCryptoX509(x);
X509_free(x);
BIO_free(bioX509);
paramCount += 2;
} /* argv[1] = "--x509cert" */
else
#endif
if (_stricmp(argv[paramCount], "--hmackey") == 0 || _stricmp(argv[paramCount], "-h") == 0) {
XSECCryptoKeyHMAC* hmacKey = XSECPlatformUtils::g_cryptoProvider->keyHMAC();
hmacKey->setKey((unsigned char *) argv[paramCount + 1], (unsigned int) strlen(argv[paramCount + 1]));
key = hmacKey;
paramCount += 2;
}
else if (_stricmp(argv[paramCount], "--clearkeys") == 0 || _stricmp(argv[paramCount], "-c") == 0) {
clearKeyInfo = true;
paramCount += 1;
}
#if defined (XSEC_HAVE_WINCAPI)
else if (_stricmp(argv[paramCount], "--windss") == 0 || _stricmp(argv[paramCount], "-wd") == 0) {
WinCAPICryptoProvider * cp;
// First set windows as the crypto provider
cp = new WinCAPICryptoProvider();
XSECPlatformUtils::SetCryptoProvider(cp);
// Now set the key
if (!CryptAcquireContext(&win32DSSCSP,
NULL,
NULL,
PROV_DSS,
0)) {
cerr << "Error acquiring Crypto context - Attempting to generate new key pair" << endl;
// Attempt to gen a new keyset
if (!CryptAcquireContext(&win32DSSCSP,
NULL,
NULL,
PROV_DSS,
CRYPT_NEWKEYSET)) {
cerr << "Error acquiring DSS Crypto Service Provider with new keyset" << endl;
return 2;
}
else {
HCRYPTKEY k;
if (!CryptGenKey(win32DSSCSP, AT_SIGNATURE, CRYPT_EXPORTABLE, &k)) {
cerr << "Error generating DSS keyset" << endl;
return 2;
}
CryptDestroyKey(k);
}
}
winKeyDSA = new WinCAPICryptoKeyDSA(win32DSSCSP, AT_SIGNATURE, true);
key = winKeyDSA;
paramCount++;
}
else if (_stricmp(argv[paramCount], "--winrsa") == 0 || _stricmp(argv[paramCount], "-wr") == 0) {
WinCAPICryptoProvider * cp;
cp = new WinCAPICryptoProvider();
XSECPlatformUtils::SetCryptoProvider(cp);
if (!CryptAcquireContext(&win32RSACSP,
NULL,
NULL,
PROV_RSA_FULL,
0)) {
cerr << "Error acquiring Crypto context - Attempting to generate new RSA key pair" << endl;
// Attempt to gen a new keyset
if (!CryptAcquireContext(&win32RSACSP,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET)) {
cerr << "Error acquiring RSA Crypto Service Provider with new keyset" << endl;
return 2;
}
else {
HCRYPTKEY k;
if (!CryptGenKey(win32RSACSP, AT_SIGNATURE, CRYPT_EXPORTABLE, &k)) {
cerr << "Error generating RSA keyset" << endl;
return 2;
}
CryptDestroyKey(k);
}
}
winKeyRSA = new WinCAPICryptoKeyRSA(win32RSACSP, AT_SIGNATURE, true);
key = winKeyRSA;
paramCount++;
}
else if (_stricmp(argv[paramCount], "--winhmac") == 0 || _stricmp(argv[paramCount], "-wh") == 0) {
WinCAPICryptoProvider * cp;
// Obtain default PROV_RSA, with default user key container
// Note we open in VERIFYCONTEXT as we do not require a assymetric key pair
if (!CryptAcquireContext(&win32RSACSP,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
cerr << "Error acquiring RSA Crypto Service Provider" << endl;
return 2;
}
cp = new WinCAPICryptoProvider();
XSECPlatformUtils::SetCryptoProvider(cp);
paramCount++;
HCRYPTKEY k;
HCRYPTHASH h;
BOOL fResult = CryptCreateHash(
win32RSACSP,
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(
win32RSACSP,
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(win32RSACSP);
hk->setWinKey(k);
key = hk;
CryptDestroyHash(h);
paramCount++;
}
else if (_stricmp(argv[paramCount], "--windsskeyinfo") == 0 || _stricmp(argv[paramCount], "-wdi") == 0) {
winDssKeyInfo = true;
paramCount++;
}
else if (_stricmp(argv[paramCount], "--winrsakeyinfo") == 0 || _stricmp(argv[paramCount], "-wri") == 0) {
winRsaKeyInfo = true;
paramCount++;
}
// Need to find a better way to check this
// If CryptAcquireCertificatePrivateKey is not defined in the included
// version of wincapi.h, CRYPT_ACQUIRE_CACHE_FLAG will not be set
#if defined (CRYPT_ACQUIRE_CACHE_FLAG)
// Code provided by Milan Tomic
//Please note that this example below use CryptAcquireCertificatePrivateKey() function
//which is not declared in wincrypt.h that ships with VC++ 6. If you would like to run
//this example you'll need to replace your old wincrypt.h and crypt32.lib with new versions.
//This example below is compatible with Windows 98/IE 5 and above OS/IE versions.
else if (_stricmp(argv[paramCount], "--wincer") == 0 || _stricmp(argv[paramCount], "-wc") == 0) {
WinCAPICryptoProvider * cp;
PCCERT_CONTEXT pSignerCert = NULL;
DWORD dwKeySpec;
HCERTSTORE hStoreHandle;
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
// Obtain default PROV_DSS with default user key container
if (!CryptAcquireContext(&win32DSSCSP,
NULL,
NULL,
PROV_DSS,
0)) {
cerr << "Error acquiring DSS Crypto Service Provider" << endl;
return 2;
}//*/
// Open 'Personal' certificate store
if (!(hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY"))) {
cerr << "Error opening 'Personal' store." << endl;
return 2;
}
// Find desired cerificate
if (!(pSignerCert = CertFindCertificateInStore(hStoreHandle,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR_A,
argv[paramCount+1],
NULL))) {
cerr << "Can't find '" << argv[paramCount+1] << "' certificate in 'Personal' store." << endl;
return 2;
}
// Now get certificate's private key
if (!CryptAcquireCertificatePrivateKey(pSignerCert,
0,
NULL,
&win32RSACSP,
&dwKeySpec,
NULL)) {
cerr << "Can't acquire private key of '" << argv[paramCount+1] << "' certificate." << endl;
exit(1);
}
cp = new WinCAPICryptoProvider();
XSECPlatformUtils::SetCryptoProvider(cp);
HCRYPTKEY k;
BOOL fResult = CryptGetUserKey(
win32RSACSP,
dwKeySpec,
&k);
if (!fResult || k == 0) {
cerr << "Error obtaining default user (AT_SIGNATURE or AT_KEYEXCHANGE) key from windows RSA provider.\n";
exit(1);
};
winKeyRSA = new WinCAPICryptoKeyRSA(win32RSACSP, k);
key = winKeyRSA;
paramCount += 2;
CertFreeCertificateContext(pSignerCert);
CertCloseStore(hStoreHandle, 0);
}
#endif /* CRYPT_ACQUIRE_CACHE_FLAG */
#endif /* XSEC_HAVE_WINCAPI */
else {
printUsage();
exit(1);
}
}
// Create and set up the parser
XercesDOMParser * parser = new XercesDOMParser;
parser->setDoNamespaces(true);
parser->setCreateEntityReferenceNodes(true);
// Now parse out file
bool errorsOccured = false;
XMLSize_t errorCount = 0;
try
{
parser->parse(argv[argc - 1]);
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;
exit (1);
}
/*
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;
exit(1);
}
XSECProvider * prov = new XSECProvider;
DSIGSignature * sig = prov->newSignatureFromDOM(theDOM, sigNode);
// Map out base path of the file
char * filename=argv[argc-1];
#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';
sig->getURIResolver()->setBaseURI(MAKE_UNICODE_STRING(baseURI));
#if XSEC_HAVE_GETCWD_DYN
free(path);
free(baseURI);
#endif
try {
sig->load();
if (clearKeyInfo == true)
sig->clearKeyInfo();
if (key != NULL)
sig->setSigningKey(key);
sig->sign();
// Add any KeyInfo elements
#if defined(XSEC_HAVE_WINCAPI)
if (winDssKeyInfo == true && winKeyDSA != NULL) {
char pBuf[1024];
char qBuf[1024];
char gBuf[1024];
char yBuf[1024];
unsigned int i;
i = winKeyDSA->getPBase64BigNums((char *) pBuf, 1024);
pBuf[i] = '\0';
i = winKeyDSA->getQBase64BigNums((char *) qBuf, 1024);
qBuf[i] = '\0';
i = winKeyDSA->getGBase64BigNums((char *) gBuf, 1024);
gBuf[i] = '\0';
i = winKeyDSA->getYBase64BigNums((char *) yBuf, 1024);
yBuf[i] = '\0';
sig->clearKeyInfo();
sig->appendDSAKeyValue(
MAKE_UNICODE_STRING(pBuf),
MAKE_UNICODE_STRING(qBuf),
MAKE_UNICODE_STRING(gBuf),
MAKE_UNICODE_STRING(yBuf));
}
if (winRsaKeyInfo == true && winKeyRSA != NULL) {
char eBuf[1024];
char mBuf[1024];
unsigned int i;
i = winKeyRSA->getExponentBase64BigNums((char *) eBuf, 1024);
eBuf[i] = '\0';
i = winKeyRSA->getModulusBase64BigNums((char *) mBuf, 1024);
mBuf[i] = '\0';
sig->clearKeyInfo();
sig->appendRSAKeyValue(
MAKE_UNICODE_STRING(mBuf),
MAKE_UNICODE_STRING(eBuf));
}
#endif
#if defined (XSEC_HAVE_OPENSSL)
if (certCount > 0) {
int i;
// Have some certificates - see if there is already an X509 list
DSIGKeyInfoList * kiList = sig->getKeyInfoList();
int kiSize = (int) kiList->getSize();
for (i = 0; i < kiSize; ++i) {
if (kiList->item(i)->getKeyInfoType() == DSIGKeyInfo::KEYINFO_X509) {
keyInfoX509 = (DSIGKeyInfoX509 *) kiList->item(i);
break;
}
}
if (keyInfoX509 == 0) {
// Not found - need to create
keyInfoX509 = sig->appendX509Data();
}
for (i = 0; i < certCount; ++i) {
keyInfoX509->appendX509Certificate(certs[i]->getDEREncodingSB().sbStrToXMLCh());
}
} /* certCount > 0 */
#endif
if (x509SubjectName != NULL) {
int i;
// Have some certificates - see if there is already an X509 list
DSIGKeyInfoList * kiList = sig->getKeyInfoList();
int kiSize = (int) kiList->getSize();
for (i = 0; i < kiSize; ++i) {
if (kiList->item(i)->getKeyInfoType() == DSIGKeyInfo::KEYINFO_X509) {
keyInfoX509 = (DSIGKeyInfoX509 *) kiList->item(i);
break;
}
}
if (keyInfoX509 == 0) {
// Not found - need to create
keyInfoX509 = sig->appendX509Data();
}
keyInfoX509->setX509SubjectName(MAKE_UNICODE_STRING(x509SubjectName));
} /* certCount > 0 */
}
catch (const XSECException &e) {
char * m = XMLString::transcode(e.getMsg());
cerr << "An error occurred during signing operation\n Message: "
<< m << endl;
XSEC_RELEASE_XMLCH(m);
errorsOccured = true;
exit (1);
}
catch (const XSECCryptoException &e) {
cerr << "A cryptographic error occurred during signature operation\n Message: "
<< e.getMsg() << endl;
errorsOccured = true;
exit(1);
}
catch (const NetAccessorException&) {
cerr << "A network error occurred during signing operation\n" << endl;
errorsOccured = true;
exit(1);
}
// Print out the result
DOMPrintFormatTarget* formatTarget = new DOMPrintFormatTarget();
const XMLCh* encNameStr = XMLString::transcode("UTF-8");
DOMNode *aNode = doc->getFirstChild();
if (aNode->getNodeType() == DOMNode::ENTITY_NODE)
{
const XMLCh* aStr = ((DOMEntity *)aNode)->getInputEncoding();
if (!strEquals(aStr, ""))
{
encNameStr = aStr;
}
}
XMLSize_t lent = XMLString::stringLen(encNameStr);
gEncodingName = new XMLCh[lent + 1];
XMLString::copyNString(gEncodingName, encNameStr, lent);
gEncodingName[lent] = 0;
gFormatter = new XMLFormatter("UTF-8", 0, formatTarget,
XMLFormatter::NoEscapes, gUnRepFlags);
cout << doc;
delete [] gEncodingName;
XMLCh * toRelease = (XMLCh *) encNameStr;
XSEC_RELEASE_XMLCH(toRelease);
delete gFormatter;
delete formatTarget;
#if defined (_WIN32) && defined (XSEC_HAVE_WINCAPI)
if (win32DSSCSP != 0)
CryptReleaseContext(win32DSSCSP,0);
if (win32RSACSP != 0)
CryptReleaseContext(win32RSACSP,0);
#endif
prov->releaseSignature(sig);
delete parser;
delete prov;
XSECPlatformUtils::Terminate();
#ifdef XSEC_HAVE_XALAN
XalanTransformer::terminate();
XPathEvaluator::terminate();
#endif
XMLPlatformUtils::Terminate();
return 0;
}