Requirements
WTL/ATL7 projects are built as UNICODE. You can make the decryptor project as non-Unicode if you want to. Projects require Visual Studio 2003 or higher. WTL projects require WTL, which can be downloaded from the Microsoft Website. OpenSSL projects require OpenSSL Crypto Lib.
Introduction
This is a simple encrypt and self-decrypt project. It encrypts based on the chosen algorithm and password. It writes out a self-decrypting executable which asks for a password to decrypt the data. For a "native" version, the way it creates the self-extracting executable is by embedding the resources, using the NT-only UpdateResource function. For a .NET version, it builds the executable from embedded C# source and then writes out a resource file, passing it on to the compiler to be embedded.
Background
A few things (among many) to remember:
- Quote: "When using CBC mode with block ciphers, you need to worry about CBC rollover. [...] On average, a cipher with an X-bit blocksize will have two ciphertext values collide every 2^X/2 blocks. Thus, it's important not to encrypt more than this amount of data with a given key" - SSL and TLS Designing and Building Secure Systems
- Clear any sensitive data with such helpers as SecureZeroMemory (it's not Win2k3 only) and not with
memset
. In .NET, at least use Array.Clear
. Refer to Scrubbing Secrets in Memory (MSDN).
- As soon as you're done with an object , always call
.Clear
methods of the SymmetricAlgorithm
/HashAlgorithm
-derived classes and CryptoStream.
- .NET comes with password-deriving class called
PasswordDeriveBytes
in cases when you need to derive a key from a user's input.
- OpenSSL comes with a password-deriving function, also.
Code Snippets
ATL7 MS CryptoAPI Classes (without Error Checking)
CCryptProv prov;
HRESULT hr = prov.Initialize([PROV_RSA_FULL|PROV_RSA_AES], NULL,
[MS_ENHANCED_PROV|MS_ENH_RSA_AES_PROV]);
if(hr == NTE_BAD_KEYSET)
hr = prov.Initialize(dwProviderType, NULL, szProvider,
CRYPT_NEWKEYSET);
CCryptDerivedKey derKey;
CCryptSHAHash sha;
sha.Initialize(prov);
TCHAR pPass1[MAX_PASS_LEN], pPass2[MAX_PASS_LEN];
int nPass1 = ::GetWindowText(hWndPwd1, pPass1, MAX_PASS_LEN);
int nPass2 = ::GetWindowText(hWndPwd2, pPass2, MAX_PASS_LEN);
hr = sha.AddData((BYTE*)pPass1, nPass1*sizeof(TCHAR));
SecureZeroMemory(pPass1, MAX_PASS_LEN*sizeof(TCHAR));
SecureZeroMemory(pPass2,MAX_PASS_LEN*sizeof(TCHAR));
BYTE tmpHash[20];
DWORD dw= sizeof(tmpHash);
for(int i=0;i<101; ++i)
{
hr = sha.GetValue(tmpHash, &dw);
sha.Destroy();
hr = sha.Initialize(prov);
hr = sha.AddData(tmpHash, dw);
}
SecureZeroMemory(tmpHash, dw);
hr = derKey.Initialize(prov,sha,[CALG_AES_256|CALG_3DES]);
BYTE iv[16];
hr = prov.GenRandom(16, iv);
hr = derKey.SetIV(iv);
hr = derKey.SetMode(CRYPT_MODE_CBC);
hr = derKey.SetPadding(PKCS5_PADDING);
HRSRC res =FindResource(NULL, MAKEINTRESOURCE(IDR_DUMMY), _T("dummy"));
HGLOBAL hGlobal = LoadResource(NULL, res);
int resLen = SizeofResource(NULL, res);
void* buf = LockResource(hGlobal);
FILE* file = _tfopen((LPCTSTR)sFileName, _T("wb"));
fwrite(buf, 1, resLen, file);
fclose(file);
BOOL bRet = FALSE;
HANDLE hUpdateRes = BeginUpdateResource(sFileName, FALSE);
derKey.Encrypt(TRUE, (BYTE*)data.m_pData, &dwFileSize, dwFileSize + blocksize);
sha.Destroy();
UpdateResource(hUpdateRes, RT_RCDATA, _T("1"),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
(void*)(LPCTSTR)sAlgID, sAlgID.GetLength()*sizeof(TCHAR));
UpdateResource(hUpdateRes, RT_RCDATA, _T("2"),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
dlgOpen.m_szFileTitle,
_tcslen(dlgOpen.m_szFileTitle)*sizeof(TCHAR));
UpdateResource(hUpdateRes, RT_RCDATA, _T("3"),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
data.m_pData, dwFileSize));
UpdateResource(hUpdateRes, RT_RCDATA, _T("4"),
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
iv, sizeof(iv))
EndUpdateResource(hUpdateRes, FALSE);
.NET App (without Error Checks)
PasswordDeriveBytes pdb = new PasswordDeriveBytes(txtPass1.Text,
UnicodeEncoding.Unicode.GetBytes("somesalt"),
"SHA512", 100);
SymmetricAlgorithm symalg=null;
if(cmbAlg.Text == "Rijndael")
{
symalg = new OpenCrypto.RijndaelOpenProvider();
algid = "1";
Key = pdb.GetBytes(32);
IV = pdb.GetBytes(16);
}
else if(cmbAlg.Text == "TripleDES")
{
symalg = new OpenCrypto.TripleDESOpenProvider();
algid = "2";
Key = pdb.GetBytes(24);
IV = pdb.GetBytes(8);
}
else if(cmbAlg.Text == "Blowfish")
{
symalg = new OpenCrypto.BlowfishOpenProvider();
algid = "3";
Key = pdb.GetBytes(32);
IV = pdb.GetBytes(8);
}
Stream inStream = dlgEncryptFile.OpenFile();
ICryptoTransform encryptor = symalg.CreateEncryptor(Key, IV);
Array.Clear(Key, 0, Key.Length);
Array.Clear(IV, 0, IV.Length);
CryptoStream cryptStream = new CryptoStream(inStream, encryptor,
CryptoStreamMode.Read);
byte[] encrData = new byte[inStream.Length + encryptor.OutputBlockSize];
int readBlockSize = encryptor.OutputBlockSize * 1000;
int totalEncrBytes=0;
for(int BytesRead=0; totalEncrBytes < encrData.Length;
totalEncrBytes += BytesRead)
{
if(totalEncrBytes + readBlockSize > encrData.Length)
readBlockSize = encrData.Length - totalEncrBytes;
BytesRead = cryptStream.Read(encrData, totalEncrBytes, readBlockSize);
if(BytesRead == 0)
break;
}
encryptor.Dispose();
cryptStream.Clear();
cryptStream.Close();
symalg.Clear();
ResourceWriter resWriter = new ResourceWriter(applicationPath +
"\\encrypted.resources");
resWriter.AddResource("1", algid);
resWriter.AddResource("2", fileTitle);
resWriter.AddResource("3", totalEncrBytes);
resWriter.AddResource("4", encrData);
encrData = null;
inStream.Close();
resWriter.Generate();
resWriter.Dispose();
BuildDecryptorAssembly();
if(File.Exists(applicationPath + "\\encrypted.resources"))
File.Delete(applicationPath + "\\encrypted.resources");
private void BuildDecryptorAssembly()
{
CSharpCodeProvider csprov = new CSharpCodeProvider();
ICodeCompiler compiler = csprov.CreateCompiler();
CompilerParameters compilerparams = new CompilerParameters(new string[]
{"System.dll", "System.Windows.Forms.dll", "System.Drawing.dll",
"opencrypto.dll"},
txtOutFile.Text, false);
compilerparams.GenerateExecutable = true;
compilerparams.CompilerOptions = "/target:winexe /resource:\""
+ applicationPath +
"\\encrypted.resources\",encrypted";
CompilerResults cr = compiler.CompileAssemblyFromSource(compilerparams, ...);
CompilerErrorCollection errs = cr.Errors;
if(!errs.HasErrors)
MessageBox.Show("Operation Successful");
else
{
foreach(CompilerError err in errs)
MessageBox.Show(err.ToString());
}
}
Two Projects Depend on OpenSSL
Two of the samples in this article build upon the OpenSSL crypto library, which builds without any problems with a Visual C++ 7 compiler. For building instructions, refer to the INSTALL.W32 file that comes with the OpenSSL library. You can build a Release/Debug of static/DLL library versions. Some of the algorithms implemented by the OpenSSL library are patented. Please refer to the README file of the OpenSSL library, especially to the Patents section. Here is the quote from the README file:
"Various companies hold various patents for various algorithms in various locations around the world. You are responsible for ensuring that your use of any algorithms is legal by checking if there are any patents in your country. The file contains some of the patents that we know about or are rumored to exist. This is not a definitive list.
RSA Security holds software patents on the RC5 algorithm. If you intend to use this cipher, you must contact RSA Security for licensing conditions. Their web page is http://www.rsasecurity.com/. RC4 is a trademark of RSA Security, so use of this label should perhaps only be used with RSA Security's permission.
The IDEA algorithm is patented by Ascom in Austria, France, Germany, Italy, Japan, the Netherlands, Spain, Sweden, Switzerland, UK and the USA. They should be contacted if that algorithm is to be used; their web page is http://www.ascom.ch/"
To build a static library without patented algorithms -- for example, mdc2, rc5, idea, etc. -- define: no-mdc2 no-rc5 no-idea
in do_ms.bat for Visual C++ WIN32. About the legal issues, if any, with RC* algorithms, you can read here.
Links
History
- 7 Aug 2003 - updated
wtlcrypt
and article content
- 10 Aug 2003 - updated
wtlcrypt
and article content
Disclaimer: THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.