Skip to content

Instantly share code, notes, and snippets.

@autotrof
Last active September 23, 2019 00:36
Show Gist options
  • Save autotrof/daf77b8801164ab2d7d55c437f2446e1 to your computer and use it in GitHub Desktop.
Save autotrof/daf77b8801164ab2d7d55c437f2446e1 to your computer and use it in GitHub Desktop.
AES encrypt-decrypt between php and javascript
//JAVASCRIPT
import CryptoJS from 'crypto-js';
import { Base64 } from 'js-base64';
const key = 'DK7Mf0GD1JR1Z3ROxeh+l/crGYKHVglLsInUN8Q8RiY=';
//function "unserialize" IS COPY FROM http://locutus.io/php/unserialize/
const unserialize = data => {
var $global = typeof window !== 'undefined' ? window : global;
var utf8Overhead = function(str) {
var s = str.length;
for (var i = str.length - 1; i >= 0; i--) {
var code = str.charCodeAt(i);
if (code > 0x7f && code <= 0x7ff) {
s++;
} else if (code > 0x7ff && code <= 0xffff) {
s += 2;
}
if (code >= 0xdc00 && code <= 0xdfff) {
i--;
}
}
return s - 1;
};
var error = function(type, msg, filename, line) {
throw new $global[type](msg, filename, line);
};
var readUntil = function(data, offset, stopchr) {
var i = 2;
var buf = [];
var chr = data.slice(offset, offset + 1);
while (chr !== stopchr) {
if (i + offset > data.length) {
error('Error', 'Invalid');
}
buf.push(chr);
chr = data.slice(offset + (i - 1), offset + i);
i += 1;
}
return [buf.length, buf.join('')];
};
var readChrs = function(data, offset, length) {
var i, chr, buf;
buf = [];
for (i = 0; i < length; i++) {
chr = data.slice(offset + (i - 1), offset + i);
buf.push(chr);
length -= utf8Overhead(chr);
}
return [buf.length, buf.join('')];
};
function _unserialize(data, offset) {
var dtype;
var dataoffset;
var keyandchrs;
var keys;
var contig;
var length;
var array;
var readdata;
var readData;
var ccount;
var stringlength;
var i;
var key;
var kprops;
var kchrs;
var vprops;
var vchrs;
var value;
var chrs = 0;
var typeconvert = function(x) {
return x;
};
if (!offset) {
offset = 0;
}
dtype = data.slice(offset, offset + 1).toLowerCase();
dataoffset = offset + 2;
switch (dtype) {
case 'i':
typeconvert = function(x) {
return parseInt(x, 10);
};
readData = readUntil(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'b':
typeconvert = function(x) {
return parseInt(x, 10) !== 0;
};
readData = readUntil(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'd':
typeconvert = function(x) {
return parseFloat(x);
};
readData = readUntil(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'n':
readdata = null;
break;
case 's':
ccount = readUntil(data, dataoffset, ':');
chrs = ccount[0];
stringlength = ccount[1];
dataoffset += chrs + 2;
readData = readChrs(data, dataoffset + 1, parseInt(stringlength, 10));
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 2;
if (chrs !== parseInt(stringlength, 10) && chrs !== readdata.length) {
error('SyntaxError', 'String length mismatch');
}
break;
case 'a':
readdata = {};
keyandchrs = readUntil(data, dataoffset, ':');
chrs = keyandchrs[0];
keys = keyandchrs[1];
dataoffset += chrs + 2;
length = parseInt(keys, 10);
contig = true;
for (i = 0; i < length; i++) {
kprops = _unserialize(data, dataoffset);
kchrs = kprops[1];
key = kprops[2];
dataoffset += kchrs;
vprops = _unserialize(data, dataoffset);
vchrs = vprops[1];
value = vprops[2];
dataoffset += vchrs;
if (key !== i) {
contig = false;
}
readdata[key] = value;
}
if (contig) {
array = new Array(length);
for (i = 0; i < length; i++) {
array[i] = readdata[i];
}
readdata = array;
}
dataoffset += 1;
break;
default:
error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype);
break;
}
return [dtype, dataoffset - offset, typeconvert(readdata)];
}
return _unserialize(data + '', 0)[2];
};
const dec = encryptedString => {
let encrypted_json = JSON.parse(Base64.decode(encryptedString));
let check1 =
typeof encrypted_json === 'object' &&
encrypted_json.hasOwnProperty('iv') &&
encrypted_json.hasOwnProperty('value') &&
encrypted_json.hasOwnProperty('mac');
if (!check1) throw new Error('payload tidak valid');
let check2 =
CryptoJS.SHA256(
encrypted_json.iv + encrypted_json.value + key
).toString() == encrypted_json.mac;
if (!check2) throw new Error('mac tidak valid');
let decrypted = CryptoJS.AES.decrypt(
encrypted_json.value,
CryptoJS.enc.Base64.parse(key),
{
iv: CryptoJS.enc.Base64.parse(encrypted_json.iv),
}
);
let plainData = decrypted.toString(CryptoJS.enc.Utf8);
let validPlainData;
try {
validPlainData = unserialize(plainData);
} catch (e) {
validPlainData = plainData;
}
return validPlainData;
};
const enc = data => {
data = JSON.stringify(data);
let encrypted = CryptoJS.AES.encrypt(data, CryptoJS.enc.Base64.parse(key), {
iv: CryptoJS.lib.WordArray.random(16),
});
let value = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
let iv = encrypted.iv.toString(CryptoJS.enc.Base64);
let mac = CryptoJS.SHA256(iv + value + key).toString();
let hasil = Base64.encode(JSON.stringify({ iv, value, mac }));
return hasil;
};
module.exports = {
key:key,
dec:dec,
enc:enc,
unserialize:unserialize
};
//PHP
//THIS FUNCTION "is_serialized" COPY CODE FROM https://gist.github.com/cs278/217091
if(!function_exists('is_serialized')){
function is_serialized($value, &$result = null)
{
// Bit of a give away this one
if (!is_string($value))
{
return false;
}
if ($value === 'b:0;')
{
$result = false;
return true;
}
$length = strlen($value);
$end = '';
if(isset($value[0])){
switch ($value[0])
{
case 's':
if ($value[$length - 2] !== '"')
{
return false;
}
case 'b':
case 'i':
case 'd':
// This looks odd but it is quicker than isset()ing
$end .= ';';
case 'a':
case 'O':
$end .= '}';
if ($value[1] !== ':')
{
return false;
}
switch ($value[2])
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
break;
default:
return false;
}
case 'N':
$end .= ';';
if ($value[$length - 1] !== $end[0])
{
return false;
}
break;
default:
return false;
}
}
if (($result = @unserialize($value)) === false)
{
$result = null;
return false;
}
return true;
}
}
//ENCRYPT
if(! function_exists('enc')){
function enc($plainData,$serializing = true,$secrete_key=null){
if($secrete_key===null) $secrete_key = substr(env('APP_KEY'),7);//USE YOUR OWN ENV IF THERE IS NO KEY
$key = base64_decode($secrete_key);
$length = mb_strlen($key, '8bit');
$cipher = $length===16?'AES-128-CBC':'AES-256-CBC';
$iv = base64_encode(random_bytes(openssl_cipher_iv_length($cipher)));
if(is_array($plainData)) $serializing = true;
if($serializing){
$data = serialize($plainData);
}else{
$data = $plainData;
}
$value = openssl_encrypt($data, $cipher, $key, 0, base64_decode($iv));
if ($value === false) throw new Exception('Tidak dapat mengenkripsi data');
$mac = hash('sha256', $iv.$value.$secrete_key);
$json = json_encode(['iv'=>$iv,'value'=>$value,'mac'=>$mac]);
if (json_last_error() !== JSON_ERROR_NONE) throw new Exception('Tidak dapat mengenkripsi data', 1);
return base64_encode($json);
}
}
//DECRYPT
if(! function_exists('dec')){
function dec($encryptedData,$secrete_key=null){
if($secrete_key===null) $secrete_key = substr(env('APP_KEY'),7);//USE YOUR OWN ENV IF THERE IS NO KEY
$key = base64_decode($secrete_key);
$length = mb_strlen($key, '8bit');
$payload = (array)json_decode(base64_decode($encryptedData));
$cipher = $length===16?'AES-128-CBC':'AES-256-CBC';
$validPayload = is_array($payload) && isset($payload['iv'], $payload['value'], $payload['mac']) &&
strlen(base64_decode($payload['iv'], true)) === openssl_cipher_iv_length($cipher);
if(!$validPayload) throw new Exception("Payload tidak valid", 1);
$validMac = hash('sha256', $payload['iv'].$payload['value'].$secrete_key)===$payload['mac'];
if(!$validMac) throw new Exception("Mac tidak valid", 1);
$iv = base64_decode($payload['iv']);
$decrypted = openssl_decrypt($payload['value'], $cipher, $key, 0, $iv);
if ($decrypted === false) throw new Exception("Tidak dapat membaca data", 1);
$plainData = is_serialized($decrypted)?unserialize($decrypted):$decrypted;
$realPlainData = json_decode($plainData,true);
if (json_last_error() !== JSON_ERROR_NONE){
return $plainData;
}else{
return $realPlainData;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment