SCHANNEL TLS Server side cannot CertFindCertificateInStore - c++

I am adding TLS encryption to a server side application. I am using the Schannel API to add the TLS. I am having a problem with CertFindCertificateInStore. It does not ever find the certificate that I am searching for. As criteria for the search I am using the name of the certificate. I have spent many hours on this now and do not understand why it is not working. Any help would be immensely appreciated. The function I am using this in is found below. Thanks,
int ServerCreateCredentials() {
//- get the certificate store
HCERTSTORE myCertStore = NULL;
myCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
X509_ASN_ENCODING,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
L"My");
// check for the failure to find the appropriate store
if (myCertStore == NULL) {
return 1;
}
// find the certificate in the store
m_CertificateContext = CertFindCertificateInStore(
myCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_A,
(LPVOID) CertificateName,
NULL);
if (m_CertificateContext == NULL) {
// try unicode
m_CertificateContext = CertFindCertificateInStore(
myCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_W,
CertificateName,
NULL);
if (m_CertificateContext == NULL ) {
// free the store
CertCloseStore(myCertStore, CERT_CLOSE_STORE_CHECK_FLAG);
return 2;
}
}
TimeStamp life;
// get the credentials
SCHANNEL_CRED SchannelCredentials;
ZeroMemory(&SchannelCredentials, sizeof(SchannelCredentials));
SchannelCredentials.dwVersion = SCHANNEL_CRED_VERSION;
SchannelCredentials.cCreds = 1; // number of credentials
SchannelCredentials.paCred = &m_CertificateContext; // credentials
SchannelCredentials.hRootStore = myCertStore; // certificate store location
SchannelCredentials.dwMinimumCipherStrength = 80; // minimum encryption allowed
SchannelCredentials.grbitEnabledProtocols = 0; // let the dll decide
SchannelCredentials.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION
| SCH_CRED_NO_SERVERNAME_CHECK
| SCH_CRED_REVOCATION_CHECK_CHAIN;
DWORD Status = SEC_E_OK;
Status = m_securityFunctionTable.AcquireCredentialsHandle(
NULL,
UNISP_NAME,
SECPKG_CRED_INBOUND,
NULL,
&SchannelCredentials,
NULL,
NULL,
&m_credentials,
&life);
// at this point we should be good
// free the store
CertCloseStore(myCertStore, CERT_CLOSE_STORE_CHECK_FLAG);
if (Status != SEC_E_OK) {
return 3;
}
return 0;

I have figured out that I was not searching on the correct parameters. You need to search based on the subject name and then it will work.

Related

Junk characters in htttps get response

