<

Easy String Encryption Using CryptoAPI in C++

Published on
21,654 Points
15,554 Views
1 Endorsement
Last Modified:
DanRollins
When your program needs to access a service, web site, or API that requires a username and password login, how can you store the password so that it is never written to disk in cleartext?  

You need to encrypt it... and I'm not talking about some sort of XOR/scrambling logic that a cypher tech could break in five minutes, but real RSA encryption.  The Windows CryptoAPI provides the tools to do this, but the documentation is rather complicated and the steps are not particularly obvious.

A typical scenario might be that you need to make a database connection that requires a password:

1) Get input of the password used in a SQL Server connection string in a settings dialog.
2) Encrypt: Make the password data unusable except to your program.
3) Store the encrypted data into the System Registry.
   ... later ...
4) Decrypt:  Recover the original cleartext data.
5) Build the database connection string using the cleartext password.

I wrote a smallish (140-line) C++ class to do steps 2 and 4, and I'll provide the entire source code in this article.  Here is the rough outline of the steps:

Setup:
    CryptAcquireContext
    CryptCreateHash
    CryptHashData
    CryptDeriveKey  (create the key needed below)
Encrypt:
    CryptEncrypt
    Convert binary to hexadecimal (for easy transport/storage)
Decrypt:
   Convert hexadecimal to binary (for using the CryptoAPI)
   CryptDecrypt

It boils down to:  Make a CryptoAPI key and use it in calls to CryptEncrypt and CryptDecrypt.   Creating that key is the only tricky (non-obvious) part.  You start with a base raw key (any string of characters), hash it, and then convert that hash into a key that can be used by the CryptoAPI.  

Base Raw Key and Salt
So what do you use as the original base raw key string of characters?  I want my code to be extremely simple and not require that the caller know the base key.  I wanted to be able to use this tool from various program modules transparently, so I provided a built-in "default" base key as a member variable.  You may see the security hole there:  Anyone who has a copy of my program can decrypt anything that has been (default-ly) encrypted by my program.

To give a warm feeling to my clients, I added a feature called a salt -- an optional, user-provided string that can be appended to the default base key string.  Now, if my users add a salt, then anybody who does not know that salt value can never decrypt the passwords (or Social Security Numbers or other encrypted data).  Note:  I put a big warning in my documentation:  "If you forget your salt value, you will lose all of the encrypted data and there is no backdoor.  Don't call tech support because we can't help you."

Problems with Binary Data
One significant problem you will encounter with encrypted data is that it is no longer simple text.   It ends up as a very random-looking array of binary data -- including unprintable characters, apostrophes and percentage signs (SQL users beware!), and worst of all, it may contain embedded binary NULL values (0x00).  As a C/C++ programmer you know that it is possible to work around such situations in various ways, but you also know that you will eventually run into headaches.  

I chose the simple expedient of always storing encrypted data as a C-style string of hexadecimal digits.   The hex-encoded secret data is always exactly twice as long as the encrypted data, which makes it somewhat easier to work with than base-64 encoding or other techniques.

Enough with the jawing... let's get to the code.

Crypt.h -- Header for the Encryption Object
#pragma once
#include "stdafx.h"
#include <Wincrypt.h>

class CCrypt
{
public:
    CCrypt(void);
    virtual ~CCrypt(void) {
        if ( m_hKey )  CryptDestroyKey( m_hKey ); 
        if ( m_hHash ) CryptDestroyHash( m_hHash ); 
        if ( m_hProv ) CryptReleaseContext( m_hProv, 0); 
    }
    BOOL SetKey( LPCSTR szKey= 0, LPCSTR pszSalt= 0 );

    BOOL EncryptDecrypt( BYTE* pData, DWORD* dwDataLen, LPCSTR pKey, BOOL fEncrypt );

    CString EncryptStrToHex(   LPCSTR szText, LPCSTR pKey= 0, LPCSTR pszSalt= 0 );
    CString DecryptStrFromHex( LPCSTR szHex,  LPCSTR pKey= 0, LPCSTR pszSalt= 0 );

