Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Encrypting Log Files

0.00/5 (No votes)
16 Jul 2000 1  
Demonstrates using encryption to protect sensitive application log file data.

This article makes use of the Crypto++ library, available here. To run compile the demo code, you will need to download the Windows version of the Crypto++ library and source files.

Overview

This was put together because I needed a secure log file class that would allow selective access.

I needed something that could Write/Read encrypted log files (because sensitive data was being kept in them). I also wanted to know who was using the application (Domain\User), so as to restrict access accordingly.

In the application I was writing, I created two little dialogs for logging on and changing the Pass Phrase/Password. The CLogIt demo application includes these dialogs.

The first thing I needed was the ability to login, so I had to create a Login Screen (below). I added the "Admin Password" edit box because I wanted a way to know if the current user had rights to use certain controls in the application (some of the logs/controls may contain sensitive information).

LogDemo Passphrase Screen Image

If they got past here then they had the correct Pass Phrase, and possibly the correct Admin Password.

There are only two entries in the registry - one for "Pass Phrase" and the other for "Admin Password". Both registry entries are encrypted, and only this application can decrypt them (using a built-in Pass Phrase).

The built in Pass Phrase encrypts the two entries in the registry, the user's Pass Phrase (given at login) En/Decrypts the log information.

I needed the ability to change these if they were ever compromised (or I thought they were), so the following dialog was born - (which should be only available to users who have the Admin password)

LogDemo Change Password/Passphrase Screen Image

What does the application look like (you ask).......

LogDemo Main Screen Image

Here is what the log file looks like.......

LogDemo Log File Image

I know it isn't so pretty (no splitter window, etc...), but I wanted to just put this together for you all and not spend a lot of time on it.

Implementation Notes

I'll explain the CLogIt code, then how I implemented the Crypt++ v3.2.

I had to find a way to get the Domain\User in one swipe, and based this code on the MSDN article Q155698...

BOOL CLogIt::GetSystemDomainUserName(void)
{

    LPTSTR UserName = User;
    LPDWORD cchUserName = &cchUser;
    LPTSTR DomainName = Domain;
    LPDWORD cchDomainName = &cchDomain;

    HANDLE hToken;

    #define MY_BUFSIZE 512  // highly unlikely to exceed 512 bytes


    UCHAR InfoBuffer[ MY_BUFSIZE ];
    DWORD cbInfoBuffer = MY_BUFSIZE;
    SID_NAME_USE snu;

    BOOL bSuccess;

    BOOL bRet = FALSE;

    CString csMsg = _T("");

    if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken)) {

        if(GetLastError() == ERROR_NO_TOKEN) {
            //

            // attempt to open the process token, since no thread token

            // exists

            //

            if(!OpenProcessToken(GetCurrentProcess(), 
                              TOKEN_QUERY, &hToken )) {
                csMsg.Format("Error(%u): OpenProcessToken: %s", 
                   GetLastError(), DisplayError(GetLastError()));
                WriteLog(csMsg, 0);
                return FALSE;
            }

        } 
        else {
            //

            // error trying to get thread token

            //

            csMsg.Format("Error(%u): OpenThreadToken: %s", 
              GetLastError(), DisplayError(GetLastError()));
            WriteLog(csMsg, 0);
            return FALSE;
        }
    
        bSuccess = GetTokenInformation(hToken, TokenUser, 
                     InfoBuffer, cbInfoBuffer, &cbInfoBuffer);
        if(!bSuccess) {
            csMsg.Format("Error(%u): GetTokenInformation: %s", 
                GetLastError(), DisplayError(GetLastError()));
            WriteLog(csMsg, 0);
            return FALSE;
        }
        else {
            bRet = LookupAccountSid(NULL, 
                ((PTOKEN_USER)InfoBuffer)->User.Sid, 
                UserName, cchUserName, DomainName, 
                cchDomainName, &snu );
            if (!bRet) {
                csMsg.Format("Error(%u): LookupAccountSid: %s", 
                  GetLastError(), DisplayError(GetLastError()));
                WriteLog(csMsg, 0);
                CloseHandle(hToken);
                return FALSE;
            }
            else {
                // fill the variable with the Domain\User Data

                m_csDomainUserName.Format("%s\\%s", DomainName, UserName);
            }
        }
    }

    return TRUE;

}

Now with that out of the way, I needed to write a function to log to a file and encrypt it.

For the image lists, I used a bitmap (16 x ...) and so when I write to the log, I simply tell it which image (from the image list) to use, based on the type of the log entry (see below code). Of course, I did have to write a class CLogIt to hold all of this (and a few extra functions to support the class).

Here is the basic method to log an encrypted string to a file:

//*********************************************************************

//

// nEntryType:

//

//        0 = Error

//        1 = Warning

//        2 = Information

//        3 = Lock (Secure)

//        4 = Dir Folder (Closed)

//        5 = Dir Folder (Opened)

//        6 = Paper (Log)

