//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: fencrypt.cpp // // Contents: File encryption tool. Encrypts a file looking in the MY // system certificate store for the specifed subject common name // with exchange private keys. // //-------------------------------------------------------------------------- #include #include #include "wincrypt.h" #include #include #include #include //+------------------------------------------------------------------------- // Helper function to make MBCS from Unicode string //-------------------------------------------------------------------------- BOOL WINAPI MkMBStr(PBYTE pbBuff, DWORD cbBuff, LPCWSTR wsz, char ** pszMB) { DWORD cbConverted; assert(pszMB != NULL); *pszMB = NULL; if(wsz == NULL) return(TRUE); // how long is the mb string cbConverted = WideCharToMultiByte( 0, 0, wsz, -1, NULL, 0, NULL, NULL); // get a buffer long enough if(pbBuff != NULL && cbConverted < cbBuff) *pszMB = (char *) pbBuff; else *pszMB = (char *) malloc(cbConverted); if(*pszMB == NULL) { SetLastError(ERROR_OUTOFMEMORY); return(FALSE); } // now convert to MB WideCharToMultiByte(0, 0, wsz, -1, *pszMB, cbConverted, NULL, NULL); return(TRUE); } //+------------------------------------------------------------------------- // Frees string allocated by the above function //-------------------------------------------------------------------------- void WINAPI FreeMBStr(PBYTE pbBuff, char * szMB) { if((szMB != NULL) && (pbBuff != (PBYTE)szMB)) free(szMB); } //+------------------------------------------------------------------------- // Win95 only supports CryptAcquireContextA. This function converts the // unicode parameters to multibyte. //-------------------------------------------------------------------------- BOOL WINAPI CryptAcquireContextU( HCRYPTPROV *phProv, LPCWSTR lpContainer, LPCWSTR lpProvider, DWORD dwProvType, DWORD dwFlags) { BYTE rgb1[_MAX_PATH]; BYTE rgb2[_MAX_PATH]; char * szContainer = NULL; char * szProvider = NULL; LONG err; err = FALSE; if( MkMBStr(rgb1, _MAX_PATH, lpContainer, &szContainer) && MkMBStr(rgb2, _MAX_PATH, lpProvider, &szProvider) ) err = CryptAcquireContextA ( phProv, szContainer, szProvider, dwProvType, dwFlags ); FreeMBStr(rgb1, szContainer); FreeMBStr(rgb2, szProvider); return(err); } //+------------------------------------------------------------------------- // Helper function to allocated the output buffer // and call CryptDecodeObject. //-------------------------------------------------------------------------- BOOL WINAPI MDecodeObject( IN DWORD dwEncodingType, IN LPCSTR lpszStructureType, IN const PBYTE pbEncoded, IN DWORD cbEncoded, OUT PVOID * ppvoid ) { DWORD cb = 0; assert(ppvoid != NULL); *ppvoid = NULL; // get the size if(!CryptDecodeObject( dwEncodingType, lpszStructureType, pbEncoded, cbEncoded, 0, // dwFlags NULL, &cb )) return(FALSE); // allocate the buffer if( (*ppvoid = malloc(cb)) == NULL ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } // Decode the data if(!CryptDecodeObject( dwEncodingType, lpszStructureType, pbEncoded, cbEncoded, 0, // dwFlags *ppvoid, &cb )) { free(*ppvoid); *ppvoid = NULL; return(FALSE); } return(TRUE); } //+------------------------------------------------------------------------- // Helper function to allocated the output buffer // and call CertRDNValueToStr. //-------------------------------------------------------------------------- DWORD WINAPI MCertRDNValueToStr( IN DWORD dwValueType, IN PCERT_RDN_VALUE_BLOB pValue, OUT OPTIONAL LPSTR * ppsz ) { DWORD cb = 0; assert(ppsz != NULL); *ppsz = NULL; // get the size cb = CertRDNValueToStrA( dwValueType, pValue, NULL, 0); // allocate the buffer if( (*ppsz = (LPSTR) malloc(cb)) == NULL ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(0); } // now convert the CERT_RDN Value to an // ascii string based on the specified // ASN value type. // This shouldn't fail. return(CertRDNValueToStrA( dwValueType, pValue, *ppsz, cb)); } //+------------------------------------------------------------------------- // Helper function to get and allocate the exported public key info //-------------------------------------------------------------------------- BOOL WINAPI MCryptExportPublicKeyInfo( HCRYPTPROV hProv, DWORD dwKeySpec, PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo ) { DWORD cbPubKeyInfo; assert(ppPubKeyInfo != NULL); *ppPubKeyInfo = NULL; // get the size if(!CryptExportPublicKeyInfo( hProv, dwKeySpec, X509_ASN_ENCODING, NULL, // pPubKeyInfo &cbPubKeyInfo ) ) return(FALSE); // allocate the buffer if( (*ppPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) malloc(cbPubKeyInfo)) == NULL ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } if(!CryptExportPublicKeyInfo( hProv, dwKeySpec, X509_ASN_ENCODING, *ppPubKeyInfo, &cbPubKeyInfo ) ) { free(*ppPubKeyInfo); *ppPubKeyInfo = NULL; return(FALSE); } return(TRUE); } //+------------------------------------------------------------------------- // Helper function to allocated the output buffer // and call CertGetCertificateContextProperty. //-------------------------------------------------------------------------- BOOL WINAPI MCertGetCertificateContextProperty( IN PCCERT_CONTEXT pCertContext, IN DWORD dwPropId, OUT void ** ppvData ) { DWORD cb = 0; assert(ppvData != NULL); *ppvData = NULL; // get the size if( !CertGetCertificateContextProperty( pCertContext, dwPropId, NULL, &cb)) return(FALSE); // allocate the buffer if( (*ppvData = malloc(cb)) == NULL ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } // Get the property out of the cert if( !CertGetCertificateContextProperty( pCertContext, dwPropId, *ppvData, &cb)) { free(*ppvData); *ppvData = NULL; return(FALSE); } return(TRUE); } //+------------------------------------------------------------------------- // Helper function to allocated the output buffer // and call CryptEncryptMessage. //-------------------------------------------------------------------------- BOOL WINAPI MCryptEncryptMessage( IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[], IN const BYTE *pbToBeEncrypted, IN DWORD cbToBeEncrypted, OUT BYTE **ppbEncryptedBlob, OUT DWORD *pcbEncryptedBlob ) { assert(ppbEncryptedBlob != NULL); *ppbEncryptedBlob = NULL; assert(pcbEncryptedBlob != NULL); *pcbEncryptedBlob = 0; // get the size if(!CryptEncryptMessage( pEncryptPara, cRecipientCert, rgpRecipientCert, pbToBeEncrypted, cbToBeEncrypted, NULL, pcbEncryptedBlob )) return(FALSE); // allocate the buffer if( (*ppbEncryptedBlob = (BYTE *) malloc(*pcbEncryptedBlob)) == NULL ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(FALSE); } // encrypt the data if(!CryptEncryptMessage( pEncryptPara, cRecipientCert, rgpRecipientCert, pbToBeEncrypted, cbToBeEncrypted, *ppbEncryptedBlob, pcbEncryptedBlob)) { free(*ppbEncryptedBlob); *ppbEncryptedBlob = NULL; *pcbEncryptedBlob = 0; return(FALSE); } return(TRUE); } //+------------------------------------------------------------------------- // Display FEncrypt usage. //-------------------------------------------------------------------------- void Usage(void) { printf("Usage: FEncrypt [options] \n"); printf("Options are:\n"); printf(" -RC2 - RC2 encryption\n"); printf(" -RC4 - RC4 encryption\n"); printf(" -SP3 - SP3 compatible encryption\n"); printf(" -FIX - Fix by loading sp3crmsg.dll\n"); exit(1); } //+------------------------------------------------------------------------- // Generalized error routine //-------------------------------------------------------------------------- #define PRINTERROR(psz, err) _PrintError((psz), (err), __LINE__) void _PrintError(char *pszMsg, DWORD err, DWORD line) { printf("%s failed on line %u: %u(%x)\n", pszMsg, line, err, err); } //+------------------------------------------------------------------------- // Grovels the cert store looking for a cert with the specified // subject common name. Then checks to see that there are private // and public exchange keys. //-------------------------------------------------------------------------- PCCERT_CONTEXT GetSubjectCertFromStore( HCERTSTORE hMyStore, const char * szSubjectName, HCRYPTPROV * phProv ) { DWORD i, j; PCCERT_CONTEXT pCertContext = NULL; PCCERT_CONTEXT pCertContextLast = NULL; PCERT_NAME_INFO pNameInfo = NULL; LPSTR sz = NULL; PCRYPT_KEY_PROV_INFO pProvInfo = NULL; HCRYPTPROV hProv = NULL; PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL; assert(hMyStore != NULL); assert(phProv != NULL); *phProv = NULL; // Enum all certs looking for the requested common // subject name that has private keys (so we know we can decrypt) while( hProv == NULL && (pCertContext = CertEnumCertificatesInStore( hMyStore, pCertContextLast)) != NULL) { // decode the subject name into RDNs if(MDecodeObject(X509_ASN_ENCODING, X509_NAME, pCertContext->pCertInfo->Subject.pbData, pCertContext->pCertInfo->Subject.cbData, (void **) &pNameInfo) ) { // loop thru looking for an CERT_RDN and COMMON Name that works for(i=0; icRDN && hProv == NULL; i++) { for(j=0; jrgRDN[i].cRDNAttr && hProv == NULL; j++) { // check to see if this is the common name if( !strcmp(pNameInfo->rgRDN[i].rgRDNAttr[j].pszObjId, szOID_COMMON_NAME) ) { // convert the string to something I can read MCertRDNValueToStr( pNameInfo->rgRDN[i].rgRDNAttr[j].dwValueType, &pNameInfo->rgRDN[i].rgRDNAttr[j].Value, &sz); // see if this is a viable certificate to use if( sz == NULL || // see if it is the common name we are looking for _stricmp(sz, szSubjectName) || // see if there are associated private keys // to ensure we can decrypt the data later !MCertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, (void **) &pProvInfo) || // Make sure it is an exchange key for encryption pProvInfo->dwKeySpec != AT_KEYEXCHANGE || // see if the keys are really there !CryptAcquireContextU( &hProv, pProvInfo->pwszContainerName, pProvInfo->pwszProvName, pProvInfo->dwProvType, pProvInfo->dwFlags & ~CERT_SET_KEY_CONTEXT_PROP_ID) ) { // On an error we didn't find a valid // key provider. Unfortunately, the CSP // may not leave the prov handle NULL // so clear it out hProv = NULL; } // Make sure the public keys in the // CSP match the public key in the certificate else if( // export the public key blob !MCryptExportPublicKeyInfo( hProv, pProvInfo->dwKeySpec, &pPubKeyInfo ) || // see if the public keys compare with // what is in the certificate !CertComparePublicKeyInfo( X509_ASN_ENCODING, &pCertContext->pCertInfo->SubjectPublicKeyInfo, pPubKeyInfo ) ) // if the keys didn't compare, then we don't // want to use this ceritificate { // close the hProv, we didn't find a valid cert assert(hProv != NULL); CryptReleaseContext(hProv, 0); hProv = NULL; } // free public key info if(pPubKeyInfo != NULL) { free(pPubKeyInfo); pPubKeyInfo = NULL; } // clean up opened prov info if(pProvInfo != NULL) { free(pProvInfo); pProvInfo = NULL; } // free the space for the ascii common name if(sz != NULL) { free(sz); sz = NULL; } } } } // free the name info data if(pNameInfo != NULL) { free(pNameInfo); pNameInfo = NULL; } } // go to the next certificate pCertContextLast = pCertContext; } assert(pProvInfo == NULL); assert(sz == NULL); assert(pNameInfo == NULL); // There is a good cert in the store, return it if(hProv != NULL) { *phProv = hProv; assert(pCertContext != NULL); return(pCertContext); } return(NULL); } //+------------------------------------------------------------------------- // Main program. Open a file to encrypt, // encrypts it and then writes the encrypted // data to the output file. //-------------------------------------------------------------------------- int __cdecl main(int argc, char * argv[]) { DWORD dwExitValue = 0; HCERTSTORE hMyStore = NULL; PCCERT_CONTEXT pCertContext = NULL; HCRYPTPROV hProv = NULL; HANDLE hFileOut = INVALID_HANDLE_VALUE; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD cbFile = 0; HANDLE hMap = NULL; PBYTE pbFile = NULL; BOOL fResult; HMODULE hDll = NULL; CMSG_SP3_COMPATIBLE_AUX_INFO SP3AuxInfo; BOOL fSP3 = FALSE; BOOL fFix = FALSE; CRYPT_ALGORITHM_IDENTIFIER encryptAlgId = {szOID_RSA_RC4, 0}; CRYPT_ENCRYPT_MESSAGE_PARA encryptInfo; PBYTE pbEncryptedBlob = NULL; DWORD cbEncryptedBlob = 0; DWORD cb = 0; // Advance past fencrypt.exe and check for leading options while (--argc > 0) { if (**++argv != '-') break; if (0 == _stricmp(argv[0], "-RC2")) encryptAlgId.pszObjId = szOID_RSA_RC2CBC; else if (0 == _stricmp(argv[0], "-RC4")) encryptAlgId.pszObjId = szOID_RSA_RC4; else if (0 == _stricmp(argv[0], "-SP3")) fSP3 = TRUE; else if (0 == _stricmp(argv[0], "-FIX")) fFix = TRUE; else { printf("Bad option: %s\n", argv[0]); Usage(); } } // must have the parameters if (argc != 3) Usage(); if (fFix) { if (NULL == (hDll = LoadLibraryA("sp3crmsg.dll"))) { PRINTERROR("LoadLibraryA(sp3crmsg.dll)", GetLastError()); goto ErrCleanUp; } } // Open the MY store if( (hMyStore = CertOpenSystemStore(NULL, "My")) == NULL ) { PRINTERROR("CertOpenSystemStore", GetLastError()); goto ErrCleanUp; } // Find a certificate in the MY store that // matches the subject name and has private keys if( (pCertContext = GetSubjectCertFromStore(hMyStore, argv[0], &hProv)) == NULL) { printf("Unable to find certificate %s with valid keys.\n", argv[0]); goto ErrCleanUp; } // At this point we have a provider, Cert and a public key. // We should be able to encrypt // Read in the clear text file if( // read in the file to encrypt (hFile = CreateFileA( argv[1], // pointer to name of the file GENERIC_READ, // access (read-write) mode FILE_SHARE_READ, // share mode NULL, // pointer to security descriptor OPEN_EXISTING, // how to create FILE_ATTRIBUTE_NORMAL, // file attributes NULL // handle to file with attributes to copy )) == INVALID_HANDLE_VALUE || // create a file mapping object (hMap = CreateFileMapping( hFile, // handle to file to map NULL, // optional security attributes PAGE_READONLY, // protection for mapping object 0, // high-order 32 bits of object size 0, // low-order 32 bits of object size NULL // name of file-mapping object )) == NULL || // Map the file into the address space (pbFile = (PBYTE) MapViewOfFileEx( hMap, // file-mapping object to map into address space FILE_MAP_READ, // access mode 0, // high-order 32 bits of file offset 0, // low-order 32 bits of file offset 0, // number of bytes to map NULL // suggested starting address for mapped view )) == NULL ) { PRINTERROR("File Open", GetLastError()); goto ErrCleanUp; } // get the size of the file if( (cbFile = GetFileSize( hFile, // handle of file to get size of NULL // address of high-order word for file size )) == 0 ) { printf("File %s has a 0 length.\n", argv[1]); goto ErrCleanUp; } // at this point we have a file mapping, go ahead and encrypt the file // Do rc4 encryption memset(&encryptInfo, 0, sizeof(CRYPT_ENCRYPT_MESSAGE_PARA)); encryptInfo.cbSize = sizeof(CRYPT_ENCRYPT_MESSAGE_PARA); encryptInfo.dwMsgEncodingType = PKCS_7_ASN_ENCODING; encryptInfo.hCryptProv = hProv; encryptInfo.ContentEncryptionAlgorithm = encryptAlgId; if (fSP3) { memset(&SP3AuxInfo, 0, sizeof(CMSG_SP3_COMPATIBLE_AUX_INFO)); SP3AuxInfo.cbSize = sizeof(CMSG_SP3_COMPATIBLE_AUX_INFO); SP3AuxInfo.dwFlags = CMSG_SP3_COMPATIBLE_ENCRYPT_FLAG; encryptInfo.pvEncryptionAuxInfo = &SP3AuxInfo; } // encrypt it fResult = MCryptEncryptMessage( &encryptInfo, 1, &pCertContext, pbFile, cbFile, &pbEncryptedBlob, &cbEncryptedBlob ); if (!fResult && fSP3 && (DWORD) E_INVALIDARG == GetLastError()) { printf( "Non-NULL pvEncryptionAuxInfo not supported in SP3 crypt32.dll\n"); encryptInfo.pvEncryptionAuxInfo = NULL; fResult = MCryptEncryptMessage( &encryptInfo, 1, &pCertContext, pbFile, cbFile, &pbEncryptedBlob, &cbEncryptedBlob ); } if (!fResult) { PRINTERROR("MCryptEncryptMessage", GetLastError()); goto ErrCleanUp; } // write the encrypted file out if( // open the output file (hFileOut = CreateFileA( argv[2], // pointer to name of the file GENERIC_WRITE, // access (read-write) mode FILE_SHARE_READ, // share mode NULL, // pointer to security descriptor CREATE_ALWAYS, // how to create FILE_ATTRIBUTE_NORMAL, // file attributes NULL // handle to file with attributes to copy )) == INVALID_HANDLE_VALUE || //write to the file !WriteFile( hFileOut, // handle to file to write to pbEncryptedBlob, // pointer to data to write to file cbEncryptedBlob, // number of bytes to write &cb, // pointer to number of bytes written NULL // pointer to structure needed for overlapped I/O ) ) { PRINTERROR("File Write", GetLastError()); goto ErrCleanUp; } CleanUp: if(hDll) FreeLibrary(hDll); if(hMap != NULL) CloseHandle(hMap); if(hFile != INVALID_HANDLE_VALUE && hFile != NULL) CloseHandle(hFile); if(hFileOut != INVALID_HANDLE_VALUE && hFile != NULL) CloseHandle(hFileOut); if(pCertContext != NULL) CertFreeCertificateContext(pCertContext); if(hProv != NULL) CryptReleaseContext(hProv, 0); if(hMyStore != NULL) CertCloseStore(hMyStore, 0); if(pbEncryptedBlob != NULL) free(pbEncryptedBlob); return(dwExitValue); ErrCleanUp: dwExitValue = 1; goto CleanUp; }