    CString EncodeToHex(   BYTE* p, int nLen );
    int     DecodeFromHex( LPCSTR pSrc, BYTE* pDest, int nBufLen );

private:
    HCRYPTPROV  m_hProv;
    HCRYPTHASH  m_hHash;
    HCRYPTKEY   m_hKey;

    BOOL        m_fOK;
    DWORD       m_nLastErr;
    CString     m_sErrMsg;
    char*       m_pszDefaultKeyRaw;
};

Open in new window

Crypt.cpp -- Code of the Encryption Object
#include "utCrypt.h"
CCrypt::CCrypt(void)
{
    m_hProv= m_hHash= m_hKey= 0; 
    m_pszDefaultKeyRaw= "fdC)Y%yum3ww09";
}
BOOL CCrypt::SetKey( LPCSTR szKey, LPCSTR szSalt/*=0*/ )
{
    m_fOK= TRUE;
    if ( 0 == m_hProv ) {
        m_fOK= CryptAcquireContext( &m_hProv, NULL, 
            MS_DEF_PROV, 
            PROV_RSA_FULL, 
            CRYPT_VERIFYCONTEXT 
        );
    }
    if ( m_fOK && (0 != m_hHash) ) {
        m_fOK= CryptDestroyHash( m_hHash ); 
        m_hHash= 0;
    }
    if ( m_fOK && (0 == m_hHash) ) {
        m_fOK= CryptCreateHash( m_hProv, CALG_MD5, 0, 0, &m_hHash );
    }
    if ( m_fOK ) {
        if ( 0 == szKey ) {  // request to use default rawKey
            char szTmp[100];
            strcpy_s( szTmp, sizeof(szTmp), m_pszDefaultKeyRaw );
            if ( szSalt ) {
                strncat_s( szTmp, sizeof(szTmp), szSalt, 5 ); // use part of salt
            }
            // minor security tweak -- scramble the key+salt
            int nLen= strlen(szTmp)-1;  
            for ( int j=0; j< nLen; j++ ) {
                char c= szTmp[nLen-j];
                szTmp[nLen-j]= (char)(szTmp[j]+5);
                szTmp[j]= c;
            }
            szKey= &szTmp[4]; // discard the first part, for fun
        }
        m_fOK= CryptHashData( m_hHash, (BYTE*)szKey, strlen(szKey), 0);
    }
    if ( m_fOK ) {
        m_fOK= CryptDeriveKey( m_hProv, CALG_RC4, m_hHash, CRYPT_EXPORTABLE, &m_hKey);
    }
    if ( !m_fOK ) { 
        m_nLastErr= GetLastError(); 
        m_sErrMsg= "Error creating encryption key";
    }
    return( m_fOK );
}
//--- workhorse function:  Encrypt or decrypt "in place"
BOOL CCrypt::EncryptDecrypt( BYTE* pData, DWORD* dwDataLen, LPCSTR pKey, BOOL fEncrypt )
{
    m_fOK= TRUE;
    SetKey( (LPCSTR)pKey );	
    if ( fEncrypt ) {
           m_fOK= CryptEncrypt( m_hKey, 0, TRUE, 0, pData, dwDataLen, *dwDataLen );
    }
    else  {
        m_fOK= CryptDecrypt( m_hKey, 0, TRUE, 0, pData, dwDataLen );
    }
    return( m_fOK );
}

CString CCrypt::EncryptStrToHex( LPCSTR szText, LPCSTR pszKey/*= 0*/, LPCSTR pszSalt/*= 0*/ )
{
    m_fOK= TRUE;
    CString sRet= "";
    DWORD nDataLen= strlen( szText );
    if ( pszSalt || pszKey || (0 == m_hKey) ) {
        m_fOK= SetKey( (LPCSTR)pszKey, pszSalt );	
    }
    if ( m_fOK ) {
        char* pTmp= new char[nDataLen+1] ;
        strncpy_s( pTmp, nDataLen+1, szText, nDataLen+1 );
        m_fOK= CryptEncrypt( m_hKey, 0, TRUE, 0, (BYTE*)pTmp, &nDataLen, nDataLen );
        if (m_fOK ) {
            sRet= EncodeToHex( (BYTE*)pTmp, nDataLen );
        }
        delete pTmp;
    }
    return( sRet );
}

CString CCrypt::DecryptStrFromHex( LPCSTR szHex, LPCSTR pszKey/*=0*/, LPCSTR pszSalt/*= 0*/ )
{
    m_fOK= TRUE;
    CString sRet= "";
    DWORD nDataLen= strlen( szHex );

    if ( pszSalt || pszKey || (0 == m_hKey) ) {
        m_fOK= SetKey( (LPCSTR)pszKey, pszSalt );	
    }
    if ( m_fOK ) {
        DWORD nDecryptLen= nDataLen/2;
        char* pTmp= new char[ nDecryptLen+1 ];
        DecodeFromHex( szHex, (BYTE*)pTmp, nDecryptLen );
        m_fOK= CryptDecrypt( m_hKey, 0, TRUE, 0, (BYTE*)pTmp, &nDecryptLen );
        if ( m_fOK ) {
            sRet= pTmp;
        }
        delete pTmp;
    }
    return( sRet );
}

//--------------------------------------------------------
// inefficient but requires no explanation :-)
CString CCrypt::EncodeToHex( BYTE* p, int nLen )
{
    CString sRet, sTmp;
    for( int j=0; j< nLen; j++ ) {
        sTmp.Format( "%02x", p[j] );
        sRet+= sTmp;
    }
    return (sRet );
}

//---------------------------------------------------------
// returns length of decoded hex buffer
int CCrypt::DecodeFromHex( LPCSTR pSrc, BYTE* pDest, int nBufLen )
{
    int nRet= 0;
    int nLen= strlen(pSrc);
    *pDest = 0;
    BYTE cIn1, cIn2, nFinal;
    for( int j=0; j< nLen; j += 2 ) {
        cIn1= (BYTE)toupper(*pSrc++);  cIn1 -= '0'; if ( cIn1>9 ) cIn1 -= 7;
        cIn2= (BYTE)toupper(*pSrc++);  cIn2 -= '0'; if ( cIn2>9 ) cIn2 -= 7;
        nFinal= (BYTE)((cIn1 << 4) | cIn2); 
        if (nFinal>255) nFinal=0; // in case trying to decode non-hex data
        *pDest++ = nFinal; 
        *pDest = 0;
        if ( nRet >= nBufLen ) {
            break;
        }
        nRet++;
    }
    return( nRet );
}

Open in new window


Here is an example of usage:
#include "Crypt.h"
    ...
void CEncryptTestDlg::OnBnClickedButton1()
{
   CCrypt crypt;
   char szSrc[]="Secret Password";

   //------------ use default rawKey
   CString sEncrypted= crypt.EncryptStrToHex( szSrc );  
   CString sDecrypted= crypt.DecryptStrFromHex( sEncrypted );
   MessageBox( sEncrypted,sDecrypted );

   //------------ test use of wrong salt value
   sEncrypted= crypt.EncryptStrToHex(   szSrc,     0,"mySalt"   );  
   sDecrypted= crypt.DecryptStrFromHex( sEncrypted,0,"yourSalt" ); // oops!
}

Open in new window


Notes:
The EncryptStrToHex function assumes that the input data is simple text -- it calculates the length using strlen().  Don't use it to encrypt binary data (any data that might contain an embedded NULL).
The CryptEncrypt and CryptDecrypt CryptoAPIs do their work in place; that is, they overwrite the original with the modified data.  I wanted to be able to pass LPCSTR data (constant string) to the functions, so I chose to make a copy of the incoming data.  This would be inefficient if working with larger buffers.
My actual (production) code provides a few variations of the main functions; for instance, it can encrypt binary data (not just text strings) and one variation does not encode to hex.  I removed most of this extraneous code from this article because those functions are not needed nearly as often -- and I wanted to leave something for you to do on your own :-)
You may want to "disguise" the length of the secret data.  For instance, a codebreaker could save a lot of time knowing that the cleartext string is only 3 characters (six hexadecimal digits).   The simplest technique is to pad the input string with spaces, and then strip them off when the cleartext is needed.  