I am facing a weird situation with a Windows application (OCX control) written in C++, which was working fine until 2 days ago. No updates were added to the application. Then suddenly, it started to have errors in the communication with a remote test server. Basically, the error is that in the response of a GET request, I am seeing junk characters like this: ÑòWo †¶âBA etc
The function that I am using for the HTTP requests is:
TCHAR* openUrlFunction(TCHAR *strServer, TCHAR *strPath, TCHAR *userAgent){
HINTERNET httpSession = NULL;
HINTERNET httpConnect = NULL;
HINTERNET httpRequest = NULL;
HINTERNET hFile = NULL;
BOOL resDownload = FALSE;
TCHAR bufferIdent[60] = { 0 };
TCHAR bufferMirror[500] = { 0 };
TCHAR szURL[200] = { 0 };
TCHAR sError[7] = { 0 };
DWORD dwFlags;
// DWORD dwTimeOut = 25000;
DWORD dwBuffLen = sizeof(dwFlags);
int i = 0;
DWORD dwRead, error;
int len;
TCHAR *res1;
TCHAR delimeter[6] = _T("\f\n\r\t\v");
HRESULT resCat;
bufferIdent[0] = '\0';
bufferMirror[0] = '\0';
szURL[0] = '\0';
std::string ss;
httpSession = InternetOpen(strAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); // header
if (httpSession)
{
httpConnect = InternetConnect(httpSession, strServer, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if (httpConnect)
{
httpRequest = HttpOpenRequest(httpConnect, "GET", strPath, NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0); parametros.
if (HttpSendRequest(httpRequest, NULL, 0, NULL, 0)) {
if (httpRequest)
{
while (InternetReadFile(httpRequest, bufferMirror, 499, &dwRead)) // Se lee el retorno
{
if (dwRead == 0)
break;
StrTrim(bufferMirror, delimeter);
while (bufferMirror[i] != NULL){
if (bufferMirror[i] != char(10))
ss = ss + bufferMirror[i];
i++;
if (bufferMirror[i] == char(10))
break;
}
i = 0;
}
}
}
else{
error = GetLastError();
_stprintf_s(sError, 7, _T("%d"), error);
InternetCloseHandle(httpRequest);
InternetCloseHandle(httpConnect);
InternetCloseHandle(httpSession);
return sError;
}
InternetCloseHandle(httpRequest);
}
InternetCloseHandle(httpConnect);
}
InternetCloseHandle(httpSession);
len = ss.length();
if (len == 0){
return NULL;
}
res1 = new TCHAR[len + 1];
resCat = StringCchCopyN(res1, len + 1, ss.c_str(), len);
if (!SUCCEEDED(resCat))
return NULL;
return res1;
}
The interesting thing here was that I decided to monitor the traffic so I have installed Fiddler on the same machine (Windows 10) where the problem is happening. After started Fiddler, I made a test with the Application and voila, the response was clear and well-formed and no junk characters were present.
I suspect that the Fiddler Everywhere Certificate that is installed has something to do with the result. So, if I turn off Fiddler, and make a new test, I again receive the junk characters.
I have checked the TLS configuration in the computer and nothing is wrong there. I am not sure if a Windows update could have caused this situation.
We have 3 machines, and the problem is occurring on 2 of the 3. All of them are Windows 10, but maybe different updates.
One other important thing here is that the server is for testing, and the client did not install any SSL certificate.
Well after looking into this problem, I just found that the root of the issue was more related to the fact the one of the variables was not correctly initialized and before the http request, this variable could contain junk characters so the next steps of the process were affected for the garbage that the variable contained. Another thing was that the connection problem with WININET was solved after installing a right SSL certificate. Even after setting some flags to bypass the SSL validation, did not work. The connection only was possible when the server was configured with the SSL certificate.

How to get the public key from a certificate in windows system certstore

I have added a certificate to the system store with something like this:
PCCERT_CONTEXT pCertContext;
HCERTSTORE hCertStore;
CRYPT_KEY_PROV_INFO provInfo;
if (pCertContext = CertCreateCertificateContext(MY_ENCODING_TYPE, certDER, certSize)) {
provInfo.pwszContainerName = idCert;
provInfo.pwszProvName = provName;
provInfo.dwProvType = provType;
provInfo.dwFlags = 0;
provInfo.cProvParam = 0;
provInfo.rgProvParam = NULL;
provInfo.dwKeySpec = AT_SIGNATURE;
if (!CertSetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &provInfo)) Error(TEXT("CertSetCertificateContextProperty"));
if (!(hCertStore = CertOpenSystemStore(NULL, L"MY"))) Error(TEXT("CertOpenSystemStore"));
if (!CertAddCertificateContextToStore(hCertStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) Error(TEXT("CertAddCertificateContextToStore"));
CertFreeCertificateContext(pCertContext);
} else Error(TEXT("CertCreateCertificateContext"));
Now I'm making a Cryptographyc Service Provider and need to get the public key from this certificate to implement the CPExportKey() function.
Is this possible? If it is, how can I do it?
Also, if someone could point me at a kind of guide, or howto, of CSP drivers implementation it would be great! I'm having a bad time searching documentation for these things.
So I found the solution below. With all non-essential for understanding code omitted.
Iterate through the certificates in the store and through its properties to find my certificate, then use the CryptDecodeObjectEx() function to convert the key to the RSA_CSP_PUBLICKEYBLOB format.
The key is kept at this location pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData and its size at pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData.
HCERTSTORE hCertStore = NULL;
PCCERT_CONTEXT pCertContext = NULL;
PBYTE pbPKEY = NULL;
DWORD iPKEYSize;
hCertStore = CertOpenSystemStore(NULL, L"MY");
while(pCertContext = CertEnumCertificatesInStore(
hCertStore,
pCertContext))
{
DWORD dwPropId = 0;
while(dwPropId = CertEnumCertificateContextProperties(
pCertContext, // The context whose properties are to be listed.
dwPropId)) // Number of the last property found.
{
// ...
// here I compare the properties to see if it is the certificate that I want.
// ...
CryptDecodeObjectEx((PKCS_7_ASN_ENCODING | X509_ASN_ENCODING),
RSA_CSP_PUBLICKEYBLOB,
pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData,
CRYPT_ENCODE_ALLOC_FLAG,
NULL,
&pbPKEY,
&iPKEYSize);
// pbData and pcbDataLen are output parameters of the function
*pcbDataLen = iPKEYSize;
memcpy(pbData, pbPKEY, *pcbDataLen);
LocalFree((HANDLE)pbPKEY);
}
}
}

