Sunday, November 16, 2008

Crypto++ to PHP

This article if following one article that I wrote about using libcurl or wget to post data to a coldfusion and php server. In my sample data were sent in a clear form to the server which is NOT the best things to do.
When submitting sensitive data to a server over HTTP using HTTP Post Request it is highly recommended to use encryption or any sort of obfuscation of your own using xor and rotation etc... to encapsulate data inside your http key/value form data.
A c++ client can achieve this thanks to the great C++ library Crypto++. There are various encryption type out there some more stronger than other... Which means some can be broken super fast some will require more CPU time. I like asymetric encryption in a sense that the client doesn't store a unique key which has to be hidden inside the client code but use a key pair instead and just the public key is stored inside the client whereas the server will store the private key... Although,
I have not found any PHP implementation of RSA so far and I think mcrypt didn't support RSA for copyright issues I guess... I would love to see a complete support of crypto++ written in PHP or available as a PHP module. If you find anything out there let me know. As a great alternative, since mcrypt support ECB a DES_EDE3 encryption solution which is also supported by crypto++ here is a good way to encrypt data in C++ and decrypt data in PHP.

Here we go !

Client Side using ECB - DES_EDE3
==================================

#include "cryptlib.h"
#include "modes.h"
#include "des.h"
#include "base64.h" <-- any base64 encoder/decoder i found the usage of crypto++ base64 class a bit hard to use since you have to know how many byte are taken to encode a byte in base 64...
#include "hex.h"

// Encode the data using the handy Crypto++ base64 encoder. Base64 uses
// 3 characters to store 2 characters.
const int BUFFER_LENGTH = 255;
byte plaintext[BUFFER_LENGTH];
byte ciphertext[BUFFER_LENGTH];
byte newciphertext[BUFFER_LENGTH];
byte decrypted[BUFFER_LENGTH];

CryptoPP::Base64Encoder base64Encoder;
CBase64Coding base64Coder;
CString MySensitiveDataUncrypted;
CString MySensitiveData;

// Set up the same key and IV
const int KEY_LENGTH = 24;
const int BLOCK_SIZE = CryptoPP::DES::BLOCKSIZE;
byte key[KEY_LENGTH], iv[CryptoPP::DES::BLOCKSIZE];
memset( key, 0, KEY_LENGTH);
memcpy( key, "012345678901234567890123", KEY_LENGTH );
memset( iv, 0, CryptoPP::DES::BLOCKSIZE);
memcpy( iv, "01234567", CryptoPP::DES::BLOCKSIZE );
memset( plaintext, 0, BUFFER_LENGTH);
memset( ciphertext, 0, BUFFER_LENGTH);
memset( newciphertext, 0, BUFFER_LENGTH);
strcpy((char*)plaintext,MySensitiveDataUncrypted.GetBuffer(0));
// now encrypt
CryptoPP::ECB_Mode::Encryption ecbEncryption(key, sizeof(key));
ecbEncryption.ProcessString(newciphertext, plaintext, BUFFER_LENGTH);
// your own base64 encoder/decoder
base64Coder.Encode((char *)newciphertext,BUFFER_LENGTH,(char *)ciphertext);
MySensitiveData.Format(_T("%s"),ciphertext);

// MySensitiveData can now be send over http


Server Side in PHP using ECB - DES_EDE3
=========================================

// $MyBase64EncodedSecretString will receive/store the encrypted string which will also be base64Encoded for HTTP protocol convenience

$key = "012345678901234567890123";
$iv = "01234567";

// Set up an "encryption" descriptor. This is basically just an object that
// encapsulates the encryption algorithm. 'tripledes' is the name of the
// algorithm, which is simply the DES algorithm done three times back to
// back. 'ecb' describes how to encrypt different blocks. See, DES
// actually only encrypts 8-byte blocks at a time. To encrypt more than 8
// bytes of data, you break the data up into 8-byte chunks (padding the
// last chunk with NULL, if need be), and then encrypt each block
// individually. Now, ECB (which stands for "Electronic Code Book", for
// whatever that's worth) means that each 8-byte block is encrypted
// independently. This has pros and cons that I don't care to discuss.
// The other option is CBC ("Cipher Block Chaining") which links the blocks,
// such as by XORing each block with the encrypted result of the previous
// block. Security geeks probably really get excited about this, but for my
// needs, I don't really care.
$td = mcrypt_module_open( 'tripledes', '', 'ecb', '' );
mcrypt_generic_init( $td, $key, $iv );

// Grab some interesting data from the descriptor.
// $maxKeySize = 24, meaning 24 bytes
// $maxIVSize = 8, meaning 8 bytes
$maxKeySize = mcrypt_enc_get_key_size( $td );
$maxIVSize = mcrypt_enc_get_iv_size( $td );
//echo "maxKeySize=$maxKeySize, maxIVSize=$maxIVSize\n";

// let's decrypt it and verify the result. Because DES pads
// the end of the original block with NULL bytes, let's trim those off to
// create the final result.
$MyEncodedSecretString = base64_decode( $MyBase64EncodedSecretString );
$MyDecodedString = rtrim( mdecrypt_generic( $td, $MyEncodedSecretString ), "\0" );

// And finally, clean up the encryption object
mcrypt_generic_deinit($td);
mcrypt_module_close($td);


Client Side Stronger Encryption using RSA
=========================================

First you will need to generate a public/private key pair using crypto++ keygen console application then your client code should be something like

// Client Side Using RSA
#include "cryptlib.h"
#include "rsa.h"
#include "hex.h"
#include "randpool.h"
#include "filesource.h"

CString MyNotverySecretStringInMemory;
CString MySensitiveData;
char pubFilename[128];
char seed[1024], message[1024];

// MAX = 19999991
strcpy(seed,"12345");

CString tmpPath;
TCHAR appPath[MAX_PATH];
::GetModuleFileName(NULL,appPath,MAX_PATH);

tmpPath = appPath;
tmpPath = tmpPath.Left(tmpPath.ReverseFind('\\')+1);
tmpPath += "public.key"; // 1024 key length for higher security.

strcpy(pubFilename,tmpPath.GetBuffer(0));
strcpy(message,MyNotverySecretStringInMemory.GetBuffer(0));
CryptoPP::FileSource pubFile(pubFilename, true, new CryptoPP::HexDecoder);
CryptoPP::RSAES_OAEP_SHA_Encryptor pub(pubFile);
CryptoPP::RandomPool randPool;
randPool.IncorporateEntropy((byte *)seed, strlen(seed));
std::string result;
CryptoPP::StringSource(message, true, new CryptoPP::PK_EncryptorFilter(randPool, pub, new CryptoPP::HexEncoder(new CryptoPP::StringSink(result))));
MySensitiveData.Format(_T("%s"),result.c_str());

// Server code will need to use private.key to decode the hexencode rsa string


Didn't find any PHP implementation yet if anyone heard about one shoot me an email.

Use it at your own certitude :)) to think your solution is going to be more secure. I would say one sentence about security. All is a matter of time given to put yourself or your solution in a "SAFE" zone until the code is broken.
The longer time it takes to break the solution the more bullet proof your solution is.

2 comments:

Willo van der Merwe said...

Hello,

You can use the openssl set of functions. OpenSSL allows for certificate exchange and full RSA encryption.

Cheers,

Willo

Anonymous said...

some of us aren't so lucky as to have a host that has mcrypt installed. here's a pure-php implementation of des that you should use instead:

http://phpseclib.sourceforge.net/documentation/crypt.html#crypt_des

...or better yet, AES:

http://phpseclib.sourceforge.net/documentation/crypt.html#crypt_aes