xsec/enc/WinCAPI/WinCAPICryptoKeyDSA.cpp (471 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 * * WinCAPICryptoKeyDSA := DSA Keys * * Author(s): Berin Lautenbach * * $Id$ * */ #include <xsec/enc/WinCAPI/WinCAPICryptoKeyDSA.hpp> #include <xsec/enc/WinCAPI/WinCAPICryptoProvider.hpp> #include <xsec/enc/XSCrypt/XSCryptCryptoBase64.hpp> #include <xsec/enc/XSECCryptoException.hpp> #include <xsec/enc/XSECCryptoUtils.hpp> #include <xsec/framework/XSECError.hpp> #if defined (XSEC_HAVE_WINCAPI) #include <xercesc/util/Janitor.hpp> XSEC_USING_XERCES(ArrayJanitor); WinCAPICryptoKeyDSA::WinCAPICryptoKeyDSA(HCRYPTPROV prov) { // Create a new key to be loaded as we go m_key = 0; m_p = prov; m_keySpec = 0; mp_P = NULL; mp_Q = NULL; mp_G = NULL; mp_Y = NULL; }; // "Hidden" WinCAPI constructor WinCAPICryptoKeyDSA::WinCAPICryptoKeyDSA(HCRYPTPROV prov, HCRYPTKEY k) : m_p(prov) { m_key = k; // NOTE - We OWN this handle m_keySpec = 0; mp_P = mp_Q = mp_G = mp_Y = NULL; m_PLen = m_QLen = m_GLen = m_YLen = 0; } WinCAPICryptoKeyDSA::WinCAPICryptoKeyDSA(HCRYPTPROV prov, DWORD keySpec, bool isPrivate) : m_p(prov) { if (isPrivate == false) { throw XSECCryptoException(XSECCryptoException::DSAError, "Public keys defined via keySpec ctor not yet supported"); } m_key = 0; m_keySpec = keySpec; mp_P = mp_Q = mp_G = mp_Y = NULL; m_PLen = m_QLen = m_GLen = m_YLen = 0; } WinCAPICryptoKeyDSA::~WinCAPICryptoKeyDSA() { // If we have a DSA, delete it if (m_key != 0) CryptDestroyKey(m_key); if (mp_P != NULL) delete[] mp_P; if (mp_Q != NULL) delete[] mp_Q; if (mp_G != NULL) delete[] mp_G; if (mp_Y != NULL) delete[] mp_Y; }; const XMLCh * WinCAPICryptoKeyDSA::getProviderName() const { return DSIGConstants::s_unicodeStrPROVWinCAPI; } // Generic key functions XSECCryptoKey::KeyType WinCAPICryptoKeyDSA::getKeyType() const { // Find out what we have if (m_key == NULL) { // For now we don't really understand Private Windows keys if (m_keySpec != 0) return KEY_DSA_PRIVATE; // Check if we have parameters loaded if (mp_P == NULL || mp_Q == NULL || mp_G == NULL || mp_Y == NULL) return KEY_NONE; else return KEY_DSA_PUBLIC; } if (m_keySpec != 0) return KEY_DSA_PAIR; // If we have m_key - it must be public return KEY_DSA_PUBLIC; } void WinCAPICryptoKeyDSA::loadPBase64BigNums(const char * b64, unsigned int len) { if (mp_P != NULL) { delete[] mp_P; mp_P = NULL; // In case we get an exception } mp_P = WinCAPICryptoProvider::b642WinBN(b64, len, m_PLen); } void WinCAPICryptoKeyDSA::loadQBase64BigNums(const char * b64, unsigned int len) { if (mp_Q != NULL) { delete[] mp_Q; mp_Q = NULL; // In case we get an exception } mp_Q = WinCAPICryptoProvider::b642WinBN(b64, len, m_QLen); } void WinCAPICryptoKeyDSA::loadGBase64BigNums(const char * b64, unsigned int len) { if (mp_G != NULL) { delete[] mp_G; mp_G = NULL; // In case we get an exception } mp_G = WinCAPICryptoProvider::b642WinBN(b64, len, m_GLen); } void WinCAPICryptoKeyDSA::loadYBase64BigNums(const char * b64, unsigned int len) { if (mp_Y != NULL) { delete[] mp_Y; mp_Y = NULL; // In case we get an exception } mp_Y = WinCAPICryptoProvider::b642WinBN(b64, len, m_YLen); } void WinCAPICryptoKeyDSA::loadJBase64BigNums(const char * b64, unsigned int len) { /* Do nothing */ } // -------------------------------------------------------------------------------- // Verify a signature encoded as a Base64 string // -------------------------------------------------------------------------------- void WinCAPICryptoKeyDSA::importKey(void) const { if (m_key != 0 || mp_P == NULL || mp_Q == NULL || mp_G == NULL || mp_Y == NULL) return; // Do some basic checks if ((m_QLen > 20) | (m_GLen > m_PLen) | (m_YLen > m_PLen)) { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Parameter lengths inconsistent"); } // Create a DSS Public-Key blob // First build a buffer to hold everything BYTE * blobBuffer; unsigned int blobBufferLen = WINCAPI_BLOBHEADERLEN + WINCAPI_DSSPUBKEYLEN + (3 * m_PLen) + 0x14 + WINCAPI_DSSSEEDLEN; XSECnew(blobBuffer, BYTE[blobBufferLen]); ArrayJanitor<BYTE> j_blobBuffer(blobBuffer); // Blob header BLOBHEADER * header = (BLOBHEADER *) blobBuffer; header->bType = PUBLICKEYBLOB; header->bVersion = 0x02; // We are using a version 2 blob header->reserved = 0; header->aiKeyAlg = CALG_DSS_SIGN; // Now the public key header DSSPUBKEY * pubkey = (DSSPUBKEY *) (blobBuffer + WINCAPI_BLOBHEADERLEN); pubkey->magic = 0x31535344; // ASCII encoding of DSS1 pubkey->bitlen = m_PLen * 8; // Number of bits in prime modulus // Now copy in each of the keys BYTE * i = (BYTE *) (pubkey); i += WINCAPI_DSSPUBKEYLEN; memcpy(i, mp_P, m_PLen); i+= m_PLen; // Q memcpy(i, mp_Q, m_QLen); i += m_QLen; // Pad with 0s unsigned int j; for (j = m_QLen; j < 20 ; ++j) *i++ = 0; // Generator memcpy(i, mp_G, m_GLen); i += m_GLen; // Pad for (j = m_GLen; j < m_PLen ; ++j) *i++ = 0; // Public key memcpy(i, mp_Y, m_YLen); i += m_YLen; // Pad for (j = m_YLen; j < m_PLen ; ++j) *i++ = 0; // Set seed to 0 for (j = 0; j < WINCAPI_DSSSEEDLEN; ++j) *i++ = 0xFF; // SEED Counter set to 0xFFFFFFFF will cause seed to be ignored // Now that we have the blob, import BOOL fResult = CryptImportKey( m_p, blobBuffer, blobBufferLen, 0, // Not signed 0, // No flags &m_key); if (fResult == 0) { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA Error attempting to import key parameters"); } } bool WinCAPICryptoKeyDSA::verifyBase64Signature(unsigned char * hashBuf, unsigned int hashLen, char * base64Signature, unsigned int sigLen) const { // Use the currently loaded key to validate the Base64 encoded signature if (m_key == 0) { // Try to import from the parameters importKey(); if (m_key == 0) { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Attempt to validate signature with empty key"); } } // Decode the signature unsigned char * rawSig; DWORD rawSigLen; XSECnew(rawSig, BYTE [sigLen]); ArrayJanitor<BYTE> j_rawSig(rawSig); // Decode the signature XSCryptCryptoBase64 b64; b64.decodeInit(); rawSigLen = b64.decode((unsigned char *) base64Signature, sigLen, rawSig, sigLen); rawSigLen += b64.decodeFinish(&rawSig[rawSigLen], sigLen - rawSigLen); // Reverse the sig - Windows stores integers as octet streams in little endian // order. The I2OSP algorithm used by XMLDSig to store integers is big endian BYTE rawSigFinal[40]; BYTE * j, *k, *l, *m; unsigned char rb[20]; unsigned char sb[20]; if (rawSigLen == 40) { j = rawSig; k = rawSig + 20; } else if (rawSigLen == 46 && ASN2DSASig(rawSig, rb, sb) == true) { j = rb; k = sb; } else { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA::VerifyBase64Signature - Expect 40 bytes in a DSA signature"); } l = rawSigFinal + 19; m = rawSigFinal + 39; while (l >= rawSigFinal) { *l-- = *j++; *m-- = *k++; } // Have to create a Windows hash object and feed in the hash BOOL fResult; HCRYPTHASH h; fResult = CryptCreateHash(m_p, CALG_SHA1, 0, 0, &h); if (!fResult) { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Error creating Windows Hash Object"); } // Feed the hash value into the newly created hash object fResult = CryptSetHashParam( h, HP_HASHVAL, hashBuf, 0); if (!fResult) { if (h) CryptDestroyHash(h); throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Error Setting Hash Value in Windows Hash object"); } // Now validate fResult = CryptVerifySignature( h, rawSigFinal, 40, m_key, NULL, 0); if (!fResult) { DWORD error = GetLastError(); /* For some reason, the default Microsoft DSS provider generally returns * NTE_FAIL (which denotes an internal failure in the provider) for a * failed signature rather than NTE_BAD_SIGNATURE */ if (error != NTE_BAD_SIGNATURE && error != NTE_FAIL) { if (h) CryptDestroyHash(h); throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Error occurred in DSA validation"); } if (h) CryptDestroyHash(h); return false; } if (h) CryptDestroyHash(h); return true; } // -------------------------------------------------------------------------------- // Sign and encode result as a Base64 string // -------------------------------------------------------------------------------- unsigned int WinCAPICryptoKeyDSA::signBase64Signature(unsigned char * hashBuf, unsigned int hashLen, char * base64SignatureBuf, unsigned int base64SignatureBufLen) const { // Sign a pre-calculated hash using this key if (m_keySpec == 0) { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Attempt to sign data a public or non-existent key"); } // Have to create a Windows hash object and feed in the hash BOOL fResult; HCRYPTHASH h; fResult = CryptCreateHash(m_p, CALG_SHA1, 0, 0, &h); if (!fResult) { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Error creating Windows Hash Object"); } // Feed the hash value into the newly created hash object fResult = CryptSetHashParam( h, HP_HASHVAL, hashBuf, 0); if (!fResult) { if (h) CryptDestroyHash(h); throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Error Setting Hash Value in Windows Hash object"); } // Now sign BYTE rawSig[50]; DWORD rawSigLen = 50; fResult = CryptSignHash( h, m_keySpec, NULL, 0, rawSig, &rawSigLen); if (!fResult || rawSigLen != 40) { if (h) CryptDestroyHash(h); throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Error occurred in DSA signing"); } if (h) CryptDestroyHash(h); // Now encode into a signature block BYTE rawSigFinal[40]; BYTE * i, * j, * m, * n; i = rawSig; j = rawSig + 20; m = rawSigFinal + 19; n = rawSigFinal + 39; while (m >= rawSigFinal) { *m-- = *i++; *n-- = *j++; } // Now encode XSCryptCryptoBase64 b64; b64.encodeInit(); unsigned int ret = b64.encode(rawSigFinal, 40, (unsigned char *) base64SignatureBuf, base64SignatureBufLen); ret += b64.encodeFinish((unsigned char *) &base64SignatureBuf[ret], base64SignatureBufLen - ret); return ret; } // -------------------------------------------------------------------------------- // Clone key // -------------------------------------------------------------------------------- XSECCryptoKey * WinCAPICryptoKeyDSA::clone() const { WinCAPICryptoKeyDSA * ret; XSECnew(ret, WinCAPICryptoKeyDSA(m_p)); if (m_key != 0) { // CryptDuplicateKey is not supported in Windows NT, so we need to export and then // reimport the key to get a copy BYTE keyBuf[2048]; DWORD keyBufLen = 2048; CryptExportKey(m_key, 0, PUBLICKEYBLOB, 0, keyBuf, &keyBufLen); // Now re-import CryptImportKey(m_p, keyBuf, keyBufLen, NULL, 0, &ret->m_key); } ret->m_PLen = m_PLen; if (mp_P != NULL) { XSECnew(ret->mp_P, BYTE[m_PLen]); memcpy(ret->mp_P, mp_P, m_PLen); } else ret->mp_P = NULL; ret->m_QLen = m_QLen; if (mp_Q != NULL) { XSECnew(ret->mp_Q, BYTE[m_QLen]); memcpy(ret->mp_Q, mp_Q, m_QLen); } else ret->mp_Q = NULL; ret->m_GLen = m_GLen; if (mp_G != NULL) { XSECnew(ret->mp_G, BYTE[m_GLen]); memcpy(ret->mp_G, mp_G, m_GLen); } else ret->mp_G = NULL; ret->m_YLen = m_YLen; if (mp_Y != NULL) { XSECnew(ret->mp_Y, BYTE[m_YLen]); memcpy(ret->mp_Y, mp_Y, m_YLen); } else ret->mp_Y = NULL; return ret; } // -------------------------------------------------------------------------------- // Some utility functions // -------------------------------------------------------------------------------- void WinCAPICryptoKeyDSA::loadParamsFromKey(void) { if (m_key == 0) { if (m_keySpec == 0) return; // See of we can get the user key if (!CryptGetUserKey(m_p, m_keySpec, &m_key)) return; } // Export key into a keyblob BOOL fResult; DWORD blobLen; fResult = CryptExportKey( m_key, 0, PUBLICKEYBLOB, 0, NULL, &blobLen); if (fResult == 0 || blobLen < 1) { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Error exporting public key"); } BYTE * blob; XSECnew(blob, BYTE[blobLen]); ArrayJanitor<BYTE> j_blob(blob); fResult = CryptExportKey( m_key, 0, PUBLICKEYBLOB, 0, blob, &blobLen); if (fResult == 0 || blobLen < 1) { throw XSECCryptoException(XSECCryptoException::DSAError, "WinCAPI:DSA - Error exporting public key"); } DSSPUBKEY * pk = (DSSPUBKEY *) ( blob + WINCAPI_BLOBHEADERLEN ); DWORD keyLen = pk->bitlen / 8; // Copy the keys BYTE * i = (BYTE *) ( pk ); i += WINCAPI_DSSPUBKEYLEN; if (mp_P != NULL) delete[] mp_P; XSECnew(mp_P, BYTE[keyLen]); memcpy(mp_P, i, keyLen); m_PLen = keyLen; i+=keyLen; if (mp_Q != NULL) delete[] mp_Q; m_QLen = 20; while (i[m_QLen - 1] == 0 && m_QLen > 0) m_QLen--; XSECnew(mp_Q, BYTE[m_QLen]); memcpy(mp_Q, i, m_QLen); i+=20; if (mp_G != NULL) delete[] mp_G; m_GLen = keyLen; while(i[m_GLen - 1] == 0 && m_GLen > 0) m_GLen--; XSECnew(mp_G, BYTE[m_GLen]); memcpy(mp_G, i, m_GLen); i+=keyLen; if (mp_Y != NULL) delete[] mp_Y; m_YLen = keyLen; while (i[m_YLen] == 0 && m_YLen > 0) m_YLen--; XSECnew(mp_Y, BYTE[m_YLen]); memcpy(mp_Y, i, m_YLen); } unsigned int WinCAPICryptoKeyDSA::getPBase64BigNums(char * b64, unsigned int len) { if (m_key == 0 && m_keySpec == 0 && mp_P == NULL) { return 0; // Nothing we can do } if (mp_P == NULL) { loadParamsFromKey(); } unsigned int bLen; unsigned char * b = WinCAPICryptoProvider::WinBN2b64(mp_P, m_PLen, bLen); if (bLen > len) bLen = len; memcpy(b64, b, bLen); delete[] b; return bLen; } unsigned int WinCAPICryptoKeyDSA::getQBase64BigNums(char * b64, unsigned int len) { if (m_key == 0 && m_keySpec == 0 && mp_Q == NULL) { return 0; // Nothing we can do } if (mp_Q == NULL) { loadParamsFromKey(); } unsigned int bLen; unsigned char * b = WinCAPICryptoProvider::WinBN2b64(mp_Q, m_QLen, bLen); if (bLen > len) bLen = len; memcpy(b64, b, bLen); delete[] b; return bLen; } unsigned int WinCAPICryptoKeyDSA::getGBase64BigNums(char * b64, unsigned int len) { if (m_key == 0 && m_keySpec == 0 && mp_G == NULL) { return 0; // Nothing we can do } if (mp_G == NULL) { loadParamsFromKey(); } unsigned int bLen; unsigned char * b = WinCAPICryptoProvider::WinBN2b64(mp_G, m_GLen, bLen); if (bLen > len) bLen = len; memcpy(b64, b, bLen); delete[] b; return bLen; } unsigned int WinCAPICryptoKeyDSA::getYBase64BigNums(char * b64, unsigned int len) { if (m_key == 0 && m_keySpec == 0 && mp_Y == NULL) { return 0; // Nothing we can do } if (mp_Y == NULL) { loadParamsFromKey(); } unsigned int bLen; unsigned char * b = WinCAPICryptoProvider::WinBN2b64(mp_Y, m_YLen, bLen); if (bLen > len) bLen = len; memcpy(b64, b, bLen); delete[] b; return bLen; } #endif /* XSEC_HAVE_WINCAPI */