I'm trying to calculate KCV for a DES encryption.
$data = 'F337CE3C64E02D96C61A9EC69E051D5A';
$transport = '4B4D3733504D3735';
$encData = bin2hex(mcrypt_encrypt(MCRYPT_DES, pack('H*', $transport),
$data, MCRYPT_MODE_ECB));
This is all fine, data gets properly encrypted.
Now I want to calculate KCV.
$key = 'F337CE3C64E02D96C61A9EC69E051D5A';
$zeroBytes = 00000000;
$kcv = bin2hex(mcrypt_encrypt(MCRYPT_3DES, $key, $zeroBytes, MCRYPT_MODE_ECB));
var_dump($kcv);
this returns wrong value : 953f34d098f996f9 and it should be (the final kcv) 53173F. What goes wrong here?
I see two issues with your code: 1. Key should be binary, 2. Null bytes should be used, not integer zeros.
$key = pack('H*', '0123456789ABCDEF');
$data = "\0\0\0\0\0\0\0\0";
$kcv = strtoupper(bin2hex(mcrypt_encrypt(MCRYPT_3DES, $key, $data, MCRYPT_MODE_ECB)));
var_dump($kcv);
This outputs D5D44FF720683D0D as expected (KCV for key 0123456789ABCDEF is D5D44FF720683D0D).
EDIT: In your case you need to append the first 16 characters to the end of the key to get the correct KCV:
$key = 'F337CE3C64E02D96C61A9EC69E051D5A';
$key .= substr($key, 0, 16);
$key = pack('H*', $key);
$data = "\0\0\0\0\0\0\0\0";
$kcv = strtoupper(bin2hex(mcrypt_encrypt(MCRYPT_3DES, $key, $data, MCRYPT_MODE_ECB)));
var_dump($kcv);
This outputs 53173F8B139F34FE. See Keying options for more details:
The standards define three keying options:
Keying option 1: All three keys are independent.
Keying option 2: K1 and K2 are independent, and K3 = K1.
Keying option 3: All three keys are identical, i.e. K1 = K2 = K3.
I'm no cryptographer, but a quick googling says that a 3DES KCV needs to be calculated on a "string of binary zeroes", and $zeroBytes = 00000000; is not that.
PHP will read that as 0, then type-juggle that into an ASCII zero character, aka 0x30.
I think what you want is:
$zeroBytes = "\0\0\0\0\0\0\0\0";
Which will give you eight NULL bytes, aka 0x00.
Related
I have a php program that stores encrpyted data in the database using openssl_encrypt.
I need to be able to decrypt this data in mysql to perform queries on it.
The problem is that im not sure what the value of my key should be in the AES_DECRYPT function of mysql.
My php code uses a base64 key that decrypts to a non ascii value.
The code base64 encodes the encrypted value and adds the IV at the end, so in mysql i decode and split this. All of that is working fine, but when i go to decrypt it returns null. presumably because i dont know how to put the key in correctly.
Ive tried UNHEX("key") FROM_BASE64("key") etc
function encrypt($data)
{
if (!$data) {
return null;
}
$key = "lvvs9pxoekc54zsBpsp+/H1235BdjsAscdRRAxr47uw="; //this is not my real key
$encryption_key = base64_decode($key);
$hex = bin2hex($encryption_key);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $encryption_key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
In mysql i am doing this
AES_DECRYPT(
FROM_BASE64(SUBSTRING_INDEX(
UPPER(
CAST(
FROM_BASE64(member_ssn)
AS CHAR(1000)
)
), '::', 1))
, '*KEY* what do i need to put here?') as decrpyted_string
I got this working so i will post my solution for those who care.
The code performs the following opperations.
takes a base64 value and decodes it
this results in a value of base64cryptstring::IV
then splits the string at the :: to get the IV and crypt string separated
then it uses these values and a FROM_BASE64 on the key itself to get the decrpyted value
SELECT dec_data.member_id, CONVERT(dec_data.dec_ssn USING LATIN1) as dec_ssn FROM (
select
*,
CAST(
AES_DECRYPT(
FROM_BASE64(SUBSTRING_INDEX(CAST(FROM_BASE64(member_ssn)AS CHAR(1000)), '::', 1)),
FROM_BASE64('yourbase64keyhere=='),
SUBSTRING_INDEX(SUBSTRING_INDEX(FROM_BASE64(member_ssn), '::', 2), '::', -1)
) AS CHAR) as dec_ssn
from lb_1_members where member_ssn is not null AND member_ssn != '') dec_data
I have numerous 15-digit numbers. I need to rearrange the digits in each string based on a set order to hide the composite identifiers. For example: convert 123456789123456 to 223134897616545
The method I am thinking of is:
Extract each digit using $array = str_split($int)
Create a new array with required order from $array above
But here is where I am stuck. How do I combine all digits of array to single integer? Also is this an efficient way to do this?
Also I need to know the order in which it was shuffled so as to retrieve the original number.
You are already half way. You can use join or implode:
$no = 123456789123456;
$no_arr = str_split($no);
shuffle($no_arr);
echo join($no_arr); // implode($no_arr);
I get this randomly:
574146563298123
Resultant is string. You can convert to int by type casting: echo (int) join($no_arr);
another aproach in maybe less lines:
$num = 1234;
$stringOriginal = (string) $num;
// build the new string, apply your algorithm for that as needed
$stringNew = $stringOriginal[1] . $stringOriginal[0] . $stringOriginal[3] . $stringOriginal[2];
$numNew = (int) $stringNew;
warning: this has no error handling, it's easy to mess up the indices and get an error because of that
This appears to be a task of obfuscation (not encryption) -- meaning that you only want to obscure the value or make it slightly harder to guess. The truth is that if you are not changing any of the characters, but merely shuffling them around, then a brute force technique can very easily try every combination of the characters in order to "happen upon" the correct one.
Security aside, use a 15-element array to re-map the characters. How you generate and store this map is up to you. Use the map to revert the shuffled string to its original value.
Code: (Demo)
$input = '123456789123456';
$map = [5, 8, 13, 2, 4, 11, 0, 14, 1, 7, 3, 12, 10, 6, 9];
$result = '';
foreach ($map as $offset) {
$result .= $input[$offset];
}
echo "Rearranged: $result\n---\n";
$reverted = '';
asort($map);
foreach ($map as $offset => $notUsed) {
$reverted .= $result[$offset];
}
echo "Reverted: $reverted";
Output:
Rearranged: 695353162844271
---
Reverted: 123456789123456
Implode the array and cast it into an int.
(int) implode('',$array)
I am using this function to supply a custom key for encrypting a string using CryptoJS
function doHash(msg){
msg = String(msg);
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
var encrypted = CryptoJS.AES.encrypt(msg, key, { iv: iv });
return encrypted;
}
Instead of supplying a passphrase I directly supply an IV and a key to encrypt.
Now I need to decrypt the ciphertext in PHP:
And this is what I found:
function aes_decrypt($encrypted,$key)
{
// if $encrypted is HEXed, then return it to binary
$encrypted = pack('H*',$encrypted);
$key = mysql_aes_key($key);
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$key,$encrypted,MCRYPT_MODE_ECB,''),"\x00..\x1F");
}
This is a direct example of mCrypt's decrypt AES function.
I cannot see anywhere where the IV goes to decrypt this. Am I missing
something, is the IV not needed when decrypting?
It seems pretty clear from the docs:
string mcrypt_decrypt ( string $cipher , string $key , string $data ,
string $mode [, string $iv ] )
So the last argument is where the IV should go. In your current code, you are passing an empty string ('').
I have dilemma. I need unique string in my database table to show it in url (I don't show id in url).
I want generate in php unique string check it is unique in table if not generate new.
What do you think in terms of performance for a string with a length of 8 characters, and with a large number of rows (about 100,000 rows).
You can create a unique id in php using uniqid().
When generating a new ID, check the DB to see if it exists. If it does, create a new one. Assuming you set up an index on that column in the database, these checks will not be very expensive.
In pseudo(-ish) code :
while (true) {
$id = uniqid();
if (!id_exists_in_db($id)) // we have a unique id
break;
}
Instead of generating a unique key besides the id, you can also create a function that hashes or encodes the id to a string. For instance, you can convert the integer to an 8 character hexidecimal representation. You can make it a little more complex if you like, but still, it's fairly easy to write a function that uses a fixed formula to convert a number to 8 letters and vice versa. That way, you don't need to store this extra data, and you don't have to worry about the value being unique.
<?php
function translate($key, $back)
{
$key = str_pad($key, 8, '0', STR_PAD_LEFT);
$a = array('1','2','3','4','5','6','7','8','9','0');
$b = array('a','b','c','d','e','f','g','h','i','j');
if ($back) {$c = $b; $b = $a; $a = $c;}
return str_replace($a, $b, $key);
}
function encode_id($int)
{
return translate($int, false);
}
function decode_id($key)
{
return (int)translate($key, true);
}
$key = encode_id(40000);
$int = decode_id($key);
echo $key;
echo '<br>';
echo $int;
I have this piece of code:
$result = new stdClass();
foreach ($array as $index => $value) {
if(is_numeric($value)){
$int = (int)$value;
$double = (double)$value;
if($int == $double)
$value = $int;
else
$value = $double;
}
$index = strtolower($index);
$result->$index = $value;
}
And it worked for ages. Now I got a problem with it. I have a column in my database that has numbers (big numbers) in it. But they're not numbers, they're varchar and those numbers are not for mathematical purpose. Unfortunately, since the column is fully filled with numbers only, it passes the is_numeric test, but since it's a giant number, it loses data due to memory limitation (4 billions, I think).
Anyway, how can I check if after the cast I lost data or not to my variable?
Thanks.
if($value<=PHP_INT_MAX) ... // safe to convert
else // not safe
Convert it back and see if it gives the same value as the source.