Please do not mark it as duplicate. I have already gone through almost all the previous post for same issue, but was not able to tackle my error.
I am trying to upgrade the Sagepay Protocol from v2.23 to v3.00
I have also deployed PHP kit provided by Sagepay and getting the same error.
Below is the AES encryption I am using to make it compatible for v3.00 in my includes.php
Any guidance would be much appreciated.
<?
/* Base 64 Encoding function **
** PHP does it natively but just for consistency and ease of maintenance, let's declare our own function **/
function base64Encode($plain) {
// Initialise output variable
$output = "";
// Do encoding
$output = base64_encode($plain);
// Return the result
return $output;
}
/* Base 64 decoding function **
** PHP does it natively but just for consistency and ease of maintenance, let's declare our own function **/
function base64Decode($scrambled) {
// Initialise output variable
$output = "";
// Fix plus to space conversion issue
$scrambled = str_replace(" ","+",$scrambled);
// Do encoding
$output = base64_decode($scrambled);
// Return the result
return $output;
}
/* The SimpleXor encryption algorithm **
** NOTE: This is a placeholder really. Future releases of Form will use AES or TwoFish. Proper encryption **
** This simple function and the Base64 will deter script kiddies and prevent the "View Source" type tampering **
** It won't stop a half decent hacker though, but the most they could do is change the amount field to something **
** else, so provided the vendor checks the reports and compares amounts, there is no harm done. It's still **
** more secure than the other PSPs who don't both encrypting their forms at all */
function simpleXor($InString, $Key) {
// Initialise key array
$KeyList = array();
// Initialise out variable
$output = "";
// Convert $Key into array of ASCII values
for($i = 0; $i < strlen($Key); $i++){
$KeyList[$i] = ord(substr($Key, $i, 1));
}
// Step through string a character at a time
for($i = 0; $i < strlen($InString); $i++) {
// Get ASCII code from string, get ASCII code from key (loop through with MOD), XOR the two, get the character from the result
// % is MOD (modulus), ^ is XOR
$output.= chr(ord(substr($InString, $i, 1)) ^ ($KeyList[$i % strlen($Key)]));
}
// Return the result
return $output;
}
//** Wrapper function do encrypt an encode based on strEncryptionType setting **
function encryptAndEncode($strPost) {
global $strEncryptionType
,$strEncryptionPassword;
if ($strEncryptionType=="XOR")
{
//** XOR encryption with Base64 encoding **
return base64Encode(simpleXor($strPost,$strEncryptionPassword));
}
else
{
//** AES encryption, CBC blocking with PKCS5 padding then HEX encoding - DEFAULT **
//** use initialization vector (IV) set from $strEncryptionPassword
$strIV = $strEncryptionPassword;
//** add PKCS5 padding to the text to be encypted
$strPost = addPKCS5Padding($strPost);
//** perform encryption with PHP's MCRYPT module
$strCrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $strEncryptionPassword, $strPost, MCRYPT_MODE_CBC, $strIV);
//** perform hex encoding and return
return "#" . bin2hex($strCrypt);
}
}
//** Wrapper function do decode then decrypt based on header of the encrypted field **
function decodeAndDecrypt($strPost) {
global $strEncryptionPassword;
if (substr($strPost,0,1)=="#")
{
//** HEX decoding then AES decryption, CBC blocking with PKCS5 padding - DEFAULT **
//** use initialization vector (IV) set from $strEncryptionPassword
$strIV = $strEncryptionPassword;
//** remove the first char which is # to flag this is AES encrypted
$strPost = substr($strPost,1);
//** HEX decoding
$strPost = pack('H*', $strPost);
//** perform decryption with PHP's MCRYPT module
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $strEncryptionPassword, $strPost, MCRYPT_MODE_CBC, $strIV);
}
else
{
//** Base 64 decoding plus XOR decryption **
return simpleXor(base64Decode($strPost),$strEncryptionPassword);
}
}
//** PHP's mcrypt does not have built in PKCS5 Padding, so we use this
function addPKCS5Padding($input)
{
$blocksize = 16;
$padding = "";
// Pad input to an even block size boundary
$padlength = $blocksize - (strlen($input) % $blocksize);
for($i = 1; $i <= $padlength; $i++) {
$padding .= chr($padlength);
}
return $input . $padding;
}
/*************
function pkcs5_pad($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
//echo "<br/>Padding:".str_repeat(chr($pad), $pad)."<";
return $text . str_repeat(chr($pad), $pad);
}
function encryptFieldData($input)
{
$key = "[mykey]";
$iv = $key;
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, "", MCRYPT_MODE_CBC, "");
if (mcrypt_generic_init($cipher, $key, $iv) != -1)
{
$cipherText = mcrypt_generic($cipher,$input );
mcrypt_generic_deinit($cipher);
$enc = bin2hex($cipherText);
}
return $enc;
}
$str = "Currency=GBP";
$datapadded = pkcs5_pad($str,16);
$cryptpadded = "#" . encryptFieldData($datapadded);
*************************/
?>
The problem was with my server mcrypt PHP extension. It wasn't install/enable on the server. I must have consider this before making all test attempts.
Thank you all for the efforts towards my issue.
Have you checked your encryption password is correct? 3045 is the first error to be thrown if the password is wrong. This is not so nuts as it sounds - encryption passwords are different between test and live.....
Related
I have encrypted mutiple strings one-by-one in using crypto-js in react.'
For encryption I used -
encryptAES = (text, key) => {
return CryptoJS.AES.encrypt(text, key).toString();
};
For decryption, I used function like following -
decryptAES = (encryptedBase64, key) => {
const decrypted = CryptoJS.AES.decrypt(encryptedBase64, key);
if (decrypted) {
try {
console.log(decrypted);
const str = decrypted.toString(CryptoJS.enc.Utf8);
if (str.length > 0) {
return str;
} else {
return 'error 1';
}
} catch (e) {
return 'error 2';
}
}
return 'error 3';
};
I have uploaded a working sample project of this encryption - decryption here.
For e.g., if I encrypt "I live in India" using key - "earth", it would output as - "U2FsdGVkX1+cBvU9yH5fIGVmliJYPXsv4AIosUGH4tA=", and similary it would decrypt successfully with the correct key.
Now I have multiple encrypted strings stored in my database, but now require them to store un-encrypted, so I wanted to decrypt them in PHP. I can decrypt them in js using the function mentioned above but I am unable to figure out how to do so in PHP. I have tried this github repository but I couldn't customize it for my use case.
For decryption, salt and ciphertext must first be determined. To do this, the encrypted data of the CryptoJS code must be Base64 decoded. The salt are the second 8 bytes of the Base64 decoded data, followed by the actual ciphertext (the first 8 bytes are the ASCII encoding of Salted__ and can be ignored).
After determining the salt, key and IV are to be derived with EVP_BytesToKey(). You can find various PHP implementations on the web, e.g. here. Note that CryptoJS uses MD5 as digest, so the digest in the linked code must be modified accordingly.
Once key and IV have been determined, the actual ciphertext can be decrypted.
All together:
<?php
// Separate salt and actual ciphertext
$ctOpenSSL = base64_decode("U2FsdGVkX1+cBvU9yH5fIGVmliJYPXsv4AIosUGH4tA=");
$salt = substr($ctOpenSSL, 8, 8);
$ciphertext = substr($ctOpenSSL, 16);
// Derive key and IV
$keyIv = EVP_BytesToKey($salt, "earth");
$key = substr($keyIv, 0, 32);
$iv = substr($keyIv, 32, 16);
// Decrypt
$decrypted = openssl_decrypt($ciphertext, "aes-256-cbc", $key, OPENSSL_RAW_DATA, $iv);
print($decrypted . PHP_EOL); // I live in India
function EVP_BytesToKey($salt, $password) {
$bytes = '';
$last = '';
while(strlen($bytes) < 48) {
$last = hash('md5', $last . $password . $salt, true);
$bytes.= $last;
}
return $bytes;
}
?>
I am looking to integrate with Sagepay/Opayo Form using their guidance here:
https://www.sagepay.co.uk/file/25041/download-document/FORM_Integration_and_Protocol_Guidelines_270815.pdf?token=Cfj49hcaD4kpE0zk7179ZLOaQx2RH_3oatPOrAV6MyM
Sagepay have now stopped supporting SDKs which means I've had to find a 2013 utility set from here:
https://github.com/ammaar23/sagepay-sdk-php/blob/master/lib/classes/util.php
I am trying to migrate away from mcrypt towards openssl_encrypt but cannot seem to replicate the desired result.
There are 2 functions in play:
static protected function addPKCS5Padding($input)
{
$blockSize = 16;
$padd = "";
// Pad input to an even block size boundary.
$length = $blockSize - (strlen($input) % $blockSize);
for ($i = 1; $i <= $length; $i++)
{
$padd .= chr($length);
}
return $input . $padd;
}
static public function encryptAes($string, $key)
{
// AES encryption, CBC blocking with PKCS5 padding then HEX encoding.
// Add PKCS5 padding to the text to be encypted.
$string = self::addPKCS5Padding($string);
// AH updated as mcrypt is now deprecated! 2020
$cipher = 'AES-128-CBC';
$ivsize = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivsize);
$crypt = openssl_encrypt($string,$cipher,$key,0,$iv);
// Perform hex encoding and return.
return "#" . strtoupper(bin2hex($crypt));
}
You can see my attempts to implement openssl_encrypt but it isn't working. The key is 55a51621a6648525 and the input string is VendorTxCode=TxCode-1310917599-223087284&Amount=36.95&Currency=GBP&Description=description&CustomerName=Fname Surname&CustomerEMail=customer#example.com&BillingSurname=Surname&BillingFirstnames=Fname&BillingAddress1=BillAddress Line 1&BillingCity=BillCity&BillingPostCode=W1A 1BL&BillingCountry=GB&BillingPhone=447933000000&DeliveryFirstnames=Fname&DeliverySurname=Surname&DeliveryAddress1=BillAddress Line 1&DeliveryCity=BillCity&DeliveryPostCode=W1A 1BL&DeliveryCountry=GB&DeliveryPhone=447933000000&SuccessURL=https://example.com/success&FailureURL=https://example.com/failure
and the output result is:
#2DCD27338114D4C39A14A855702FBAB2EF40BCAC2D76A3ABC0F660A07E9C1C921C2C755BA9B59C39F882FBF6DFED114F23141D94E50A01A665B1E31A86C07CA1CD1BB8EF5B6CF2C23D495CD 79F9C0F678D61773E7A1AA30AA5B23D56503FC0B52AC0694A8C341263D2C5FE1BAD93BDB94726761E155E900448F644AF1F67BE1AC77E852B9D90809A44F258EE9478B6D8C1C4ED58759263E7DBF 8871C6592287C0358F36F4EEC326CEDDD440DA2FED8AB35F1B630A5C6FA671E4D78CC8CACECF9DFDC31D6C5EC8270FB21E297E2C2E14F99A04223EFFD4F00062D440E78A3D2C7140EC8F123D24 7B75E7482AE98858DA34D37EDE6D7C69AA74391F559305CF675ADB3615244A107ABBB6AF26E29A2FFA059B12688D90FE09E0DE069325BFF3587A695F5DA36E4B809B69CC9A37034F166B63B5A62 B986F4DA34E9AC9516AFDE70642EC7DAD1AEBA93A1F347D6AC7046E967DCBFE7ACFCEE5DAFC0B29F1765032B3060EBE565CBD57D092075D15CF12725199C6881605B2E0F105698CE3ADD04361C A9D620C187B90E3F9849445B5C3C0FDF1768BFFD61F97E51316826F4F10E0E3E668F0A9F5ED9CCDA6F2C7CC957F12DB48F9041482E3D035E7A91852C404BFA325FED947E71F57B871DFAC6AF4FF2 9F4513A4A80B2D7ECC9D19D47ED04FA99CDFC881DFA771E1EA4F3F9B2C5AC673EF3DA2699A309CC8522993A63CB8D45D3CDF09B1DFDC573CD19679B250AD6721450B5042F201670B464505DCAE F59E2C67ABACC9AE2EEE793CE191FEBF66B8FAF4204EFFB359246B9C99FB52805C46375FF35140F74707FBC73C7731A28A2C883A
How can I create the desired output from my input code because the output I generate starts with: 403444324634333730363535313636373136413432363737353332363133303644373736
openssl_encrypt applies PKCS7 padding by default, so the addPKCS5Padding method is no longer needed.
In the mcrypt_encrypt code iv = key is used, so you must not generate a random IV for code equivalence.
In openssl_encrypt the flag OPENSSL_RAW_DATA must be applied as 4th parameter, otherwise the data will be returned Base64 encoded.
With these changes, the openssl_encrypt code produces the same result as the mcrypt_encrypt code:
<?php
function encryptAes($string, $key)
{
//$string = self::addPKCS5Padding($string); // don't pad explicitly
$cipher = 'AES-128-CBC';
//$ivsize = openssl_cipher_iv_length($cipher);
//$iv = openssl_random_pseudo_bytes($ivsize); // mcrypt_encrypt code: iv = key
$crypt = openssl_encrypt($string,$cipher,$key,OPENSSL_RAW_DATA,$key); // use raw data
return "#" . strtoupper(bin2hex($crypt));
}
$plain = "VendorTxCode=TxCode-1310917599-223087284&Amount=36.95&Currency=GBP&Description=description&CustomerName=Fname Surname&CustomerEMail=customer#example.com&BillingSurname=Surname&BillingFirstnames=Fname&BillingAddress1=BillAddress Line 1&BillingCity=BillCity&BillingPostCode=W1A 1BL&BillingCountry=GB&BillingPhone=447933000000&DeliveryFirstnames=Fname&DeliverySurname=Surname&DeliveryAddress1=BillAddress Line 1&DeliveryCity=BillCity&DeliveryPostCode=W1A 1BL&DeliveryCountry=GB&DeliveryPhone=447933000000&SuccessURL=https://example.com/success&FailureURL=https://example.com/failure";
$key = "55a51621a6648525";
print(encryptAes($plain, $key));
?>
For the choice iv = key note also the answers to this question.
#Topaco answered beautifully. Here is an additional bit, ( not related to the question ), while doing Sagepay integration you will need the decrypt function too for verifying payment/callback. Here is the function you can use.
function decrypytAES($string, $key) {
$cipher = 'AES-128-CBC';
$strIn = hex2bin(substr($strIn, 1));
return openssl_decrypt($strIn, $cipher, $key, OPENSSL_RAW_DATA, $key);
}
Im attempting to integrate the SagePay payment gateway into a website using the 'Form Integration' method.
Basically the form integration method works by inserting a FORM in a webpage and POSTing information to SagePay's servers whenever the Submit button for the FORM is selected. Before the information can be sent to SagePay's servers it must be encrypted using the AES/CBC/PKCS#5 algorithm, before being Base 64 encoded.
I have a basic knowledge of encryption but I have no experience of using it in PHP. Can anyone please help me formulate the AES/CBC/PKCS#5 algorithm in PHP please?
Here's my efforts so far:
$CRYPT = "Text Goes Here";
$blocksize = 16;//Does 16 Refer To 16 Bytes Or 16 Bits? 16 Bytes = 128 Bits.
$cryptLength = strlen($CRYPT);
$cryptLengthModuloBlocksize = $cryptLength % $blocksize;
$pad = $blocksize - $cryptLengthModuloBlocksize;
$padString = str_repeat(chr($pad), $pad);
$CRYPT = $CRYPT . $padString;
$encryptionPassword = 'password';
$Encrypted_CRYPT = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $encryptionPassword, $CRYPT, MCRYPT_MODE_CBC);
$Base64_Encrypted_CRYPT = base64_encode($Encrypted_CRYPT);
echo "<form action = 'https://test.sagepay.com/gateway/service/vspform-register.vsp' method = 'POST'>
<input type = 'hidden' name = 'VPSProtocol' value = '3.00' />//
<input type = 'hidden' name = 'TxType' value = 'PAYMENT' />
<input type = 'hidden' name = 'Vendor' value = 'vendorName' />
<input type = 'hidden' name = 'Crypt' value = '" . $Base64_Encrypted_CRYPT . "' />
<input type= 'submit' value = 'Submit'>
</form>";
Any help is much appreciated.
Here is a working implementation of AES/CBC/PKCS#5 working with Sagepay's form integration
You will need mcrypt installed. sp_encryption is a define for the encryption key.
/**
* #param string $data - the key=value pairs separated with &
* #return string
*/
protected function encode_data($data) {
$strIn = $this->pkcs5_pad($data);
$strCrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, sp_encryption, $strIn, MCRYPT_MODE_CBC, sp_encryption);
return "#" . bin2hex($strCrypt);
}
/**
* #param string $data - crypt response from Sagepay
* #return string
*/
protected function decode_data($data) {
if (substr($data, 0, 1) == "#") {
$strIn = hex2bin(substr($data, 1));
return $this->pkcs5_unpad(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, sp_encryption, $strIn, MCRYPT_MODE_CBC, sp_encryption));
}
return '';
}
protected function pkcs5_pad($text) {
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = $size - (strlen($text) % $size);
return $text . str_repeat(chr($pad), $pad);
}
protected function pkcs5_unpad($text) {
$pad = ord($text{strlen($text) - 1});
if ($pad > strlen($text)) return false;
if (strspn($text, $text{strlen($text) - 1}, strlen($text) - $pad) != $pad) {
return false;
}
return substr($text, 0, -1 * $pad);
}
On a side note make sure the VPSProtocol you use is '3.00' not 3, and not 3.0 as these won't work!
Hoep this helps and sagepay get some official PHP docs out soon.
Yes, that's fine. (As soon as we have a PHP kit for v3.00 we'll display it on our website).
Hope the below helps you to.
Example:
AES encryption with Base64 encoding
AES encryption, CBC blocking with PKCS5 padding then HEX encoding
//** Wrapper function do encrypt an encode based on strEncryptionType setting **
function encryptAndEncode($strIn) {
global $strEncryptionType
,$strEncryptionPassword;
{
//** AES encryption, CBC blocking with PKCS5 padding then HEX encoding **
//** use initialization vector (IV) set from $strEncryptionPassword
$strIV = $strEncryptionPassword;
//** add PKCS5 padding to the text to be encypted
$strIn = addPKCS5Padding($strIn);
//** perform encryption with PHP's MCRYPT module
$strCrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $strEncryptionPassword, $strIn, MCRYPT_MODE_CBC, $strIV);
//** perform hex encoding and return
return "#" . bin2hex($strCrypt);
}
}
//** Wrapper function do decode then decrypt based on header of the encrypted field **
function decodeAndDecrypt($strIn) {
global $strEncryptionPassword;
if (substr($strIn,0,1)=="#")
{
//** HEX decoding then AES decryption, CBC blocking with PKCS5 padding **
//** use initialization vector (IV) set from $strEncryptionPassword
$strIV = $strEncryptionPassword;
//** remove the first char which is # to flag this is AES encrypted
$strIn = substr($strIn,1);
//** HEX decoding
$strIn = pack('H*', $strIn);
//** perform decryption with PHP's MCRYPT module
return removePKCS5Padding(
mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $strEncryptionPassword, $strIn, MCRYPT_MODE_CBC, $strIV));
}
}
Are you using PKCS5 padding then HEX encoding as our older PHP kits used this.
You may want to have a look at a previous post to compare what you are sending. It is using an older protocol, v2.23 and XOR. If you are using our new protocol you will be using v3.00 and AES.
If you want to email feedback#sagepay.com we can discuss further. Please quote this forum post URL.
Sage Pay Support
You can see the working v3 example here: https://github.com/tolzhabayev/sagepayForm-php
I 'm stuck. It seems that AES encryption done by PHP cannot be decrypted in windows.
PHP code:
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128,"12345678", "test", MCRYPT_MODE_CBC));
Windows Code:
"s" has the string which is created by the above response after converting back from base64.
bool Decrypt(char* s,char* key,char* dest)
{
// Create the crypto provider context.
HCRYPTPROV hProvider = NULL;
if (!CryptAcquireContext(&hProvider,
NULL, // pszContainer = no named container
MS_ENH_RSA_AES_PROV, // pszProvider = default provider
PROV_RSA_AES,
0))
return false;
// Construct the blob necessary for the key generation.
aes128keyBlob aes_blob128;
aes_blob128.header.bType = PLAINTEXTKEYBLOB;
aes_blob128.header.bVersion = CUR_BLOB_VERSION;
aes_blob128.header.reserved = 0;
aes_blob128.header.aiKeyAlg = CALG_AES_128;
aes_blob128.keySize = 16;
memcpy(aes_blob128.bytes, key, 16);
HCRYPTKEY hKey = NULL;
if (!CryptImportKey(hProvider,
(BYTE*)(&aes_blob128),
sizeof(aes_blob128),
NULL, //
0, //
&hKey)) {
...
}
// Set Mode
DWORD dwMode = CRYPT_MODE_CBC;
CryptSetKeyParam( hKey, KP_MODE, (BYTE*)&dwMode, 0 );
DWORD length = 16;
BOOL X = CryptDecrypt(hKey,
NULL, // hHash = no hash
TRUE, // Final
0,
(BYTE*)s,
&length);
//int le = GetLastError();
memcpy(dest,s,16);
CryptDestroyKey(hKey);
CryptReleaseContext(hProvider, 0);
}
What could be wrong?
The information you provided is not enough to say for certain but I think that your problem is the key length.
In PHP code you pass "12345678" as a key. And AES128 has a key length of 128bit or 16 bytes.
PHP pads the remaining with zero bytes, as stated in the documentation on mcrypt_encrypt.
In the C++ code you pass only the pointer to your key buffer to Decrypt function. But then you copy 16 bytes from it to the key BLOB:
aes_blob128.keySize = 16;
memcpy(aes_blob128.bytes, key, 16);
Then if you call your function like:
char dest[16];
bool result = Decrypt(string_from_php,"12345678",dest);
than the 8 bytes that happen to reside in memory after the "12345678" constant will be copied to the key blob and passed to CryptImportKey as an actual key. Thus the key in C and in PHP code would be actually different and the decryption will fail due to padding error.
PHP MCRYPT functions are a bit different than the windows decryption functions.
Because the mcrypt function takes the key of any length and converts it to the length required by the algorithm by adding \0 at the end of key string.
Please note to create a key with the specified length required for the encryption by the algorithm on both sides.
use md5 on the key and then convert it to the length required for the algorithm.
See below URL
Encrypt in PHP, Decrypt in C# (WP7 / Silverlight) using AES / Rijndael
http://pumka.net/2009/12/16/rsa-encryption-cplusplus-delphi-cryptoapi-php-openssl-2/
http://www.developer.nokia.com/Community/Wiki/Encrypt-Decrypt_contacts_database_entries_using_Symbian_C%2B%2B
Read it
I droped the MD5 crap out of PHP and C#, and they are now working properly.
Just in case you dropped here looking for the same answer, here is a sample code. Don't forget to make your own key and iv (although those bellow will work, is not recommended to use!)
PHP:
function encrypt128($message) {
$vector = "0000000000000000";
$key = "00000000000000000000000000000000";
$block = mcrypt_get_block_size('rijndael_128', 'cbc');
$pad = $block - (strlen($message) % $block);
$message .= str_repeat(chr($pad), $pad);
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
mcrypt_generic_init($cipher, $key, $vector);
$result = mcrypt_generic($cipher, $message);
mcrypt_generic_deinit($cipher);
return base64_encode($result);
}
C++
Encrypt-Decrypt contacts database entries using Symbian C++
http://www.developer.nokia.com/Community/Wiki/Encrypt-Decrypt_contacts_database_entries_using_Symbian_C%2B%2B
Headers Required:
#include <cntdb.h> // CContactDatabse,
#include <cntitem.h> //CContactItem,CContactItemFieldSet
http://www.developer.nokia.com/Community/Wiki/Encrypt-Decrypt_contacts_database_entries_using_Symbian_C%2B%2B
Encrypt Contact Fields
void CEncryptContactContainer::EncryptAll()
{
CContactDatabase *contactDB = CContactDatabase::OpenL();
CleanupStack::PushL(contactDB);
TContactIter iter(*contactDB);
TContactItemId aContactId;
//Developer can take Heap based descriptor for large/unknown size of contact items.
TBuf16<70> aValue;
const CContactIdArray* contactArray = contactDB->SortedItemsL();
TInt cnt=contactArray->Count();
for(TInt i=0;i<cnt;i++)
{
CContactItem* contactItem=NULL;
contactItem= contactDB->OpenContactL((*contactArray)[i]);
CleanupStack::PushL(contactItem);
CContactItemFieldSet& fieldSet= contactItem->CardFields();
TInt fieldCount=fieldSet.Count(); // This will give number of contact fields.
for(TInt index=0; index < fieldCount; index++)
{
CContactItemField& field = fieldSet[index];
const CContentType& type = field.ContentType();
if(!(type.ContainsFieldType(KUidContactFieldBirthday)))
{
TPtrC name = contactItem->CardFields()[index].TextStorage()->Text();
aValue.Copy(name);
Encrypt(aValue); // Call real encyption here
contactItem->CardFields()[index].TextStorage()->SetTextL(aValue);
}
} //Inner for loop ends here
contactDB->CommitContactL(*contactItem);
CleanupStack::PopAndDestroy(contactItem);
} //Outer for loop ends here
CleanupStack::PopAndDestroy(contactDB);
}
void CEncryptContactContainer:: Encrypt (TDes& aValue)
{
for(TInt iCount=0; iCount< aValue.Length();iCount++)
{
aValue[iCount]+=3;
}
}
Decrypt Contact Fields
void CEncryptContactContainer::DecryptAll()
{
CContactDatabase *contactDB = CContactDatabase::OpenL();
CleanupStack::PushL(contactDB);
TContactIter iter(*contactDB);
TContactItemId aContactId;
TBuf16<70> aValue;
const CContactIdArray* contactArray = contactDB->SortedItemsL();
TInt cnt=contactArray->Count();
for(TInt i=0;i<cnt;i++)
{
CContactItem* contactItem=NULL;
contactItem= contactDB->OpenContactL((*contactArray)[i]);
CleanupStack::PushL(contactItem);
CContactItemFieldSet& fieldSet= contactItem->CardFields();
TInt fieldCount=fieldSet.Count(); // This will give number of contact fields.
for(TInt index=0; index < fieldCount; index++)
{
CContactItemField& field = fieldSet[index];
const CContentType& type = field.ContentType();
if(!(type.ContainsFieldType(KUidContactFieldBirthday)))
{
TPtrC name = contactItem->CardFields()[index].TextStorage()->Text();
aValue.Copy(name);
Decrypt(aValue);
contactItem->CardFields()[index].TextStorage()->SetTextL(aValue);
}
} //Inner for loop ends here
contactDB->CommitContactL(*contactItem);
CleanupStack::PopAndDestroy(contactItem);
} //Outer for loop ends here
CleanupStack::PopAndDestroy(contactDB);
}
void CEncryptContactContainer:: Decrypt (TDes& aValue)
{
for(TInt iCount=0; iCount< aValue.Length();iCount++)
{
aValue[iCount]-=3;
}
}
C#:
byte[] cripted = EncryptStringToBytes("Test", System.Text.Encoding.UTF8.GetBytes("00000000000000000000000000000000"), System.Text.Encoding.UTF8.GetBytes("0000000000000000"));
Openfire stores encrypted passwords in a database using blowfish encryption.
http://svn.igniterealtime.org/svn/repos/openfire/trunk/src/java/org/jivesoftware/util/Blowfish.java is the java implementation for how encrypt / decrypt functions work in openfire.
My goal is to create new user entries in the database via PHP and MySQLI. All of the variations I've tried have yielded results that don't match what already exists in the database. For example:
d3f499857b40ac45c41828ccaa5ee1f90b19ca4e0560d1e2dcf4a305f219a4a2342aa7364e9950db is one of the encrypted passwords. clear text, this is stackoverflow
I've tried a few variations:
echo mcrypt_cbc(MCRYPT_BLOWFISH, '1uY40SR771HkdDG', 'stackoverflow', MCRYPT_ENCRYPT, '12345678');
// result: áë*sY¶nŸÉX_33ô
Another based on mcrypt blowfish php slightly different results when compared to java and .net
$key = '1uY40SR771HkdDG';
$pass = 'stackoverflow';
$blocksize = mcrypt_get_block_size('blowfish', 'cbc'); // get block size
$pkcs = $blocksize - (strlen($data) % $blocksize); // get pkcs5 pad length
$data.= str_repeat(chr($pkcs), $pkcs); // append pkcs5 padding to the data
// encrypt and encode
$res = base64_encode(mcrypt_cbc(MCRYPT_BLOWFISH,$key, $pass, MCRYPT_ENCRYPT));
echo $res;
// result: 3WXKASjk35sI1+XJ7htOGw==
Any clever ideas, or any glaring problems? I simply want to implement Blowfish.encryptString() as referenced in the first link in this question.
Here's a class I made, it encrypts and decrypts properly.
Note, you need to save / [pre/app]end the IV in order to reproduce results.
Some test vectors for the java code would be nice.
<?php
/**
* Emulate OpenFire Blowfish Class
*/
class OpenFireBlowfish
{
private $key;
private $cipher;
function __construct($pass)
{
$this->cipher = mcrypt_module_open('blowfish','','cbc','');
$this->key = pack('H*',sha1($pass));
}
function encryptString($plaintext, $iv = '')
{
if ($iv == '') {
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->cipher));
}
else {
$iv = pack("H*", $iv);
}
mcrypt_generic_init($this->cipher, $this->key, $iv);
$bs = mcrypt_enc_get_block_size($this->cipher); // get block size
$plaintext = mb_convert_encoding($plaintext,'UTF-16BE'); // set to 2 byte, network order
$pkcs = $bs - (strlen($plaintext) % $bs); // get pkcs5 pad length
$pkcs = str_repeat(chr($pkcs), $pkcs); // create padding string
$plaintext = $plaintext.$pkcs; // append pkcs5 padding to the data
$result = mcrypt_generic($this->cipher, $plaintext);
mcrypt_generic_deinit($this->cipher);
return $iv.$result;
}
function decryptString($ciphertext)
{
$bs = mcrypt_enc_get_block_size($this->cipher); // get block size
$iv_size = mcrypt_enc_get_iv_size($this->cipher);
if ((strlen($ciphertext) % $bs) != 0) { // check string is proper size
return false;
}
$iv = substr($ciphertext, 0, $iv_size); // retrieve IV
$ciphertext = substr($ciphertext, $iv_size);
mcrypt_generic_init($this->cipher, $this->key, $iv);
$result = mdecrypt_generic($this->cipher, $ciphertext); // decrypt
$padding = ord(substr($result,-1)); // retrieve padding
$result = substr($result,0,$padding * -1); // and remove it
mcrypt_generic_deinit($this->cipher);
return $result;
}
function __destruct()
{
mcrypt_module_close($this->cipher);
}
}
$enckey = "1uY40SR771HkdDG";
$enciv = 'd3f499857b40ac45';
$javastring = 'd3f499857b40ac45c41828ccaa5ee1f90b19ca4e0560d1e2dcf4a305f219a4a2342aa7364e9950db';
$a = new OpenFireBlowfish($enckey);
$encstring = bin2hex($a->encryptString('stackoverflow',$enciv));
echo $encstring . "\n";
echo $a->decryptString(pack("H*", $encstring)) . "\n";
$b = new OpenFireBlowfish($enckey);
echo $b->decryptString(pack("H*", $javastring)) . "\n";
There is nothing wrong with your code, however to generate the same code as Openfire, you will need to add in two other items before the encrypted text.
length of ciphertext
CBCIV (initialization variable)
Read "public String decryptString(String sCipherText)" in java code, it's all there. Also check the docs on how to use CBCIV in PHP.
Openfire's code prepends the CBCIV passed with the output string. It also using Unicode as the character set. These together may be the problem area.
I don't know enough about Blowfish's internals to help more, sorry.