How to Sign an EXE with Additional Certificates using CryptoAPI and SignerSign

I'm trying to build a tool that will mass sign a bunch of files based on Kernel-Mode Code Signing requirements. I know that signtool can take an additional certificate for cross-signatures trust via the /ac argument, but have not been able to figure out how to do the same using SignerSign or SignerSignEx. I've even spied on signtool's API calls, and mirroring them does not seems to produce the same affect.
Be aware, signtool or other command-line utilities cannot be used for this purpose due to project constraints.
Is there any documentation or examples on how to accomplish this?
Okay, after working on this for awhile, I've finally figured our how to do signing with a cross certificate. First, you will need four, or five depending on your version of signtool, certificates that are embedded in the signtool EXE's resources under the CERTIFICATE resource type, they all start with MS. Now I made my version pull all certificates from files so the following example pseudo code will explain how to do this. It's not purely the CryptoAPI calls, but does explain the basic process used by signtool. All certificate usage validation has also been left out for simplicity.
// inputs: string pfx_file_path, string cross_cert_file_path, string password, string file_to_sign
Certificate_Store signer_collection = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL);
Certificate_Store signer_store = CertOpenStore(CERT_STORE_PROV_MEMORY, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL,
CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | CERT_STORE_READONLY_FLAG, NULL);
File pfx_file = File::open(pifx_file_path, GENERIC_READ, OPEN_EXISTING);
File_Mapping<BYTE> pfx_mapping = File_Mapping<BYTE>::map(pfx_file);
CRYPT_DATA_BLOB pfx_blob = { pfx_mapping.size(), pfx_mapping.data() };
Certificate_Store signer_pfx = PFXImportCertStore(&pfx_blob, password, CRYPT_USER_KEYSET);
// CertEnumCertificatesInStore
for (Certificate certificate: signer_pfx) {
signer_store.add(certificate); // CertAddCertificateContextToStore
}
signer_collection.add(signer_store); // CertAddStoreToCollection
Certificate signer;
for (Certificate certificate: signer_collection) {
// Assumes first certificate is the signer, need better validation.
signer = CertDuplicateCertificateContext(certificate);
break;
}
if (signer != NULL) {
throw NoSginerException();
}
Certificate_Store additional_collection;
if (cross_cert_file_path != NULL) {
Certificate_Store cross_collection;
Certificate_Store cross_store;
Certificate cross_certificate = Certificate::load(cross_cert_file_path);
// CryptQueryObject(CERT_QUERY_OBJECT_FILE, cross_cert_file_path,
// CERT_QUERY_CONTENT_FLAG_CERT, CERT_QUERY_FORMAT_FLAG_ALL,
// 0, NULL, NULL, NULL, NULL, NULL, &cross_certificate);
cross_collection = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL);
cross_collection.add(signer_collection);
cross_store = CertOpenStore(CERT_STORE_PROV_MEMORY, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL,
CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | CERT_STORE_READONLY_FLAG, NULL);
cross_store.add(cross_certificate);
cross_collection.add(cross_store);
Certificate_Store ms_root_store = CertOpenStore(sz_CERT_STORE_PROV_MEMORY, 0, NULL,
CERT_STORE_CREATE_NEW_FLAG | CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL);
// This is where the embedded certificates from the MS Code Validation roots are collectioned.
for (Resource resource: Program_Resources::resources_under("CERTIFICATE")) { // EnumResourceNames, Find/Load/Lock|Resource
Certificate certificate = Certificate::from_blob(resource.size(), resource.data());
// CERT_BLOB blob = { resource.size(), resources.data() };
// CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob,
// CERT_QUERY_CONTENT_FLAG_CERT | CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT,
// CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, NULL, NULL, NULL, NULL,
// &certificate);
ms_root_store.add(certificate);
}
cross_collection.add(certificate);
static const DWORD CHAIN_FLAGS = CERT_CHAIN_DISABLE_PASS1_QUALITY_FILTERING |
CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS |
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
static const CERT_CHAIN_PARA CHAIN_PARAMS = { sizeof(CERT_CHAIN_PARA) };
Certificate_Chain chain = Certificate_Chain::get(HCCE_LOCAL_MACHINE, signer, NULL,
cross_collection, &CHAIN_PARAMS, CHAIN_FLAGS, NULL);
// CertGetCertificateChain(HCCE_LOCAL_MACHINE, signer, NULL,
// cross_collection, &CHAIN_PARAMS, CHAIN_FLAGS, NULL, &chain);
Certificate_Store additional_store = CertOpenStore(CERT_STORE_PROV_MEMORY,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG |
CERT_STORE_READONLY_FLAG, NULL);
for (DWORD l = 0; l != chain->cLowerQualityChainContext; ++l) {
PCCERT_CHAIN_CONTEXT low_chain = pChain->rgpLowerQualityChainContext[l];
for (DWORD c = 0; c != low_chain->cChain; ++c) {
PCERT_SIMPLE_CHAIN simple_chain = low_chain->rgpChain[c];
for (DWORD e = 0; e != simple_chain->cElement; ++e) {
PCERT_CHAIN_ELEMENT element = simple_chain->rgpElement[e];
additional_store.add(element->pCertContext);
}
}
}
additional_collection = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, CERT_STORE_CREATE_NEW_FLAG, NULL);
additional_collection.add(additional_store);
}
SIGNER_FILE_INFO file_info = { sizeof(SIGNER_FILE_INFO) };
file_info.pwszFileName = file_to_sign;
DWORD index = 0;
SIGNER_SUBJECT_INFO subject_info = { sizeof(SIGNER_SUBJECT_INFO) };
subject_info.pdwIndex = &index;
subject_info.dwSubjectChoice = SIGNER_SUBJECT_FILE;
subject_info.pSignerFileInfo = &file_info;
SIGNER_CERT_STORE_INFO store_info = { sizeof(SIGNER_CERT_STORE_INFO) };
store_info.dwCertPolicy = SIGNER_CERT_POLICY_STORE;
store_info.pSigningCert = signer;
store_info.hCertStore = additional_collection;
SIGNER_CERT cert_info = { sizeof(SIGNER_CERT) };
cert_info.dwCertChoice = SIGNER_CERT_STORE;
cert_info.pCertStoreInfo = &store_info;
SIGNER_ATTR_AUTHCODE authcode_attr = { sizeof(SIGNER_ATTR_AUTHCODE) };
SIGNER_SIGNATURE_INFO signature_info = { sizeof(SIGNER_SIGNATURE_INFO) };
signature_info.algidHash = CALG_SHA;
signature_info.dwAttrChoice = SIGNER_AUTHCODE_ATTR;
signature_info.pAttrAuthcode = &authcode_attr;
SIGNER_PROVIDER_INFO provider_info = { sizeof(SIGNER_PROVIDER_INFO) };
provider_info.pwszProviderName = L"";
provider_info.dwPvkChoice = PVK_TYPE_KEYCONTAINER;
provider_info.pwszKeyContainer = L"";
HRESULT hr = SignerSign(&subject_info, &cert_info, &signature_info, &provider_info, NULL, NULL, NULL);

