ABAP string to SHA256 different to SAPUI5/Javascript - javascript

Currently on my SAPUI5 project, I am creating a HMAC encoded string with this line of code:
var secretKey = CryptoJS.enc.Hex.parse('SECRETKEY'); //USING THE CRYPTOJS LIBRARY!
var hash = CryptoJS.HmacSHA256('abc', secretKey);
hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
which gives eZdbNMwgWKOANEiozokNG2FGfzI7Yy/B8IQKXr3+krY=
I am using the CryptoJS library to execute this code in UI5.
However the problem is that I am receiving the wrong HMAC encoded string when I want to do the same in ABAP. After testing a few times, it seems like the encoding (in abap) is wrong before the HMAC is calculated.
Is there a function module that does 'CryptoJS.enc.Hex.parse()' - after googling what it does it interprets the parameter as encoded and converts it into a word array:
DATA:
lv_sign_key_x TYPE xstring,
lv_hmac_result TYPE string.
DATA(lv_binary_secret) = cl_abap_hmac=>string_to_xstring('SECRETKEY').
cl_abap_hmac=>calculate_hmac_for_char(
EXPORTING
if_algorithm = 'SHA256' "Hash Algorithm
if_key = lv_binary_secret "HMAC Key
if_data = 'abc' "Data
IMPORTING
ef_hmacb64string = lv_hmac_result "HMAC value as base64-encoded string
).
which gives 9dyEZn5G+uiRwsNqgY5S6k9/gmCheFNF4vFa5qBKK1w=

Shows exact way to encode to hex and calculate to HMAC256. Link
data lv_binary_secret type xstring.
data lv_string type string value '48656c6c6f2c20576f726c6421'.
translate lv_string to upper case.
lv_binary_secret = lv_string.
cl_abap_hmac=>calculate_hmac_for_char(
EXPORTING
if_algorithm = 'SHA256' "Hash Algorithm
if_key = lv_binary_secret "HMAC Key
if_data = 'abc' "Data
IMPORTING
ef_hmacb64string = data(lv_hmac_result) "HMAC value as base64-encoded string
).`enter code here`

Related

Replicate BTOA Javascript Function in SAS Data Step

When I encrypt the string g.b#yahoo.it in Javascript using the BTOA function I get the result Zy5iQHlhaG9vLml0.
This function converts the string to the base64 representation of the string. When I try and do the equivalent in SAS I get a different value:
proc fcmp outlib=common.funcs.funcs;
function b64(string $) $;
length digest $32767;
digest=strip(put(string,$base64x32767.));
return(digest);
endsub;
quit;
data email;
input email $12.;
datalines;
g.b#yahoo.it
run;
data X;
set email;
e1 = "<" || trim(email) || ">";
pw_e = b64(e1);
put _all_;
run;
Gives me:
email=g.b#yahoo.it e1=<g.b#yahoo.it> pw_e=PGcuYkB5YWhvby5pdD4= _ERROR_=0 _N_=1
Based on the documentation for each, they are using the same alphabet:
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a003161924.htm
https://www.w3schools.com/jsref/met_win_btoa.asp
I want to replicate the same function in SAS Data Step to obtain the same result. Is there a way to do this in SAS?
They are doing the same thing. You are modifying the string that you are passing to the base64 encoding function in SAS which is why you are getting a different result. The string you are using in SAS is <g.b#yahoo.it> (you added the < and > characters around it.
If you run that same string of <g.b#yahoo.it> through btoa() you get the same result of PGcuYkB5YWhvby5pdD4=

Convert Java AES encryption to Javascript using the crypto library