But you can also use the lower-level Crypt::EncryptDecrypt function -- it does not assume that the input is clean text, so it will accept embedded NULLs; thus, you could append a NULL and pad with random characters.  For instance, if the input text is
     "Secret Password\0asXXefg"
then after decrypting, the string will end at the right place.
Just an afterthought:  If all you need is a way to authenticate the person who uses your program, there is no need to encrypt or store or transport his password at all.  Instead just create and store a hash of his password.  Each time he logs on, hash the input password and compare it to the stored value.  That way the cleartext password is irrelevant -- never needed and never stored.  

References:
Cryptography Reference
http://msdn.microsoft.com/en-us/library/aa380256(VS.85).aspx

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
If you liked this article and want to see more from this author,  please click the Yes button near the:
      Was this article helpful?
label that is just below and to the right of this text.   Thanks!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1
Comment
Author:DanRollins
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
4 Comments
 

Expert Comment

by:Sumit_Makhe
could you please upload demo project?

This code is not working for me...

1. I create new class "Crypt.h"
2.then "Crypt.cpp"

then during compilation VC++ asked me to add '#include "StdAfx.h"' in "Crypt.cpp" after that following list of errors appeared...

1>------ Build started: Project: EditPath, Configuration: Debug Win32 ------
1>Build started 3/1/2012 8:22:24 PM.
1>InitializeBuildStatus:
1>  Touching "Debug\EditPath.unsuccessfulbuild".
1>ClCompile:
1>  All outputs are up-to-date.
1>  Crypt.cpp
1>d:\ibs\editpath\editpath\crypt.cpp(1): warning C4627: '#include "Crypt.h"': skipped when looking for precompiled header use
1>          Add directive to 'StdAfx.h' or rebuild precompiled header
1>d:\ibs\editpath\editpath\crypt.cpp(4): error C2653: 'CCrypt' : is not a class or namespace name
1>d:\ibs\editpath\editpath\crypt.cpp(5): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\ibs\editpath\editpath\crypt.cpp(6): error C2065: 'm_hProv' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(6): error C2065: 'm_hHash' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(6): error C2065: 'm_hKey' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(7): error C2065: 'm_pszDefaultKeyRaw' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(8): warning C4508: 'CCrypt' : function should return a value; 'void' return type assumed
1>d:\ibs\editpath\editpath\crypt.cpp(9): error C2653: 'CCrypt' : is not a class or namespace name
1>d:\ibs\editpath\editpath\crypt.cpp(11): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(12): error C2065: 'm_hProv' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(13): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(13): error C2065: 'm_hProv' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(19): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(19): error C2065: 'm_hHash' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(20): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(20): error C2065: 'm_hHash' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(21): error C2065: 'm_hHash' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(23): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(23): error C2065: 'm_hHash' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(24): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(24): error C2065: 'm_hProv' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(24): error C2065: 'm_hHash' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(26): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(29): error C2065: 'm_pszDefaultKeyRaw' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(42): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(42): error C2065: 'm_hHash' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(44): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(45): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(45): error C2065: 'm_hProv' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(45): error C2065: 'm_hHash' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(45): error C2065: 'm_hKey' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(47): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(48): error C2065: 'm_nLastErr' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(49): error C2065: 'm_sErrMsg' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(51): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(54): error C2653: 'CCrypt' : is not a class or namespace name
1>d:\ibs\editpath\editpath\crypt.cpp(56): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(57): error C2660: 'SetKey' : function does not take 1 arguments
1>d:\ibs\editpath\editpath\crypt.cpp(59): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(59): error C2065: 'm_hKey' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(62): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(62): error C2065: 'm_hKey' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(64): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(67): error C2653: 'CCrypt' : is not a class or namespace name
1>d:\ibs\editpath\editpath\crypt.cpp(69): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(70): error C2440: 'initializing' : cannot convert from 'const char [1]' to 'ATL::CStringT<BaseType,StringTraits>'
1>          with
1>          [
1>              BaseType=wchar_t,
1>              StringTraits=StrTraitMFC_DLL<wchar_t>
1>          ]
1>          Constructor for class 'ATL::CStringT<BaseType,StringTraits>' is declared 'explicit'
1>          with
1>          [
1>              BaseType=wchar_t,
1>              StringTraits=StrTraitMFC_DLL<wchar_t>
1>          ]
1>d:\ibs\editpath\editpath\crypt.cpp(72): error C2065: 'm_hKey' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(73): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(75): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(78): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(78): error C2065: 'm_hKey' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(79): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(80): error C3861: 'EncodeToHex': identifier not found
1>d:\ibs\editpath\editpath\crypt.cpp(87): error C2653: 'CCrypt' : is not a class or namespace name
1>d:\ibs\editpath\editpath\crypt.cpp(89): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(90): error C2440: 'initializing' : cannot convert from 'const char [1]' to 'ATL::CStringT<BaseType,StringTraits>'
1>          with
1>          [
1>              BaseType=wchar_t,
1>              StringTraits=StrTraitMFC_DLL<wchar_t>
1>          ]
1>          Constructor for class 'ATL::CStringT<BaseType,StringTraits>' is declared 'explicit'
1>          with
1>          [
1>              BaseType=wchar_t,
1>              StringTraits=StrTraitMFC_DLL<wchar_t>
1>          ]
1>d:\ibs\editpath\editpath\crypt.cpp(93): error C2065: 'm_hKey' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(94): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(96): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(99): error C3861: 'DecodeFromHex': identifier not found
1>d:\ibs\editpath\editpath\crypt.cpp(100): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(100): error C2065: 'm_hKey' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(101): error C2065: 'm_fOK' : undeclared identifier
1>d:\ibs\editpath\editpath\crypt.cpp(111): error C2653: 'CCrypt' : is not a class or namespace name
1>d:\ibs\editpath\editpath\crypt.cpp(115): error C2664: 'void ATL::CStringT<BaseType,StringTraits>::Format(const wchar_t *,...)' : cannot convert parameter 1 from 'const char [5]' to 'const wchar_t *'
1>          with
1>          [
1>              BaseType=wchar_t,
1>              StringTraits=StrTraitMFC_DLL<wchar_t>
1>          ]
1>          Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>d:\ibs\editpath\editpath\crypt.cpp(123): error C2653: 'CCrypt' : is not a class or namespace name
1>
1>Build FAILED.
0
 