LDAP SASL Binding C++

I'm currently implementing authorization mechanism on Linux against AD domain. I use for authorization the OpenLDAP library. Now I'm trying to perform the binding operation using ldap_sasl_bind_s function, and as the response from server my application is receiving the challenge but I'm not sure how to solve it. So I'm stuck with this:
berval creds; // User creds
berval *srv = NULL; // Server challenge
creds.bv_val = (char*)password.c_str();
creds.bv_len = password.length();
ret = ldap_sasl_bind_s(
ldapConnection,
username.c_str(),
"DIGEST-MD5",
&creds,
NULL,
NULL,
&srv
);
if((srv != NULL) && (ret == LDAP_SASL_BIND_IN_PROGRESS)) // If challenge has been received
{
// Challenge solving mechanism goes there.
ret = ldap_sasl_bind_s(
ldapConnection,
username.c_str(),
"DIGEST-MD5",
srv, // Not sure if it's the right place
NULL,
NULL,
NULL
);
if(ret != LDAP_SUCCESS) // Here I get 0x31 (LDAP_INVALID_CREDENTIALS)
{
ldap_unbind_ext(ldapConnection, NULL, NULL);
return false;
}
}
Ok, thanks to IBM Knowledge Center I figured how to bind credentials. Using simple auth mechanism we can do this by calling
ret = ldap_sasl_bind_s(
ldapConnection,
"username#example.com",
NULL, // Simple bind mechanism
&creds,
NULL,
NULL,
NULL
);