//        7 = User (incognito)

//        8 = Note

//

//**********************************************************************

void CLogIt::WriteLog(CString csMsg, int nEntryType)
{
    // We don't want to write to it 

    // if it's being writing to!

    if (!IsOpen()) {

        CString csText = _T("");
        if (m_bDirExists) {
            m_fLog = fopen(m_csFullLogName, "a");
            if (m_fLog != NULL) {
                csText.Format("%s~%i~%s~%s", m_csDomainUserName, 
                            nEntryType, m_csLogEntryTime, csMsg);
                CString szRet = EncryptString(LPCTSTR(csText), 
                            LPCTSTR(m_csAppsPPhrase));
                csText = szRet;
                fprintf(m_fLog, "%s\n", csText);
            }

            int nClosed = fclose(m_fLog);
            if (nClosed == 0)
                m_fLog = NULL;
            else
                MessageBox(NULL, "Problem closing log file", 
                            "Error: Closing file", MB_OK);

            Sleep(100); // Give it time to close


        }
        else {
            csText.Format("Logging Directory doesn't exist [%s]!", m_csLogDir);
            MessageBox(NULL, csText, "Error: Accessing Log Directory", MB_OK);
        }
    }
}

The EncryptString method called above uses the DefaultEncryptorWithMAC class of the Crypto++ libraries.

Basically, this code is modeled on the code from the "Cryptest" Project (found in the Crypto++ zip file), modified so I could use CString instead of const char *:

using namespace CryptoPP; // Cryptlib.zip source uses a namespace


CString CLogIt::EncryptString(CString csInStr, CString csPassPhrase)
{
    unsigned int unLen = csInStr.GetLength();
    char* szOutstr;

    DefaultEncryptorWithMAC encryptor((LPCTSTR)csPassPhrase, new HexEncoder());
    encryptor.Put((byte *)(LPCTSTR)csInStr, unLen);
    encryptor.Close();

    unsigned int unOutputLength = encryptor.MaxRetrieveable();
    szOutstr = new char[unOutputLength+1];
    encryptor.Get((byte *)szOutstr, unOutputLength);
    szOutstr[unOutputLength] = 0;

    CString csRet = szOutstr;
    return csRet;
}


CString CLogIt::DecryptString(CString csInStr, CString csPassPhrase)
{
    unsigned int unLen = csInStr.GetLength();
    char* szOutstr;

    DefaultDecryptorWithMAC *p;

    HexDecoder decryptor(p=new DefaultDecryptorWithMAC((LPCTSTR)csPassPhrase));
    decryptor.Put((byte *)(LPCTSTR)csInStr, unLen);
    decryptor.Close();
    assert(p->CurrentState() == DefaultDecryptorWithMAC::MAC_GOOD);

    unsigned int unOutputLength = decryptor.MaxRetrieveable();
    szOutstr = new char[unOutputLength+1];
    decryptor.Get((byte *)szOutstr, unOutputLength);
    szOutstr[unOutputLength] = 0;

    CString csRet = szOutstr;
    return csRet;
}

That's it!

I was asked by someone why I am posting this information (Encrypt/Decrypt) source that could prove to be dangerous to what I write at home, and my reply was "I got this code from the web and I just simply added a GUI to it, so all I am doing is reposting something that has already been done (just with a different look)".

In my version (at home), I used the CFileChangeEvent class written by Franky Braem (also found here at CodeProject) that monitors the current log file for changes. When it is changed (by someone else using the program) and if I am currently viewing that log file, it refreshes it with the new information. (If you want that added, let me know).

I would like to say at this time that without all of you out there (and Chris Maunder for CodeProject) who are willing to share your ideas (and code), some of us beginner programmers would never be able to develop our skills so quickly and more important so efficiently...On that note, I basically just created something you folks already have, and if that is the case, I am always willing to learn different (possibly the correct) ways to do things. So please let me know if it can be done easier/more effectively/or just that you liked it!!!! Thanks again! Dan

Obtaining the Crypto++ Library

Now I am only a beginner in C++ programming (approx. 1 yr.) and I would never be able to make a class for encryption at this stage of my C++ playing, so I surf'd until I found some source code that does the job. The code I found was source called "Crypto++ v3.2". I basically added the library (and all necessary header files) to get the En/Decryption working. The source also contains an example (Console App) called "Cryptest", and as with any open source site, it is below (in it's entirety). In it you will find code for "DES, MD2, MD5, RC2, RC5, RC6, RSA (to name a few)" encryption. There is even code to "Zip (gzip/gunzip), In/Deflate, etc...".

The simplest way to compile the demo application, having downloaded the Crypto++ files, is to provide the compiler with the paths needed to find the header files and the compiled library.  You can set these up using the Tools | Options | Directories page from the VC IDE.

Download Crypto++ v3.2.

Acknowledgements

Along with the Crypto++ library (see above), the demo also makes use of Robert Pittenger's CRegistry class. This class has been included in the demo download.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here