LVL 49

Author Comment

by:DanRollins
Create a dialog-based MFC App.  Add the two files.
0
 

Expert Comment

by:Sumit_Makhe
Yes Dan I did that, My application is in VC++ MFC Unicode. My goal is to protect(encrypt) password string using my personal key. This string will be saved in Windows registry. I'm going to use this password by fetching registry key and decryption using the same key but in different application. Nothing is working for me, I tried with DPAPI but no luck. Where did I go wrong?
0
 

Expert Comment

by:KittyKat09
I think your code would not compile becuase the name of the .h file in the code should be Crypt.h and not "utCrypt.h" which is in the .cpp file as #include "utCrypt.h" which is no where to be found. So I just deleted the "ut" part and all of the errors disapear expect for this one :

Error      2      error LNK2019: unresolved external symbol "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall CCrypt::EncodeToHex(unsigned char *,int)" (?EncodeToHex@CCrypt@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PAEH@Z) referenced in function "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall CCrypt::EncryptStrToHex(char const *,char const *,char const *)" (?EncryptStrToHex@CCrypt@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PBD00@Z)      C:\Users\user\Documents\Visual Studio 2010\Projects\KeyDll\SprintDll\Crypt.obj

any clue to why this is being thrown? I am trying to add this to a DLL project I have. Is it possible to include in a TLL console app? or do I need to Create a dialog-based MFC App? I would prefer to just create the .cpp and .h files.
0

Featured Post

Revamp Your Training Process

Drastically shorten your training time with WalkMe's advanced online training solution that Guides your trainees to action.

Join & Write a Comment

This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month