ACE ordering within a DACL using AddAccessAllowedAceEx

I needed to grant access to a file on Windows in a c++ program. I browsed around and copy/pasted code from MSDN and came up with the following. It has been working as far as I can tell.
But then today I stumbled across a warning in MSDN for the use of AddAccessAllowedAceEx, which says: "The caller must ensure that ACEs are added to the DACL in the correct order.". It then refers the reader to this: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379298(v=vs.85).aspx
So, my request is for any seasoned Windows programmer to review my code below and tell me if I am going to have problems vis-a-vis ACE ordering within the DACL of the file I am modifying (which is passed in via szPath in my function). I will say that I simply added my new ACE to the end of the DACL. If this will be a problem, must I really read out all the ACE's from the DACL, inspect them and then add them back one at a time being sure to insert my new ACE in the correct position to respect the correct ordering?
char* whoOps::ACLAmigo::AddACEToDACL(char* szPath, char* szSecurityPrincipal, DWORD dwPermission)
{
ACL_SIZE_INFORMATION ACLInfo;
memset(&ACLInfo, 0, sizeof(ACL_SIZE_INFORMATION));
UCHAR BuffSid[256];
PSID pSID = (PSID)BuffSid;
int returnCode = ResolveSID(szSecurityPrincipal, pSID);
SE_OBJECT_TYPE SEObjType = SE_FILE_OBJECT;
PACL pOldDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
SECURITY_INFORMATION ACLSecInfo = DACL_SECURITY_INFORMATION;
returnCode = GetNamedSecurityInfoA(szPath, SEObjType, ACLSecInfo, NULL, NULL, &pOldDACL, NULL, &pSD);
char* szReturn = NULL;
if (returnCode != ERROR_SUCCESS) {
szReturn = "GetNamedSecurityInfoA() failed.";
} else {
BOOL getACLResult = GetAclInformation(pOldDACL, &ACLInfo, sizeof(ACLInfo), AclSizeInformation);
if (!getACLResult) {
szReturn = "GetAclInformation() failed.";
} else {
DWORD cb = 0;
DWORD cbExtra = sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(pSID);
cb = ACLInfo.AclBytesInUse + cbExtra;
PACL pNewDACL = static_cast<PACL>(HeapAlloc(GetProcessHeap(),0,cb));
BOOL initACLResult = InitializeAcl(pNewDACL, cb, ACL_REVISION);
if (!initACLResult) {
szReturn = "InitializeAcl() failed.";
} else {
for (DWORD i = 0; i < ACLInfo.AceCount; ++i) {
ACE_HEADER * pACE = 0;
GetAce(pOldDACL, i, reinterpret_cast<void**>(&pACE));
pACE->AceFlags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
pACE->AceType = ACCESS_ALLOWED_ACE_TYPE;
AddAce(pNewDACL, ACL_REVISION, MAXDWORD, pACE, pACE->AceSize);
}
BOOL addACEResult = AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, dwPermission, pSID);
if (!addACEResult) {
szReturn = "AddAccessAllowedAceEx() failed.";
} else {
DWORD setSIResult = SetNamedSecurityInfoA(szPath, SEObjType, ACLSecInfo, NULL, NULL, pNewDACL, NULL);
if (!setSIResult) {
szReturn = "SetNamedSecurityInfoA() failed.";
} else {
szReturn = "AddACEToDACL() succesful.";
}
}
}
if (pNewDACL) HeapFree(GetProcessHeap(),0, pNewDACL);
}
if (pSD) LocalFree(pSD);
}
return szReturn;
}
ACE ordering is really important! Yes, best solution would be
read the content of the ACL before modifying
programmatically inspect each element (ACE) in the Security Descriptor
place your entry appropriatelly (denied before allow)
This serie has a good amount of samples.