void CEstEIDIEPluginBHO::signWithCNG(BSTR id, BSTR hash, BSTR *signature) { LOG_LOCATION; #ifdef WIN_XP EstEID_log("WARNING: CNG is not supported for windows XP"); #else int methodResult = true; #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) SECURITY_STATUS secStatus = ERROR_SUCCESS; NCRYPT_KEY_HANDLE hKey = NULL; DWORD cbSignature = 0; NTSTATUS status = ((NTSTATUS)0xC0000001L); PBYTE pbSignature = NULL; PCCERT_CONTEXT certContext = NULL; HCERTSTORE cert_store; BOOL must_release_provider; int hashHexLength = _bstr_t(hash).length()/2; BCRYPT_PKCS1_PADDING_INFO padInfo; padInfo.pszAlgId = 0; switch(hashHexLength) { case BINARY_SHA1_LENGTH: padInfo.pszAlgId = NCRYPT_SHA1_ALGORITHM;break; case BINARY_SHA224_LENGTH: padInfo.pszAlgId = L"SHA224"; break; case BINARY_SHA256_LENGTH : padInfo.pszAlgId = NCRYPT_SHA256_ALGORITHM; break; case BINARY_SHA512_LENGTH: padInfo.pszAlgId = NCRYPT_SHA512_ALGORITHM; break; default: break; } try { if(!id || !strlen(CW2A(id))) { throw CryptoException(ESTEID_CERT_NOT_FOUND_ERROR); } EstEID_log("signing started, selected certificate id = %s", CW2A(id)); if(padInfo.pszAlgId == 0) { throw CryptoException(ESTEID_INVALID_HASH_ERROR); } cert_store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_READONLY_FLAG, L"MY"); if(!cert_store) throw CryptoException(); while(certContext = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, certContext)) { if(certificateMatchesId(certContext, id)) { if (!CryptAcquireCertificatePrivateKey(certContext, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG|CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &hKey, NULL, &must_release_provider)) { throw CryptoException(ESTEID_CRYPTO_API_ERROR); } BYTE hashBytes[65]; CryptStringToBinary(hash, hashHexLength*2, CRYPT_STRING_HEX, hashBytes, (DWORD*)&hashHexLength, 0, 0); EstEID_log("Number of bytes stored in hashBytes buffer = %u", hashHexLength); EstEID_log("signing with %s", CW2A(padInfo.pszAlgId)); if(FAILED(secStatus = NCryptSignHash(hKey, &padInfo, (PBYTE)hashBytes, hashHexLength, NULL, 0, &cbSignature, 0))) { throw CryptoException(secStatus); } pbSignature = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbSignature); if(NULL == pbSignature){ throw CryptoException(ESTEID_CRYPTO_API_ERROR); } if(FAILED(secStatus = NCryptSignHash(hKey, &padInfo, (PBYTE)hashBytes, hashHexLength, pbSignature, cbSignature, &cbSignature, BCRYPT_PAD_PKCS1))) { throw CryptoException(secStatus); } std::stringstream ss; for (DWORD i = 0; i < cbSignature; i++) { ss << std::hex << std::setfill('0') << std::setw(2) << (short)pbSignature[i]; } *signature = _bstr_t(ss.str().c_str()).Detach(); } } } catch(CryptoException e) { *signature = _bstr_t("").Detach(); if(pbSignature) HeapFree(GetProcessHeap(), 0, pbSignature); if(hKey) NCryptFreeObject(hKey); if(certContext) CertFreeCertificateContext(certContext); if(must_release_provider && cert_store) CertCloseStore(cert_store, 0); throw CryptoException(e.windowsErrorCode); } CertFreeCertificateContext(certContext); if(must_release_provider) CertCloseStore(cert_store, 0); #endif }
QList<TokenData> QCNG::tokens() const { qWarning() << "Start enumerationg providers"; QHash<SslCertificate,QCNGCache> cache; DWORD count = 0; NCryptProviderName *names = nullptr; NCryptEnumStorageProviders( &count, &names, NCRYPT_SILENT_FLAG ); for( DWORD i = 0; i < count; ++i ) { qWarning() << "Found provider" << QString::fromWCharArray(names[i].pszName); if( wcscmp( names[i].pszName, MS_SMART_CARD_KEY_STORAGE_PROVIDER ) == 0 ) { for( const QString &reader: QPCSC::instance().readers() ) { qWarning() << reader; QString scope = QString(R"(\\.\%1\)").arg(reader); d->enumKeys( cache, names[i].pszName, LPCWSTR(scope.utf16()) ); } } else d->enumKeys( cache, names[i].pszName ); } NCryptFreeBuffer( names ); d->cache = cache; qWarning() << "End enumerationg providers"; QList<TokenData> result; for(QHash<SslCertificate,QCNGCache>::const_iterator i = cache.constBegin(); i != cache.constEnd(); ++i) { TokenData t; t.setCard(i.key().type() & SslCertificate::EstEidType || i.key().type() & SslCertificate::DigiIDType ? i.value().guid : i.key().subjectInfo(QSslCertificate::CommonName)); t.setCert(i.key()); result << t; } return result; } TokenData QCNG::selectCert( const SslCertificate &cert ) { qWarning() << "Select:" << cert.subjectInfo( "CN" ); if( !d->cache.contains( cert ) ) return TokenData(); d->selected = d->cache[cert]; qWarning() << "Found:" << d->selected.guid << d->selected.key; TokenData t; t.setCard( cert.type() & SslCertificate::EstEidType || cert.type() & SslCertificate::DigiIDType ? d->selected.guid : cert.subjectInfo( QSslCertificate::CommonName ) ); t.setCert( cert ); return t; } QByteArray QCNG::sign( int method, const QByteArray &digest ) const { d->err = PinUnknown; BCRYPT_PKCS1_PADDING_INFO padInfo = { NCRYPT_SHA256_ALGORITHM }; switch( method ) { case NID_sha224: padInfo.pszAlgId = L"SHA224"; break; case NID_sha256: padInfo.pszAlgId = NCRYPT_SHA256_ALGORITHM; break; case NID_sha384: padInfo.pszAlgId = NCRYPT_SHA384_ALGORITHM; break; case NID_sha512: padInfo.pszAlgId = NCRYPT_SHA512_ALGORITHM; break; case NID_md5_sha1: //padInfo.pszAlgId = L"SHAMD5"; break; default: break; } DWORD size = 0; QByteArray res; NCRYPT_KEY_HANDLE k = d->key(); QString algo(5, 0); SECURITY_STATUS err = NCryptGetProperty(k, NCRYPT_ALGORITHM_GROUP_PROPERTY, PBYTE(algo.data()), DWORD((algo.size() + 1) * 2), &size, 0); algo.resize(size/2 - 1); bool isRSA = algo == "RSA"; err = NCryptSignHash(k, isRSA ? &padInfo : nullptr, PBYTE(digest.constData()), DWORD(digest.size()), nullptr, 0, &size, isRSA ? BCRYPT_PAD_PKCS1 : 0); if(FAILED(err)) return res; res.resize(int(size)); err = NCryptSignHash(k, isRSA ? &padInfo : nullptr, PBYTE(digest.constData()), DWORD(digest.size()), PBYTE(res.data()), DWORD(res.size()), &size, isRSA ? BCRYPT_PAD_PKCS1 : 0); NCryptFreeObject( k ); switch( err ) { case ERROR_SUCCESS: d->err = PinOK; return res; case SCARD_W_CANCELLED_BY_USER: d->err = PinCanceled; break; default: res.clear(); break; } return res; }