//+-------------------------------------------------------------------------- // // Copyright (c) 1997-1999 Microsoft Corporation // // File: gencert.cpp // // Contents: // // History: // //--------------------------------------------------------------------------- #include "pch.cpp" #include "misc.h" #include "utils.h" #include "gencert.h" #include "globals.h" #ifndef UNICODE const DWORD dwCertRdnValueType = CERT_RDN_PRINTABLE_STRING; #else const DWORD dwCertRdnValueType = CERT_RDN_UNICODE_STRING; #endif #ifndef CertStrToName // // Function prototype not found in wincrypt.h or anywhere but // is in crypt32.lib // #ifdef __cplusplus extern "C" { #endif BOOL WINAPI CertStrToNameA( DWORD dwCertEncodingType, // in LPCSTR pszX500, // in DWORD dwStrType, // in void* pvReserved, // in, optional BYTE* pbEncoded, // out DWORD* pcbEncoded, // in/out LPCSTR* ppszError // out, optional ); CertStrToNameW( DWORD dwCertEncodingType, // in LPCWSTR pszX500, // in DWORD dwStrType, // in void* pvReserved, // in, optional BYTE* pbEncoded, // out DWORD* pcbEncoded, // in/out LPCWSTR* ppszError // out, optional ); #ifdef UNICODE #define CertStrToName CertStrToNameW #else #define CertStrToName CertStrToNameA #endif #ifdef __cplusplus } #endif #endif /******************************************************************************************* Function: LSEncryptBase64EncodeHWID() Description: Encrypt using license server private key then base64 encode the hardware ID Arguments: IN PHWID - pointer to HWID to be encrypt/encoded OUT DWORD* cbBase64EncodeHwid - size of pointer to encrypted/encoded string OUT PBYTE* szBase64EncodeHwid - Pointer to encrypted/encoded string. Returns: TRUE if successful, FALSE otherwise, call GetLastError() for detail. *******************************************************************************************/ BOOL TLSEncryptBase64EncodeHWID( PHWID pHwid, DWORD* cbBase64EncodeHwid, PBYTE* szBase64EncodeHwid ) { DWORD status=ERROR_SUCCESS; // // Encrypt HWID // BYTE tmp_pbEncryptedHwid[sizeof(HWID)*2+2]; DWORD tmp_cbEncryptedHwid=sizeof(tmp_pbEncryptedHwid); do { memset(tmp_pbEncryptedHwid, 0, sizeof(tmp_pbEncryptedHwid)); if((status=LicenseEncryptHwid( pHwid, &tmp_cbEncryptedHwid, tmp_pbEncryptedHwid, g_cbSecretKey, g_pbSecretKey) != LICENSE_STATUS_OK)) { break; } // // BASE64 Encode Encrypted HWID - printable char. string // if((status=LSBase64Encode( tmp_pbEncryptedHwid, tmp_cbEncryptedHwid, NULL, cbBase64EncodeHwid)) != ERROR_SUCCESS) { break; } *szBase64EncodeHwid=(PBYTE)AllocateMemory(*cbBase64EncodeHwid*(sizeof(TCHAR)+1)); if(*szBase64EncodeHwid == NULL) { SetLastError(status = ERROR_OUTOFMEMORY); break; } // base64 encoding status=LSBase64Encode( tmp_pbEncryptedHwid, tmp_cbEncryptedHwid, (TCHAR *)*szBase64EncodeHwid, cbBase64EncodeHwid); } while(FALSE); return status == ERROR_SUCCESS; } /*******************************************************************************************/ DWORD TLSAddCertAuthorityInfoAccess( LPTSTR szIssuerDnsName, PCERT_EXTENSION pExtension ) /* */ { LSCERT_AUTHORITY_INFO_ACCESS certInfoAccess; LSCERT_ACCESS_DESCRIPTION certAcccessDesc; certAcccessDesc.pszAccessMethod=szOID_X509_ACCESS_PKIX_OCSP; certAcccessDesc.AccessLocation.dwAltNameChoice = LSCERT_ALT_NAME_DNS_NAME; certAcccessDesc.AccessLocation.pwszDNSName = szIssuerDnsName; certInfoAccess.cAccDescr = 1; certInfoAccess.rgAccDescr = &certAcccessDesc; pExtension->pszObjId = szOID_X509_AUTHORITY_ACCESS_INFO; pExtension->fCritical = TRUE; return TLSCryptEncodeObject( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_X509_AUTHORITY_ACCESS_INFO, &certInfoAccess, &pExtension->Value.pbData, &pExtension->Value.cbData ); } /////////////////////////////////////////////////////////////////////////////// DWORD TLSAddCertAuthorityKeyIdExtension( LPTSTR szIssuer, ULARGE_INTEGER* CertSerialNumber, PCERT_EXTENSION pExtension ) /* */ { // // Use CERT_AUTHORITY_KEY_ID2_INFO // some structure not defined in SP3's wincrypt.h // LSCERT_ALT_NAME_ENTRY certAltNameEntry; LSCERT_AUTHORITY_KEY_ID2_INFO authKeyId2Info; memset(&authKeyId2Info, 0, sizeof(authKeyId2Info)); authKeyId2Info.AuthorityCertSerialNumber.cbData = sizeof(ULARGE_INTEGER); authKeyId2Info.AuthorityCertSerialNumber.pbData = (PBYTE)CertSerialNumber; memset(&certAltNameEntry, 0, sizeof(certAltNameEntry)); certAltNameEntry.dwAltNameChoice=CERT_ALT_NAME_DIRECTORY_NAME; //LSCERT_ALT_NAME_RFC822_NAME; certAltNameEntry.DirectoryName.cbData = (_tcslen(szIssuer) + 1) * sizeof(TCHAR); certAltNameEntry.DirectoryName.pbData = (PBYTE)szIssuer; authKeyId2Info.AuthorityCertIssuer.cAltEntry=1; authKeyId2Info.AuthorityCertIssuer.rgAltEntry=&certAltNameEntry; pExtension->pszObjId = szOID_X509_AUTHORITY_KEY_ID2; pExtension->fCritical = TRUE; return TLSCryptEncodeObject( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_X509_AUTHORITY_KEY_ID2, &authKeyId2Info, &pExtension->Value.pbData, &pExtension->Value.cbData ); } /////////////////////////////////////////////////////////////////////////////// DWORD TLSExportPublicKey( IN HCRYPTPROV hCryptProv, IN DWORD dwKeyType, IN OUT PDWORD pcbByte, IN OUT PCERT_PUBLIC_KEY_INFO *ppbByte ) /* */ { BOOL bRetCode=TRUE; *pcbByte=0; *ppbByte=NULL; bRetCode = CryptExportPublicKeyInfo( hCryptProv, dwKeyType, X509_ASN_ENCODING, NULL, pcbByte); if(bRetCode == FALSE) goto cleanup; if((*ppbByte=(PCERT_PUBLIC_KEY_INFO)AllocateMemory(*pcbByte)) == NULL) { bRetCode = FALSE; goto cleanup; } bRetCode = CryptExportPublicKeyInfo( hCryptProv, dwKeyType, X509_ASN_ENCODING, *ppbByte, pcbByte); if(bRetCode == FALSE) { FreeMemory(*ppbByte); *pcbByte = 0; } cleanup: return (bRetCode) ? ERROR_SUCCESS : GetLastError(); } /////////////////////////////////////////////////////////////////////////////// DWORD TLSCryptEncodeObject( IN DWORD dwEncodingType, IN LPCSTR lpszStructType, IN const void * pvStructInfo, OUT PBYTE* ppbEncoded, OUT DWORD* pcbEncoded ) /* Description: Allocate memory and encode object, wrapper for CryptEncodeObject() */ { DWORD dwStatus = ERROR_SUCCESS; if(!CryptEncodeObject(dwEncodingType, lpszStructType, pvStructInfo, NULL, pcbEncoded) || (*ppbEncoded=(PBYTE)AllocateMemory(*pcbEncoded)) == NULL || !CryptEncodeObject(dwEncodingType, lpszStructType, pvStructInfo, *ppbEncoded, pcbEncoded)) { dwStatus=GetLastError(); } return dwStatus; } ////////////////////////////////////////////////////////////////////// DWORD TLSCryptSignAndEncodeCertificate( IN HCRYPTPROV hCryptProv, IN DWORD dwKeySpec, IN PCERT_INFO pCertInfo, IN PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, IN OUT PBYTE* ppbEncodedCert, IN OUT PDWORD pcbEncodedCert ) /* */ { BOOL bRetCode; bRetCode = CryptSignAndEncodeCertificate( hCryptProv, dwKeySpec, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, pCertInfo, pSignatureAlgorithm, NULL, NULL, pcbEncodedCert); if(bRetCode == FALSE && GetLastError() != ERROR_MORE_DATA) goto cleanup; *ppbEncodedCert=(PBYTE)AllocateMemory(*pcbEncodedCert); if(*ppbEncodedCert == FALSE) goto cleanup; bRetCode = CryptSignAndEncodeCertificate( hCryptProv, AT_SIGNATURE, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, pCertInfo, pSignatureAlgorithm, NULL, *ppbEncodedCert, pcbEncodedCert); if(bRetCode == FALSE) { FreeMemory(*ppbEncodedCert); *pcbEncodedCert = 0; } cleanup: return (bRetCode) ? ERROR_SUCCESS : GetLastError(); } //////////////////////////////////////////////////////////////////////// #define MAX_NUM_CERT_BLOBS 200 // actually, we can't go over 10. DWORD TLSVerifyProprietyChainedCertificate( HCRYPTPROV hCryptProv, PBYTE pbCert, DWORD cbCert ) /*++ --*/ { DWORD dwStatus=ERROR_SUCCESS; PCert_Chain pCertChain = (PCert_Chain)pbCert; UNALIGNED Cert_Blob *pCertificate = NULL; PCCERT_CONTEXT pIssuerCert = NULL; PCCERT_CONTEXT pSubjectCert = NULL; DWORD dwVerifyFlag = CERT_DATE_DONT_VALIDATE; int i; if( pCertChain == NULL || cbCert <= 0 || MAX_CERT_CHAIN_VERSION < GET_CERTIFICATE_VERSION(pCertChain->dwVersion) || pCertChain->dwNumCertBlobs > MAX_NUM_CERT_BLOBS || pCertChain->dwNumCertBlobs <= 1 ) // must have at least two certificates { SetLastError(dwStatus = TLS_E_INVALID_DATA); return dwStatus; } // // Verify input data before actually allocate memory // pCertificate = (PCert_Blob)&(pCertChain->CertBlob[0]); for(i=0; i < pCertChain->dwNumCertBlobs; i++) { if (((PBYTE)pCertificate > (cbCert + pbCert - sizeof(Cert_Blob))) || (pCertificate->cbCert == 0) || (pCertificate->cbCert > (DWORD)((pbCert + cbCert) - pCertificate->abCert))) { return (LICENSE_STATUS_INVALID_INPUT); } pCertificate = (PCert_Blob)(pCertificate->abCert + pCertificate->cbCert); } // // First certificate is root certificate // pCertificate = (PCert_Blob)&(pCertChain->CertBlob[0]); pIssuerCert = CertCreateCertificateContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertificate->abCert[0]), pCertificate->cbCert ); if(pIssuerCert == NULL) { dwStatus = GetLastError(); // just for debugging. goto cleanup; } dwStatus = ERROR_SUCCESS; pSubjectCert = CertDuplicateCertificateContext(pIssuerCert); for(i=0; i < pCertChain->dwNumCertBlobs; i++) { if(pSubjectCert == NULL) { dwStatus = GetLastError(); break; } // // verify subject's certificate dwVerifyFlag = CERT_STORE_SIGNATURE_FLAG; if(CertVerifySubjectCertificateContext( pSubjectCert, pIssuerCert, &dwVerifyFlag ) == FALSE) { dwStatus = GetLastError(); break; } if(dwVerifyFlag != 0) { // signature verification failed. dwStatus = TLS_E_INVALID_DATA; break; } if(CertFreeCertificateContext(pIssuerCert) == FALSE) { dwStatus = GetLastError(); break; } pIssuerCert = pSubjectCert; pCertificate = (PCert_Blob)(pCertificate->abCert + pCertificate->cbCert); pSubjectCert = CertCreateCertificateContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(pCertificate->abCert[0]), pCertificate->cbCert ); } cleanup: if(pSubjectCert != NULL) { CertFreeCertificateContext(pSubjectCert); } if(pIssuerCert != NULL) { CertFreeCertificateContext(pIssuerCert); } return dwStatus; } //////////////////////////////////////////////////////////////////////// BOOL IsHydraClientCertficate( PCERT_INFO pCertInfo ) { CERT_EXTENSION UNALIGNED * pCertExtension=pCertInfo->rgExtension; DWORD dwVersion = TERMSERV_CERT_VERSION_UNKNOWN; DWORD UNALIGNED * pdwVersion; for(DWORD i=0; i < pCertInfo->cExtension; i++, pCertExtension++) { if(strcmp(pCertExtension->pszObjId, szOID_PKIX_HYDRA_CERT_VERSION) == 0) { pdwVersion = (DWORD UNALIGNED *) pCertExtension->Value.pbData; if(pCertExtension->Value.cbData == sizeof(DWORD) && *pdwVersion <= TERMSERV_CERT_VERSION_CURRENT) { dwVersion = *pdwVersion; break; } } } return (dwVersion == TERMSERV_CERT_VERSION_UNKNOWN) ? FALSE : TRUE; } //////////////////////////////////////////////////////////////////////// DWORD ChainProprietyCert( HCRYPTPROV hCryptProv, HCERTSTORE hCertStore, PCCERT_CONTEXT pCertContext, PCert_Chain pCertChain, DWORD* dwCertOffset, DWORD dwBufSize) { DWORD dwStatus = ERROR_SUCCESS; DWORD dwFlags; PCCERT_CONTEXT pCertIssuer=NULL; pCertIssuer=NULL; dwFlags = CERT_STORE_SIGNATURE_FLAG; // // Get the issuer's certificate from store // pCertIssuer = CertGetIssuerCertificateFromStore( hCertStore, pCertContext, pCertIssuer, &dwFlags ); if(pCertIssuer != NULL) { if(dwFlags & CERT_STORE_SIGNATURE_FLAG) { // invalid signature dwStatus = TLS_E_INVALID_DATA; } else { // // Recursively find the issuer of the issuer's certificate // dwStatus = ChainProprietyCert( hCryptProv, hCertStore, pCertIssuer, pCertChain, dwCertOffset, dwBufSize ); } } else { dwStatus = GetLastError(); if(dwStatus != CRYPT_E_SELF_SIGNED) { goto cleanup; } // // Verify issuer's certificate // if(CryptVerifyCertificateSignature( hCryptProv, X509_ASN_ENCODING, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, &pCertContext->pCertInfo->SubjectPublicKeyInfo)) { dwStatus=ERROR_SUCCESS; } } if(dwStatus == ERROR_SUCCESS) { // // Push certificate into propriety certificate chain // if((*dwCertOffset + pCertContext->cbCertEncoded) >= dwBufSize) { dwStatus = ERROR_MORE_DATA; goto cleanup; } (pCertChain->dwNumCertBlobs)++; UNALIGNED Cert_Blob *pCertBlob = (PCert_Blob)((PBYTE)&(pCertChain->CertBlob) + *dwCertOffset); pCertBlob->cbCert = pCertContext->cbCertEncoded; memcpy( &(pCertBlob->abCert), pCertContext->pbCertEncoded, pCertContext->cbCertEncoded); *dwCertOffset += (sizeof(pCertBlob->cbCert) + pCertContext->cbCertEncoded); } cleanup: if(pCertIssuer != NULL) { CertFreeCertificateContext(pCertIssuer); } return dwStatus; } //////////////////////////////////////////////////////////////////////// DWORD TLSChainProprietyCertificate( HCRYPTPROV hCryptProv, BOOL bTemp, PBYTE pbLicense, DWORD cbLicense, PBYTE* pbChained, DWORD* cbChained ) { HCERTSTORE hCertStore=NULL; DWORD dwStatus=ERROR_SUCCESS; CRYPT_DATA_BLOB Serialized; PCCERT_CONTEXT pCertContext=NULL; PCCERT_CONTEXT pPrevCertContext=NULL; PCERT_INFO pCertInfo; BOOL bFound=FALSE; Serialized.pbData = pbLicense; Serialized.cbData = cbLicense; DWORD dwCertOffset = 0; PCert_Chain pCertChain; DWORD numCerts=0; DWORD cbSize=0; if(hCryptProv == NULL) { SetLastError(dwStatus = ERROR_INVALID_PARAMETER); goto cleanup; } hCertStore=CertOpenStore( sz_CERT_STORE_PROV_PKCS7, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, hCryptProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, &Serialized ); if(!hCertStore) { dwStatus=GetLastError(); goto cleanup; } // // Get number of certificate and estimated size first - save memory // do { pCertContext = CertEnumCertificatesInStore( hCertStore, pPrevCertContext ); if(pCertContext == NULL) { dwStatus = GetLastError(); if(dwStatus != CRYPT_E_NOT_FOUND) goto cleanup; dwStatus = ERROR_SUCCESS; break; } numCerts++; cbSize += pCertContext->cbCertEncoded; pPrevCertContext = pCertContext; } while(TRUE); *cbChained = cbSize + numCerts * sizeof(Cert_Blob) + sizeof(Cert_Chain); // // Allocate memory for our propriety certificate chain // pCertChain=(PCert_Chain)LocalAlloc(LPTR, *cbChained); if(pCertChain == NULL) { dwStatus = GetLastError(); goto cleanup; } pCertChain->dwVersion = CERT_CHAIN_VERSION_2 | ((bTemp) ? 0x80000000 : 0); // // Enumerate license in certificate to find actual client license. // pPrevCertContext = NULL; do { pCertContext=CertEnumCertificatesInStore(hCertStore, pPrevCertContext); if(pCertContext == NULL) { // end certificate in store or error if((dwStatus=GetLastError()) != CRYPT_E_NOT_FOUND) goto cleanup; dwStatus = ERROR_SUCCESS; break; } pPrevCertContext = pCertContext; if(IsHydraClientCertficate(pCertContext->pCertInfo)) { bFound = TRUE; } } while(bFound == FALSE); if(bFound == FALSE) { dwStatus = ERROR_INVALID_PARAMETER; goto cleanup; } // // Recusively chain certificate in backward. // dwStatus = ChainProprietyCert( hCryptProv, hCertStore, pCertContext, pCertChain, &dwCertOffset, *cbChained); *pbChained = (PBYTE)pCertChain; cleanup: if(hCertStore) CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG); return dwStatus; } /////////////////////////////////////////////////////////////////////////////// DWORD TLSCertSetCertRdnStr( IN OUT CERT_NAME_BLOB* pCertNameBlob, IN LPTSTR szRdn ) /* Abstract: Add RDN into certificate Parameter: pCertNameBlob - szRdn - RDN to be added, see CertStrToName() for help Returns: ERROR_INVALID_PARAMETER Memory allocation failed. Error returns from CertStrToName() */ { if(pCertNameBlob == NULL) return ERROR_INVALID_PARAMETER; BOOL bRetCode=TRUE; bRetCode = CertStrToName( X509_ASN_ENCODING, szRdn, CERT_X500_NAME_STR | CERT_SIMPLE_NAME_STR, NULL, pCertNameBlob->pbData, &pCertNameBlob->cbData, NULL ); if(bRetCode != TRUE) goto cleanup; pCertNameBlob->pbData = (PBYTE)AllocateMemory(pCertNameBlob->cbData); if(pCertNameBlob->pbData == NULL) goto cleanup; bRetCode = CertStrToName( X509_ASN_ENCODING, szRdn, CERT_X500_NAME_STR | CERT_SIMPLE_NAME_STR, NULL, pCertNameBlob->pbData, &pCertNameBlob->cbData, NULL ); cleanup: return (bRetCode) ? ERROR_SUCCESS : GetLastError(); } /////////////////////////////////////////////////////////////////////////////// DWORD TLSCertSetCertRdnName( IN OUT CERT_NAME_BLOB* pCertNameBlob, IN CERT_NAME_INFO* pRdn ) /* Abstract: Add RDN into certificate Parameters: pCertNameBlob - pRdn - Returns ERROR_INVALID_PARAMETER Error code from CryptEncodeObject() Memory allocation fail. */ { if(pCertNameBlob == NULL || pRdn == NULL) return ERROR_INVALID_PARAMETER; // // CertStrToName() not defined in SP3 build environment // return TLSCryptEncodeObject( CRYPT_ASN_ENCODING, X509_NAME, pRdn, &pCertNameBlob->pbData, &pCertNameBlob->cbData ); } ////////////////////////////////////////////////////////////////////////// DWORD TLSSetCertRdn( PCERT_NAME_BLOB pCertNameBlob, PTLSClientCertRDN pLsCertRdn ) /* */ { DWORD dwStatus=ERROR_SUCCESS; switch(pLsCertRdn->type) { case LSCERT_RDN_STRING_TYPE: dwStatus = TLSCertSetCertRdnStr( pCertNameBlob, pLsCertRdn->szRdn ); break; case LSCERT_RDN_NAME_INFO_TYPE: dwStatus = TLSCertSetCertRdnName( pCertNameBlob, pLsCertRdn->pCertNameInfo ); break; case LSCERT_RDN_NAME_BLOB_TYPE: *pCertNameBlob = *pLsCertRdn->pNameBlob; break; case LSCERT_CLIENT_INFO_TYPE: { PBYTE szBase64EncodeHwid=NULL; DWORD cbBase64EncodeHwid=0; if(!TLSEncryptBase64EncodeHWID( pLsCertRdn->ClientInfo.pClientID, &cbBase64EncodeHwid, &szBase64EncodeHwid)) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, dwStatus=TLS_E_ENCRYPTHWID, GetLastError() ); break; } CERT_RDN_ATTR rgNameAttr[] = { { OID_SUBJECT_CLIENT_COMPUTERNAME, dwCertRdnValueType, _tcslen(pLsCertRdn->ClientInfo.szMachineName) * sizeof(TCHAR), (UCHAR *)pLsCertRdn->ClientInfo.szMachineName }, { OID_SUBJECT_CLIENT_USERNAME, dwCertRdnValueType, _tcslen(pLsCertRdn->ClientInfo.szUserName) * sizeof(TCHAR), (UCHAR *)pLsCertRdn->ClientInfo.szUserName }, { OID_SUBJECT_CLIENT_HWID, dwCertRdnValueType, cbBase64EncodeHwid*sizeof(TCHAR), (UCHAR *)szBase64EncodeHwid } }; CERT_RDN rgRDN[] = { sizeof(rgNameAttr)/sizeof(rgNameAttr[0]), &rgNameAttr[0] }; CERT_NAME_INFO Name = {1, rgRDN}; dwStatus = TLSCertSetCertRdnName( pCertNameBlob, &Name ); FreeMemory(szBase64EncodeHwid); } break; default: dwStatus = ERROR_INVALID_PARAMETER; } return dwStatus; } ////////////////////////////////////////////////////////////////////////// DWORD TLSGenerateCertificate( HCRYPTPROV hCryptProv, DWORD dwKeySpec, ULARGE_INTEGER* pCertSerialNumber, PTLSClientCertRDN pCertIssuer, PTLSClientCertRDN pCertSubject, FILETIME* ftNotBefore, FILETIME* ftNotAfter, PCERT_PUBLIC_KEY_INFO pSubjectPublicKey, DWORD dwNumExtensions, PCERT_EXTENSION pCertExtensions, PDWORD pcbEncodedCert, PBYTE* ppbEncodedCert ) /* */ { DWORD dwStatus=ERROR_SUCCESS; CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm={ szOID_OIWSEC_sha1RSASign, 0, 0 }; CERT_INFO CertInfo; PCERT_PUBLIC_KEY_INFO pbPublicKeyInfo=NULL; DWORD cbPublicKeyInfo=0; memset(&CertInfo, 0, sizeof(CERT_INFO)); CertInfo.dwVersion = CERT_V3; CertInfo.SerialNumber.cbData = sizeof(*pCertSerialNumber); CertInfo.SerialNumber.pbData = (PBYTE)pCertSerialNumber; CertInfo.SignatureAlgorithm = SignatureAlgorithm; dwStatus = TLSSetCertRdn( &CertInfo.Issuer, pCertIssuer ); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_SETCERTISSUER, dwStatus ); goto cleanup; } CertInfo.NotBefore = *ftNotBefore; CertInfo.NotAfter = *ftNotAfter; dwStatus = TLSSetCertRdn( &CertInfo.Subject, pCertSubject ); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_SETCERTSUBJECT, dwStatus ); goto cleanup; } if(pSubjectPublicKey) { CertInfo.SubjectPublicKeyInfo = *pSubjectPublicKey; } else { dwStatus = TLSExportPublicKey( hCryptProv, dwKeySpec, &cbPublicKeyInfo, &pbPublicKeyInfo ); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_EXPORT_KEY, dwStatus ); goto cleanup; } CertInfo.SubjectPublicKeyInfo = *pbPublicKeyInfo; } CertInfo.cExtension = dwNumExtensions; CertInfo.rgExtension = pCertExtensions; dwStatus = TLSCryptSignAndEncodeCertificate( hCryptProv, dwKeySpec, &CertInfo, &SignatureAlgorithm, ppbEncodedCert, pcbEncodedCert ); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_SIGNENCODECERT, dwStatus ); } cleanup: if(pbPublicKeyInfo) { FreeMemory(pbPublicKeyInfo); } if(pCertIssuer->type != LSCERT_RDN_NAME_BLOB_TYPE) { FreeMemory(CertInfo.Issuer.pbData); } if(pCertSubject->type != LSCERT_RDN_NAME_BLOB_TYPE) { FreeMemory(CertInfo.Subject.pbData); } return dwStatus; } ////////////////////////////////////////////////////////////////////// DWORD TLSCreateSelfSignCertificate( IN HCRYPTPROV hCryptProv, IN DWORD dwKeySpec, IN PBYTE pbSPK, IN DWORD cbSPK, IN DWORD dwNumExtensions, IN PCERT_EXTENSION pCertExtension, OUT PDWORD cbEncoded, OUT PBYTE* pbEncoded ) /* */ { DWORD dwStatus=ERROR_SUCCESS; DWORD index; #define MAX_EXTENSIONS_IN_SELFSIGN 40 SYSTEMTIME sysTime; FILETIME ftTime; CERT_EXTENSION rgExtension[MAX_EXTENSIONS_IN_SELFSIGN]; int iExtCount=0, iExtNotFreeCount=0; FILETIME ftNotBefore; FILETIME ftNotAfter; ULARGE_INTEGER ulSerialNumber; TLSClientCertRDN certRdn; CERT_BASIC_CONSTRAINTS2_INFO basicConstraint; // modify here is we want to set to different issuer name LPTSTR szIssuerName; szIssuerName = g_szComputerName; //static LPTSTR pszEnforce=L"Enforce"; CERT_RDN_ATTR rgNameAttr[] = { { szOID_COMMON_NAME, dwCertRdnValueType, _tcslen(szIssuerName) * sizeof(TCHAR), (UCHAR *)szIssuerName }, //#if ENFORCE_LICENSING // { // szOID_BUSINESS_CATEGORY, // dwCertRdnValueType, // _tcslen(pszEnforce) * sizeof(TCHAR), // (UCHAR *)pszEnforce // }, //#endif { szOID_LOCALITY_NAME, dwCertRdnValueType, _tcslen(g_pszScope) * sizeof(TCHAR), (UCHAR *)g_pszScope } }; CERT_RDN rgRDN[] = { sizeof(rgNameAttr)/sizeof(rgNameAttr[0]), &rgNameAttr[0] }; CERT_NAME_INFO Name = {1, rgRDN}; certRdn.type = LSCERT_RDN_NAME_INFO_TYPE; certRdn.pCertNameInfo = &Name; memset(rgExtension, 0, sizeof(rgExtension)); // // Set validity of self sign certificate // // // If system time is not in sync, this will cause server // can't request cert. from license server // memset(&sysTime, 0, sizeof(sysTime)); GetSystemTime(&sysTime); sysTime.wYear = 1970; if(TLSSystemTimeToFileTime(&sysTime, &ftNotBefore) == FALSE) { dwStatus = GetLastError(); goto cleanup; } // // draft-ietf-pkix-ipki-part1-06.txt section 4.1.2.5.1 // where year is greater or equal to 50, the year shall be interpreted as 19YY; and // where year is less than 50, the year shall be interpreted as 20YY // sysTime.wYear = PERMANENT_CERT_EXPIRE_DATE; if(TLSSystemTimeToFileTime(&sysTime, &ftNotAfter) == FALSE) { dwStatus = GetLastError(); goto cleanup; } ulSerialNumber.LowPart = ftNotBefore.dwLowDateTime; ulSerialNumber.HighPart = ftNotBefore.dwHighDateTime; // // Add basic constrains extension to indicate this is a CA certificate // rgExtension[iExtCount].pszObjId = szOID_BASIC_CONSTRAINTS2; rgExtension[iExtCount].fCritical = FALSE; basicConstraint.fCA = TRUE; // act as CA basicConstraint.fPathLenConstraint = TRUE; basicConstraint.dwPathLenConstraint = 0; // can only issue certificates // to end-entities and not to further CAs dwStatus=TLSCryptEncodeObject( X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS2, &basicConstraint, &(rgExtension[iExtCount].Value.pbData), &(rgExtension[iExtCount].Value.cbData) ); if(dwStatus != ERROR_SUCCESS) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_SIGNENCODECERT, dwStatus ); goto cleanup; } iExtCount++; // // From here - extension memory should not be free // if(pbSPK != NULL && cbSPK != 0) { rgExtension[iExtCount].pszObjId = szOID_PKIS_TLSERVER_SPK_OID; rgExtension[iExtCount].fCritical = FALSE; rgExtension[iExtCount].Value.pbData = pbSPK; rgExtension[iExtCount].Value.cbData = cbSPK; iExtNotFreeCount++; iExtCount++; } for(index = 0; index < dwNumExtensions; index ++, iExtCount++, iExtNotFreeCount++ ) { rgExtension[iExtCount] = pCertExtension[index]; } dwStatus = TLSGenerateCertificate( hCryptProv, dwKeySpec, &ulSerialNumber, &certRdn, &certRdn, &ftNotBefore, &ftNotAfter, NULL, iExtCount, rgExtension, cbEncoded, pbEncoded ); cleanup: // // Don't free memory for SPK and extensions... // for(int i=0; i < iExtCount - iExtNotFreeCount; i++) { FreeMemory(rgExtension[i].Value.pbData); } return (dwStatus != ERROR_SUCCESS) ? TLS_E_CREATE_SELFSIGN_CERT : ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////// DWORD TLSGenerateSingleCertificate( IN HCRYPTPROV hCryptProv, IN PCERT_NAME_BLOB pIssuerRdn, IN PTLSClientCertRDN pSubjectRdn, IN PCERT_PUBLIC_KEY_INFO pSubjectPublicKeyInfo, IN PTLSDBLICENSEDPRODUCT pLicProduct, OUT PBYTE* ppbEncodedCert, IN PDWORD pcbEncodedCert ) /* */ { DWORD dwStatus = ERROR_SUCCESS; #define MAX_CLIENT_EXTENSION 10 CERT_EXTENSION CertExtension[MAX_CLIENT_EXTENSION]; DWORD dwNumExtensions=0; DWORD currentCertVersion=TERMSERV_CERT_VERSION_CURRENT; TLSClientCertRDN IssuerRdn; #if ENFORCE_LICENSING // // Use certificate that CH gets for us // IssuerRdn.type = LSCERT_RDN_NAME_BLOB_TYPE; //IssuerRdn.pNameBlob = &g_LicenseCertContext->pCertInfo->Subject; IssuerRdn.pNameBlob = pIssuerRdn; #else LPTSTR szIssuerName; // modify here if we want to set to different issuer name szIssuerName = g_szComputerName; CERT_RDN_ATTR rgNameAttr[] = { { OID_ISSUER_LICENSE_SERVER_NAME, dwCertRdnValueType, _tcslen(szIssuerName) * sizeof(TCHAR), (UCHAR *)szIssuerName }, { OID_ISSUER_LICENSE_SERVER_SCOPE, dwCertRdnValueType, _tcslen(g_pszScope) * sizeof(TCHAR), (UCHAR *)g_pszScope } }; CERT_RDN rgRDN[] = { sizeof(rgNameAttr)/sizeof(rgNameAttr[0]), &rgNameAttr[0] }; CERT_NAME_INFO Name = {1, rgRDN}; IssuerRdn.type = LSCERT_RDN_NAME_INFO_TYPE; IssuerRdn.pCertNameInfo = &Name; #endif //------------------------------------------------------------------------------------------ // add extension to certificate // WARNING : End of routine free memory allocated for extension's pbData, skip those // that can't be free, for example, version stamp extension. all these is just // to keep memory fragmentaion low //------------------------------------------------------------------------------------------ // // DO NOT FREE pbData on first two extensions // // Hydra Certificate version stamp - DO NOT FREE memset(&CertExtension, 0, sizeof(CertExtension)); dwNumExtensions = 0; // // Add License Server Info // CertExtension[dwNumExtensions].pszObjId = szOID_PKIX_HYDRA_CERT_VERSION; CertExtension[dwNumExtensions].fCritical = TRUE; CertExtension[dwNumExtensions].Value.cbData = sizeof(DWORD); CertExtension[dwNumExtensions].Value.pbData = (PBYTE)¤tCertVersion; dwNumExtensions++; // manufacturer's name, no encoding - DO NOT FREE CertExtension[dwNumExtensions].pszObjId = szOID_PKIX_MANUFACTURER; CertExtension[dwNumExtensions].fCritical = TRUE; CertExtension[dwNumExtensions].Value.cbData = (_tcslen(pLicProduct->szCompanyName)+1) * sizeof(TCHAR); CertExtension[dwNumExtensions].Value.pbData = (PBYTE)pLicProduct->szCompanyName; dwNumExtensions++; // // MS Licensed Product Info, no encoding // LICENSED_VERSION_INFO LicensedInfo; memset(&LicensedInfo, 0, sizeof(LicensedInfo)); LicensedInfo.wMajorVersion = HIWORD(pLicProduct->dwProductVersion); LicensedInfo.wMinorVersion = LOWORD(pLicProduct->dwProductVersion); LicensedInfo.dwFlags = (pLicProduct->bTemp) ? LICENSED_VERSION_TEMPORARY : 0; DWORD dwLSVersionMajor; DWORD dwLSVersionMinor; dwLSVersionMajor = GET_SERVER_MAJOR_VERSION(TLS_CURRENT_VERSION); dwLSVersionMinor = GET_SERVER_MINOR_VERSION(TLS_CURRENT_VERSION); LicensedInfo.dwFlags |= ((dwLSVersionMajor << 4 | dwLSVersionMinor) << 16); if(TLSIsBetaNTServer() == FALSE) { LicensedInfo.dwFlags |= LICENSED_VERSION_RTM; } #if ENFORCE_LICENSING LicensedInfo.dwFlags |= LICENSE_ISSUER_ENFORCE_TYPE; #endif CertExtension[dwNumExtensions].pszObjId = szOID_PKIX_LICENSED_PRODUCT_INFO; CertExtension[dwNumExtensions].fCritical = TRUE; dwStatus=LSLicensedProductInfoToExtension( 1, pLicProduct->dwPlatformID, pLicProduct->dwLanguageID, (PBYTE)pLicProduct->szRequestProductId, (_tcslen(pLicProduct->szRequestProductId) + 1) * sizeof(TCHAR), (PBYTE)pLicProduct->szLicensedProductId, (_tcslen(pLicProduct->szLicensedProductId) + 1) * sizeof(TCHAR), &LicensedInfo, 1, &(CertExtension[dwNumExtensions].Value.pbData), &(CertExtension[dwNumExtensions].Value.cbData) ); if(dwStatus != ERROR_SUCCESS) goto cleanup; dwNumExtensions++; // // Add license server info into extension // CertExtension[dwNumExtensions].pszObjId = szOID_PKIX_MS_LICENSE_SERVER_INFO; CertExtension[dwNumExtensions].fCritical = TRUE; dwStatus=LSMsLicenseServerInfoToExtension( g_szComputerName, (LPTSTR)g_pszServerPid, g_pszScope, &(CertExtension[dwNumExtensions].Value.pbData), &(CertExtension[dwNumExtensions].Value.cbData) ); if(dwStatus != ERROR_SUCCESS) goto cleanup; dwNumExtensions++; // // Add policy module specific extension if( pLicProduct->pbPolicyData != NULL && pLicProduct->cbPolicyData != 0 ) { CertExtension[dwNumExtensions].pszObjId = szOID_PKIS_PRODUCT_SPECIFIC_OID; CertExtension[dwNumExtensions].fCritical = TRUE; CertExtension[dwNumExtensions].Value.pbData = pLicProduct->pbPolicyData; CertExtension[dwNumExtensions].Value.cbData = pLicProduct->cbPolicyData; dwNumExtensions++; } // // Add CertAuthorityKeyId2Info for certificate chain dwStatus=TLSAddCertAuthorityKeyIdExtension( g_szComputerName, &pLicProduct->ulSerialNumber, CertExtension + dwNumExtensions ); if(dwStatus != ERROR_SUCCESS) goto cleanup; dwNumExtensions++; // Add Access info dwStatus = TLSGenerateCertificate( hCryptProv, AT_SIGNATURE, &pLicProduct->ulSerialNumber, &IssuerRdn, pSubjectRdn, &pLicProduct->NotBefore, &pLicProduct->NotAfter, pLicProduct->pSubjectPublicKeyInfo, dwNumExtensions, CertExtension, pcbEncodedCert, ppbEncodedCert ); cleanup: // Extensions. DO NOT FREE first two extensions for(int i=2; i < dwNumExtensions; i++) { FreeMemory(CertExtension[i].Value.pbData); } return (dwStatus != ERROR_SUCCESS) ? TLS_E_CREATE_CERT : ERROR_SUCCESS; } //////////////////////////////////////////////////////////// DWORD TLSGenerateClientCertificate( IN HCRYPTPROV hCryptProv, IN DWORD dwNumLicensedProduct, IN PTLSDBLICENSEDPRODUCT pLicProduct, IN WORD wLicenseChainDetail, OUT PBYTE* ppbEncodedCert, OUT PDWORD pcbEncodedCert ) /*++ ++*/ { DWORD dwStatus = ERROR_SUCCESS; HCERTSTORE hStore = NULL; PCCERT_CONTEXT pCertContext = NULL; PBYTE pbCert=NULL; DWORD cbCert=NULL; DWORD index; TLSClientCertRDN clientCertRdn; PCERT_NAME_BLOB pIssuerNameBlob = NULL; // // Create a in-memory store // hStore=CertOpenStore( CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, hCryptProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, NULL ); if(!hStore) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_OPEN_CERT_STORE, dwStatus=GetLastError() ); goto cleanup; } #ifndef ENFORCE_LICENSING pIssuerNameBlob = &g_SelfSignCertContext->pCertInfo->Subject; #else if( g_SelfSignCertContext == NULL ) { TLSASSERT(FALSE); dwStatus = TLS_E_INTERNAL; goto cleanup; } if(g_bHasHydraCert && g_hCaStore && wLicenseChainDetail == LICENSE_DETAIL_DETAIL) { pIssuerNameBlob = &g_LicenseCertContext->pCertInfo->Subject; } else { pIssuerNameBlob = &g_SelfSignCertContext->pCertInfo->Subject; } #endif // // Generate client certificate and add to certstore // for(index = 0; index < dwNumLicensedProduct; index++) { if(pCertContext != NULL) { // // Need to keep one pCertContext for later use // CertFreeCertificateContext(pCertContext); pCertContext = NULL; } clientCertRdn.type = LSCERT_CLIENT_INFO_TYPE; clientCertRdn.ClientInfo.szUserName = pLicProduct[index].szUserName; clientCertRdn.ClientInfo.szMachineName = pLicProduct[index].szMachineName; clientCertRdn.ClientInfo.pClientID = &pLicProduct[index].ClientHwid; dwStatus = TLSGenerateSingleCertificate( hCryptProv, pIssuerNameBlob, &clientCertRdn, pLicProduct[index].pSubjectPublicKeyInfo, pLicProduct+index, &pbCert, &cbCert ); if(dwStatus != ERROR_SUCCESS) { break; } // // Add certificate to store // pCertContext = CertCreateCertificateContext( X509_ASN_ENCODING, pbCert, cbCert ); if(pCertContext == NULL) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_CREATE_CERTCONTEXT, dwStatus=GetLastError() ); break; } // // always start from empty so CERT_STORE_ADD_ALWAYS // if(!CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_ALWAYS, NULL)) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_ADD_CERT_TO_STORE, dwStatus=GetLastError() ); break; } FreeMemory(pbCert); pbCert = NULL; } if(dwStatus == ERROR_SUCCESS) { #ifndef ENFORCE_LICENSING // // Add license server's certificate if(!CertAddCertificateContextToStore(hStore, g_LicenseCertContext, CERT_STORE_ADD_ALWAYS, NULL)) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_ADD_CERT_TO_STORE, dwStatus=GetLastError() ); goto cleanup; } #else // // we don't support LICENSE_DETAIL_MODERATE at this time, treat it as LICENSE_DETAIL_SIMPLE // if(g_bHasHydraCert && g_hCaStore && wLicenseChainDetail == LICENSE_DETAIL_DETAIL) { // // Chain issuer certificate with client certificate // if(!TLSChainIssuerCertificate(hCryptProv, g_hCaStore, hStore, pCertContext)) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_ADD_CERT_TO_STORE, dwStatus=GetLastError() ); goto cleanup; } } else { // // Add license server's certificate if(!CertAddCertificateContextToStore(hStore, g_SelfSignCertContext, CERT_STORE_ADD_ALWAYS, NULL)) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_ADD_CERT_TO_STORE, dwStatus=GetLastError() ); goto cleanup; } } #endif CRYPT_DATA_BLOB saveBlob; memset(&saveBlob, 0, sizeof(saveBlob)); // save certificate into memory if(!CertSaveStore(hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, LICENSE_BLOB_SAVEAS_TYPE, CERT_STORE_SAVE_TO_MEMORY, &saveBlob, 0) && (dwStatus=GetLastError()) != ERROR_MORE_DATA) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_SAVE_STORE, dwStatus=GetLastError() ); goto cleanup; } if(!(saveBlob.pbData = (PBYTE)AllocateMemory(saveBlob.cbData))) { dwStatus=TLS_E_ALLOCATE_MEMORY; goto cleanup; } // save certificate into memory if(!CertSaveStore(hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, LICENSE_BLOB_SAVEAS_TYPE, CERT_STORE_SAVE_TO_MEMORY, &saveBlob, 0)) { TLSLogEvent( EVENTLOG_ERROR_TYPE, TLS_E_GENERATECLIENTELICENSE, TLS_E_SAVE_STORE, dwStatus=GetLastError() ); goto cleanup; } *ppbEncodedCert = saveBlob.pbData; *pcbEncodedCert = saveBlob.cbData; } cleanup: FreeMemory(pbCert); if(pCertContext) { CertFreeCertificateContext(pCertContext); } if(hStore) { CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG); } return dwStatus; }