Im trying to convert the java library - AESCrypt-Java
to javascript.
This is my implementation so far for the decrypt function. Im not able to decrypt the text. Can someone figure out where I'm going wrong?
function decrypt(password, base64text) {
key = generateKey(password);
var decodedCipherText = new Buffer(base64text, 'base64')
var iv = new Buffer(16);
iv.fill(0);
var decipher = crypto.createDecipheriv("aes-256-cbc", key, iv)
let decrypted = decipher.update(decodedCipherText, 'base64', 'utf-8');
decrypted += decipher.final('utf-8')
return decryptedBytes
}
function generateKey(password) {
return crypto.createHash('sha256').update(usr_id).digest();
}
var encryptedText = '1+2yFMDH1C/uIc1huwezbrsQ==';
var password = '8AVrWtyabQ';
decrypt(password, encryptedText)
The expected plaintext output is Wordpress.
You are making a few decisions that will adversely affect the security of your sensitive values:
You are using a static, all-zero IV. The IV must be unique and non-predictable for every message encrypted with a specific key. The IV can then be prepended to the cipher text and transmitted unprotected to the recipient, where it is sliced and used for decryption.
Your key derivation function (KDF) is weak -- SHA-256 can be cracked at 23 billion attempts per second on commodity hardware. Use a key-stretching algorithm like PBKDF2 with a high iteration count, or bcrypt or scrypt for memory hardness.
Your cipher text is not authenticated -- AES/CBC provides confidentiality, but not integrity or authentication. An interceptor can manipulate the cipher text in transmission and attempt to decrypt it. This can result in unauthorized decryption (i.e. injecting malicious plaintext into your application) or a padding oracle attack, and eventually cipher text recovery. Use an authenticated encryption (with associated data) (AE or AEAD) cipher mode to mitigate this, or add a strong HMAC construction using a separate key over the cipher text and verify prior to decryption with a constant-time equals method.
new Buffer(string, encoding) and new Buffer(size) are deprecated and Buffer.from(string, encoding) and Buffer.alloc(size) should be used instead. You create a Buffer containing the provided cipher text which is encoded in Base64. I have a feeling there is an issue occurring with your encoding (you don't provide any example output for us to see). Here is an example of encrypting and decrypting with Buffer objects.
function encrypt(buffer){
var cipher = crypto.createCipher(algorithm,password)
var crypted = Buffer.concat([cipher.update(buffer),cipher.final()]);
return crypted;
}
function decrypt(buffer){
var decipher = crypto.createDecipher(algorithm,password)
var dec = Buffer.concat([decipher.update(buffer) , decipher.final()]);
return dec;
}
var hw = encrypt(new Buffer("hello world", "utf8"))
// outputs hello world
console.log(decrypt(hw).toString('utf8'));
As you can see, cipher.update(buffer) handles the encoding internally so you don't need to.

Base64 encode a javascript object

I have large Javascript objects which I would like to encode to base-64 for AWS Kinesis`
It turns out that:
let objStr = new Buffer(JSON.stringify(obj), 'ascii');
new Buffer(objStr, 'base64').toString('ascii') !== objStr
I'm trying to keep this as simple as possible.
How can I base-64 encode JSON and safely decode it back to its original value?
From String to Base-64
var obj = {a: 'a', b: 'b'};
var encoded = btoa(JSON.stringify(obj))
To decode back to actual
var actual = JSON.parse(atob(encoded))
For reference look here.
https://developer.mozilla.org/en/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
You misunderstood the Buffer(str, [encoding]) constructor, the encoding tells the constructor what encoding was used to create str, or what encoding the constructor should use to decode str into a byte array.
Basically the Buffer class represents byte streams, it's only when you convert it from/to strings that encoding comes into context.
You should instead use buffer.toString("base64") to get base-64 encoded of the buffer content.
let objJsonStr = JSON.stringify(obj);
let objJsonB64 = Buffer.from(objJsonStr).toString("base64");
When converting object to base64 I was getting out of latin range issues and character invalid error.
I made it work in my project with the below line.
Include the base64 and utf8 node packages and access them like this:
var bytes = base64.encode(utf8.encode(JSON.stringify(getOverviewComments())));
You can easily encode and decode from and to JSON/Base64 using a Buffer:
JSON to Base64:
function jsonToBase64(jsonObj) {
const jsonString = JSON.stringify(jsonObj)
return Buffer.from(jsonString).toString('base64')
}
Base64 to JSON:
function encodeBase64ToJson(base64String: string) {
const jsonString = Buffer.from(base64String,'base64').toString()
return JSON.parse(jsonString)
}
atob() and btoa() are outdated and should no longer be used.

How can I obfuscate a string in JavaScript?

Basically, I want to make a game in JavaScript and allow a user to get a copy paste-able code which stores their data. In reality, this "code" is actually obfuscated JSON that can be decoded by the application later.
I don't need much security, as I am aware that if people put some effort in they can view/modify the save, and I have no interest in stopping them. I just want the average user to not be tempted and/or see unnecessary information.
Thanks in advance.
you can use base64 encoding to encode your json String. it would be faster approach.
If you with pure javascript :
var encodedData = btoa("stringToEncode");
If you are using nodejs:
base-64 encoding:
var encodedStr = new Buffer("Hello World").toString('base64')
decode to original value:
var originalString = new Buffer("SGVsbG8gV29ybGQ=", 'base64').toString('utf-8')
Well... given that there is no security concern and you only want users to see what appears to be garbled data you can "encode" all the json data
var jsonData = {"key":"value"};
// turn it into a string
var jsonString = JSON.stringify(jsonData);
// replace some letters
var awkardString = jsonString.replace(/a/g, '!Ax6'); // be carefull, you should replace a letter with a pattern that does not already exist on the string.
// encode it with some type of reversible encoding
var garbledData = encodeURI(jsonString);
// output is: %7B%22key%22:%22v!Ax6lue%22%7D
// to "decode" it do the same steps in reverse
awkardString = decodeURI(garbledData);
jsonString = awkardString.replace(/!Ax6/g, 'a'); // now you see, if '!Ax6' existed on the source string, you would loose it and get an 'a' in return. That is why the replacement should be as unique as possible
jsonData = JSON.parse(jsonString);

make a javascript HMAC token

I am trying to emulate the following .NET code in javascript using the Crypto.js library.
var hashInput = "public=ID1000000001::routetype=POST::route=personsearch::key1=ID1000000001::key2=1043"
byte[] inputBytes = new byte[hashInput.Length * sizeof(char)];
System.Buffer.BlockCopy(hashInput.ToCharArray(), 0, inputBytes,0,inputBytes.Length);
byte[] keyBytes = HexadecimalStringToByteArray("A_HEX_STRING_GOES_HERE");
var hmac = HMACSHA256.Create();
hmac.Key = keyBytes;
var clientHash = hmac.ComputeHash(inputBytes);
This gives me a ByteArray which is used as part of POST to a WebAPI in the form of
[41,197,254,91,244,87.....] etc.
I want to make the same exact byte array in a javascript client but i am having diffculty getting this. I have tried the following:
var stringToHash = 'public=ID1000000001::routetype=POST::route=personsearch::key1=ID1000000001::key2=1043';
var privateKey = 'A_HEX_STRING_GOES_HERE';
var hash = CryptoJS.HmacSHA256(stringToHash, privateKey);
//this results in a WordArray, which can be converted to many types
//however i cannot get the byte array as in the .net example
//i.e. i just want to get [41,197,254,91,244,87....] etc.
I can see on the documentation for Crypto.js how to convert to base64, and other formats, but not to the ByteArray which i need.
Any ideas?
--UPDATE
Thanks for the advice on not using BlockCopy, and also for pointing me in the direction of the encoding issues which i had completely neglected.
This was part of the issue. The other part was that i had managed to misuse the HMACSHA256 class. I found (after several, several hours) that the .NET code was not producing the correct hash value.
It turns out this code DID produce the correct Hash:
var hashInput = "a::string::to::hash";
var privateKey = "C0B615950F9D577A4EAF64C9B4F2E50F3DA2C6BB6F790FA346E9732788B29A08AC5444F1B82984DB190A75D3861CC4802D598EBF0025FD1C327928F43EB1C80E";
byte[] inputBytes = Encoding.UTF8.GetBytes(hashInput);
byte[] keyBytes = Encoding.UTF8.GetBytes(privateKey);
HMACSHA256 hmac = new HMACSHA256(keyBytes);
hmac.Initialize();
var clientHash = hmac.ComputeHash(inputBytes);
var base64 = Convert.ToBase64String(clientHash);
Lucky for me my WebAPI is not live yet!
The problem is probably the character encoding. You should use for instance the method encode_utf8(stringValue) in JavaScript and new UTF8Encoding.getBytes(String value) in .NET, to make sure that the character encodings match. Note that modern cryptography is based on bytes, not on strings or characters. Using System.Buffer.BlockCopy() to encode characters is really bad practice.

Categories