Poor Key Management

Poor Key Management

Using cryptography is easy; securely storing and using keys is hard. All too often, good systems are let down by poor key management. For example, hard-coding a key in an executable image is trivial to break, even when people don t have access to your source code.

Breaking DVD Encryption: A Hard Lesson in Storing Secrets

Possibly the most famous exploit involving storing secret data in an executable file is the DVD encryption keys exposed by the XingDVD Player from RealNetworks Inc. subsidiary Xing Technologies. The software did not have the DVD keys satisfactorily protected, and hackers were able to release a controversial program named DeCSS to crack DVDs based on key information gleaned from the executable. More information about this is available at www.cnn.com/TECH/computing/9911/05/dvd.hack.idg.

If a key is a simple text string such as This1sAPa$sword, you can use a tool (such as one named Strings) to dump all the strings in a .DLL or .EXE to determine the password. Simple trial and error by the attacker will determine which string on the file is the correct key. Trust me: such strings are extremely easy to break. File a bug if you see lines such as these:

// SSsshh!! Don't tell anyone. char *szPassword="&162hV1);sWa1";

And what if the password is highly random, as a good key should be? Surely a tool like Strings will not find the key because it s not an ASCII string. It too is easy to determine because the key data is random! Code and static data are not random. If you create a tool to scan for entropy in an executable image, you will quickly find the random key.

In fact, such a tool has been created by a British company named nCipher (www.ncipher.com). The tool operates by attaching itself to a running process and then scanning the process memory looking for entropy. When it finds areas of high randomness, it determines whether the data is a key, such as a key used for SSL/TLS. Most of the time, it gets it right! A document outlining this sort of attack, Playing Hide and Seek with Stored Keys, is available at www.ncipher.com/products/rscs/downloads/whitepapers/keyhide2.pdf. nCipher has kept the tool to itself.

Refer to Chapter 7, Storing Secrets, for information about storing secret information in software.

important

Do not hard-code secret keys in your code. They will be found out; it is just a matter of time. If you think no one will work it out, you are sadly mistaken.

Keep Keys Close to the Source

When using secret information such as cryptographic keys and passwords, you must keep the keys close to the point where they encrypt and decrypt data. The rationale is simple: highly mobile secrets stay secret only a short time! As a friend once said to me, The value of a secret is inversely proportional to its availability. Or, put another way, A secret known by many is no longer a secret! This applies not only to people knowing a secret but also to code. As I mentioned earlier in this book, all code has bugs, and the more code that has access to secret data, the greater the chance the secret will be exposed to an attacker. Take a look at Figure 6-2.

Figure 6-2

Allowing keys to roam through an application and keeping keys close to the point where they are used.

The example on the left of Figure 6-2 shows the password passed from function to function and executable to executable. GetKey reads the password from a persistent store and passes the password through EncryptWithKey, Encrypt, DoWork, and ultimately to EncryptData. This is a poor design because a security flaw in any of the functions could leak the private password to an assailant armed with a debugger.

The example on the right is a better design. GetKeyHandle acquires a handle to the password and passes the handle eventually to EncryptData, which then reads the key from the persistent store. If any of the intermediate functions are compromised, the attacker has access only to the handle and not to the password directly.

important

Secret data, including passwords, passed throughout an application is more likely to be compromised than secret data maintained in a central location and used only locally.

The CryptGenKey and CryptExportKey Functions

Microsoft CryptoAPI (CAPI) includes the CryptGenKey function to generate a cryptographically strong key, yet you never see the key value directly. Rather, you access it using a handle to the key. The key is protected by CAPI, and all references to the key are made through the handle. If you need to store the key in some form of persistent storage, such as a floppy disk, a database, a file, or the registry, you can export the key by using the CryptExportKey function and import the key from the persistent store by using CryptImportKey. The key is protected by either a public key in a certificate (and later decrypted with the private key) or, new in Windows 2000 and later, a symmetric key. The key is never in plaintext except deep inside CryptoAPI, and hence the key is safer. Plaintext refers to text that hasn t been encrypted. Sometimes it s also called cleartext.

The following C++ code shows how to generate and export a private key:

/* ProtectKey.cpp */ #include "stdafx.h" using namespace std; // Get the symmetric exchange key used to encrypt the key. void GetExchangeKey(HCRYPTPROV hProv, HCRYPTKEY *hXKey) { // The key-exchange key comes from an external source. HCRYPTHASH hHash; BYTE bKey[16]; if (!GetKeyFromStorage(bKey, sizeof bKey)) throw GetLastError(); if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) throw GetLastError(); if (!CryptHashData(hHash, bKey, sizeof bKey, 0)) throw GetLastError(); if (!CryptDeriveKey(hProv, CALG_3DES, hHash, CRYPT_EXPORTABLE, hXKey)) throw GetLastError(); } void main() { HCRYPTPROV hProv = NULL; HCRYPTKEY hKey = NULL; HCRYPTKEY hExchangeKey = NULL; LPBYTE pbKey = NULL; try { if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) throw GetLastError(); // Generate two 3DES keys, and mark them as exportable. // Note: these keys are kept in CAPI at this point. if (!CryptGenKey(hProv, CALG_3DES, CRYPT_EXPORTABLE, &hKey)) throw GetLastError(); // Get a key that we can use to encrypt the two 3DES keys. GetExchangeKey(hProv, &hExchangeKey); // Determine blob size. DWORD dwLen = 0; if (!CryptExportKey(hKey, hExchangeKey, SYMMETRICWRAPKEYBLOB, 0, pbKey, &dwLen)) throw GetLastError(); pbKey = new BYTE[dwLen]; // Array to hold 3DES keys ZeroMemory(pbKey, dwLen); // Now get the shrouded blob. if (!CryptExportKey(hKey, hExchangeKey, SYMMETRICWRAPKEYBLOB, 0, pbKey, &dwLen)) throw GetLastError(); cout << "Cool, " << dwLen << " byte wrapped key is exported." << endl; // Write shrouded key to Key.bin; overwrite if needed // using ostream::write() rather than operator<<, // as the data may contain NULLs. ofstream file("c:\\key.bin", ios_base::binary); file.write(reinterpret_cast<const char *>(pbKey), dwLen); file.close(); } catch(DWORD e) { cerr << "Error " << e << hex << " " << e << endl; } // Clean up. if (hExchangeKey) CryptDestroyKey(hExchangeKey); if (hKey) CryptDestroyKey(hKey); if (hProv) CryptReleaseContext(hProv, 0); if (pbKey) delete [] pbKey; }

You can also find the example code on the companion CD in the folder Secureco\Chapter 6\ProtectKey. Note that the GetExchangeKey function is only an example your application will need to have a version of this function to acquire the key-exchange key from its storage location or possibly from the user. From now on, you can acquire the shrouded key from storage and use it to encrypt and decrypt data without knowing what the key actually is! This application generates two Triple-DES (3DES) keys. 3DES is an encrypting algorithm that processes data three times in succession with three different keys. It s more difficult to break than straight DES.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2005
Pages: 153

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net