Environment :- XML-Security-C - CVS version 17/05/05 XALAN-1.7.0 and XERCES-2.5.0 Compiler VC7 (2003) #define HAVE_OPENSSL 0 #define HAVE_WINCAPI 1 #define USING_XALAN
I am trying to validate a SOAP response signed using the Verisign TSIK toolkit. The XML-SOAP response has a detached public key which I have installed into the "current user" store. There is no "KeyInfo" or "Object" in my XML document but this seems to be acceptable to DSIGSignature::load(); I don't seem to be able to verify the public key via an WinCAPICryptoX509 using the WinCAPICryptoX509(PCCERT_CONTEXT) constructor - It loads the key correctly but does not set up the DSA/RSA provider handles which are needed later on during processing. Using the mscrypto API - I find the certificate context and create a X509 certificate with it. WinCAPICryptoX509 * x509 = new WinCAPICryptoX509(certContext); sig->setSigningKey(x509->clonePublicKey()); // FAILS HERE.... However WinCAPICryptoX509::clonePublicKey() fails due to the fact that WinCAPICryptoX509::m_pDSS has not been set. The only way I seem able to set WinCAPICryptoX509::m_pDSS is via the constructor WinCAPICryptoX509::WinCAPICryptoX509(HCRYPTPROV provRSA, HCRYPTPROV provDSS) but if I do this I am then unable to set WinCAPICryptoX509::mp_certContext certificate context handle. (*) Is there a call similar to WinCAPICryptoX509::SetDSAProvider() ? (*) As an aside the MSDN documentation for CryptImportPublicKeyInfo() states that it "is always acceptable" to use X509_ASN_ENCODING | PKCS_7_ASN_ENCODING for the DWORD dwCertEncodingType parameter - [is this relevant?] To generate a DSS crypto provider handle I am using WinCAPICryptoProvider * cp = new WinCAPICryptoProvider(NULL, NULL, CRYPT_MACHINE_KEYSET); XSECPlatformUtils::SetCryptoProvider(cp); (*) Would this handle be the correct one to send to WinCAPICryptoX509::SetDSAProvider() if it existed ie WinCAPICryptoX509::SetDSAProvider(cp->getProviderDSS()); I have also tried WinCAPICryptoProvider *cp = new WinCAPICryptoProvider(NULL, NULL, CRYPT_MACHINE_KEYSET); WinCAPICryptoX509 *x509 = (WinCAPICryptoX509 *)cp->X509(); At this stage there is no way I seem able to set the X509 certificate context eg x509->mp_certContext = certContext; // no class member fn() exists to allow me to do this. (*) If I manually set the value of m_pDSS (via the debugger) inside WinCAPICryptoX509::clonePublicKey() with a handle derived from calling new WinCAPICryptoProvider(NULL, NULL, CRYPT_MACHINE_KEYSET). WinCAPICryptoX509::clonePublicKey() does not fail and "seems" to generate a valid Key. Calling sig->Verify() however gives me the error message... WinCAPI:DSA::VerifyBase64Signature - Expect 40 bytes in a DSA signature -- Does this mean my document is fundementally rubbish ?? Looking at the public key details I can see. [Signature Algorithm] sha1DSA [Public Key] DSA (1024 Bits) [Thumbprint Algorithm] sha1 Eg... WinCAPICryptoProvider *cp = new WinCAPICryptoProvider(NULL, NULL, CRYPT_MACHINE_KEYSET); // get value of cp->m_provDSS WinCAPICryptoX509::clonePublicKey() { HCRYPTKEY key; BOOL fResult; &m_pDSS = 1415288 /* set to valid handle gained by WinCAPICryptoProvider(NULL, NULL, CRYPT_MACHINE_KEYSET); (using debugger) if(getPublicKeyType() == XSECCryptoKey::KEY_DSA_PUBLIC) { fResult= CryptImportPublicKeyInfo(m_pDSS, X509_ASN_ENCODING, &(mp_certContext->pCertInfo->SubjectPublicKeyInfo), &key); } } Can someone verify my steps are correct.. Load the document into DSIGSignature() CertOpenStore() certCtxt = CertFindCertificateInStore() Create X509 from certCtxt Load the signature inside DSIGSignature() Set the DSIGSignature signing key to a cloned copy of the public key stored in X509 (do I have to clone) verify the DSIGSignature // // my source code....based heavily on SimpleValidate.cpp // #define IDATTRIBUTENS "http://schemas.xmlsoap.org/ws/2002/07/utility" #define IDATTRIBUTENAME "Id" #define MY_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) #define SIGNER_NAME L"TESTSIGNER" #define CERT_STORE_NAME L"MY" int main (int argc, char **argv) { //.. //.. Reading and validation code ommitted //.. // create a signature object to validate the document XSECProvider prov; DSIGSignature * sig = prov.newSignatureFromDOM(doc); sig->registerIdAttributeName(MAKE_UNICODE_STRING("ID")); // Register defined attribute name sig->registerIdAttributeNameNS(MAKE_UNICODE_STRING(IDATTRIBUTENS), MAKE_UNICODE_STRING(IDATTRIBUTENAME)); // using MSCryptoAPI try { HCERTSTORE certStore = NULL; PCCERT_CONTEXT certContext = NULL; // open the microsoft certiticate store certStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, CERT_STORE_NAME); if(certStore == NULL) { cout << "Certificate store cannot be opened\n"; exit (1); } // find signer's certificate certContext = CertFindCertificateInStore(certStore, MY_TYPE, 0, CERT_FIND_SUBJECT_STR, SIGNER_NAME, NULL); if(certContext == NULL) { cout << "unable to find signers certificate\n"; exit (1); } // build an x509 certificate from my cert context WinCAPICryptoX509 * x509 = new WinCAPICryptoX509(certContext); sig->load(); // no keyinfo in my XML sig->setSigningKey(x509->clonePublicKey()); // FAILS HERE.... if (sig->verify()) { cout << "Signature Valid\n"; } else { char * err = XMLString::transcode(sig->getErrMsgs()); cout << "Incorrect Signature\n"; cout << err << endl; XSEC_RELEASE_XMLCH(err); } } catch (XSECException &e) { cerr << "An error occured during a signature load\n Message: " << e.getMsg() << endl; exit(1); } catch (XSECCryptoException &e) { cerr << "An error occured in the XML-Security-C Crypto routines\n Message: " << e.getMsg() << endl; exit(1); } return 0; }