jsSHA, CryptoJS and OpenSSL libraries giving different results - javascript

New to JS, I'm also learning to use crypto libraries. I don't understand why signing/encoding the same message with the same secret yields differing results.
I'm using jsSHA 1.3.1 found here, and CryptoJS 3.0.2 described here trying to create a base64 sha-1 encoded hmac signature. Here's the code:
In html...
<script src="lib/jsSHA/src/sha1.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/rollups/hmac-sha1.js"></script>
And in js...
var message = "shah me";
var secret = "hide me";
var crypto = CryptoJS.HmacSHA1(message, secret).toString(CryptoJS.enc.Base64) + '=';
var shaObj = new jsSHA(message, "ASCII");
var jssha = shaObj.getHMAC(secret, "ASCII", "B64") + '=';
return "crypto answer is " + crypto + " jssha answer is " + jssha;
Can you help me explain why these results differ?
crypto answer is 3e929e69920fb7d423f816bfcd6654484f1f6d56= jssha
answer is PpKeaZIPt9Qj+Ba/zWZUSE8fbVY=
What's more, both of these differ with the signature I'm generating in rails, like this...
digest = OpenSSL::Digest::Digest.new('sha1')
raw_signature = OpenSSL::HMAC.digest(digest, "hide me","shah me")
b64_signature = Base64.encode64(raw_signature).strip
(would have liked to supply a fiddle, which seems to be a very good common practice, but that, too, is new to me and I was unable to get one working for this question).
Thanks in advance.

There are 3 errors in your code :)
You're missing the enc-base64-min.js for crypto-js. Without it, CryptoJS.enc.Base64 will be undefined
You're missing a parameter when calling .getHMAC(). It's .getHMAC(secret, secret_type, hash_type, output_encoding)
With 1+2 adding a = isn't necessary (nor right)
<script src="lib/jsSHA/src/sha1.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/rollups/hmac-sha1.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/components/enc-base64-min.js"></script>
var message = "shah me";
var secret = "hide me";
var crypto = CryptoJS.HmacSHA1(message, secret).toString(CryptoJS.enc.Base64);
var shaObj = new jsSHA(message, "ASCII");
var jssha = shaObj.getHMAC(secret, "ASCII", "SHA-1", "B64");
return "crypto answer is " + crypto + " jssha answer is " + jssha;
Example

Related

How to get raw output from SHA1 using JS as PHP does?

I'm trying to dynamically generate a security header at Postman pre-request script. To do so, I need to transform the following code snippet from PHP to JS.
$password = "SECRETPASSWORD";
$nonce = random_bytes(32);
date_default_timezone_set("UTC");
$created = date(DATE_ATOM);
$encodedNonce = base64_encode($nonce);
$passwordHash = base64_encode(sha1($nonce . $created . sha1($password, true), true));
(Note the true flag at php's sha1() function, forcing raw output).
I've coded this code snippet so far:
var uuid = require('uuid');
var CryptoJS = require('crypto-js');
var moment = require('moment');
// Generate messageId
var messageId = uuid.v4();
pm.environment.set('messageId', messageId);
// Generate nonce
var nonce = uuid.v4();
var encodedNonce = CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(nonce)
);
pm.environment.set('nonce', encodedNonce);
// Generate created
var created = moment().utc().format();
pm.environment.set('created', created);
// Generate password hash
var password = 'SECRETPASSWORD';
var rawSha1Password = Buffer.from(CryptoJS.SHA1(password).toString(CryptoJS.enc.Base64), "base64").toString("utf8");
var passwordHash = CryptoJS.SHA1(nonce + created + rawSha1Password).toString(CryptoJS.enc.Base64);
pm.environment.set('passwordHash', passwordHash);
My JS script is almost working, the only problem seems to be the sha1 generation. Taking the following example values:
password: SECRETPASSWORD
nonce: 55d61876-f882-42f0-b390-dc662a7e7279
created: 2021-01-21T18:19:32Z
The output from PHP is:
encodedNonce: NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
passwordHash: olI18mUowhmeCwjb1FJNHtTHYDA=
But, the output from JS is:
encodedNonce: NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
passwordHash: tk/uYkL/3Uq0oIkYO0nlBGnV/0E=
As you can see, the encodedNonce is built correctly; however the passwordHash value is different. As I'm using Postman, I have a limited JS libraries available.
Taking this into account, how can I get the same result as the PHP one?
In the line
var rawSha1Password = Buffer.from(CryptoJS.SHA1(password).toString(CryptoJS.enc.Base64), "base64").toString("utf8");
the password hash is read into a buffer and then UTF-8 decoded. The UTF-8 decoding generally corrupts the data, see here. A possible solution is to concatenate the WordArrays instead of the strings:
function getPasswordHash(test){
// Generate nonce
var nonceWA = !test ? CryptoJS.lib.WordArray.random(32) : CryptoJS.enc.Utf8.parse('55d61876-f882-42f0-b390-dc662a7e7279');
console.log('nonce (Base64): ' + nonceWA.toString(CryptoJS.enc.Base64));
// Generate created
var created = !test ? moment().utc().format('YYYY-MM-DDTHH:mm:ss[Z]') : '2021-01-21T18:19:32Z';
var createdWA = CryptoJS.enc.Utf8.parse(created);
console.log('created: ' + created);
// Hash password
var pwd = 'SECRETPASSWORD';
var pwdHashWA = CryptoJS.SHA1(pwd);
// Hash nonce + created + pwd
var passwordHash = CryptoJS.SHA1(nonceWA.concat(createdWA).concat(pwdHashWA)).toString(CryptoJS.enc.Base64);
console.log('passwordHash: ' + passwordHash);
}
getPasswordHash(true); // with testdata
getPasswordHash(false); // without testdata
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
When the code is executed, the first call of getPasswordHash() uses the test data for nonce and date (test=true), the second call applies a random nonce and the current date (test=false) . The call with the test data returns the same result as the PHP code:
nonce (Base64): NTVkNjE4NzYtZjg4Mi00MmYwLWIzOTAtZGM2NjJhN2U3Mjc5
created: 2021-01-21T18:19:32Z
passwordHash: olI18mUowhmeCwjb1FJNHtTHYDA=
CryptoJS implements CryptoJS.lib.WordArray.random() as CSPRNG, which is the counterpart of the PHP method random_bytes(). The use of the uuid library is therefore actually not necessary. I have chosen CryptoJS.lib.WordArray.random() in my example because it is closest to the PHP code.

Convert from Fantom to Javascript

Can someone help me convert the following Fantom code to Javascript?
// compute salted hmac
hmac := Buf().print("${username}:${userSalt}").hmac("SHA-1", password.toBuf).toBase64
// now compute login digest using nonce
digest := "${hmac}:${nonce}".toBuf.toDigest("SHA-1").toBase64
I've been able to compute the hmac variable using CryptoJS:
var hash = CryptoJS.HmacSHA1("alice:6s6Q5Rn0xZP0LPf89bNdv+65EmMUrTsey2fIhim/wKU=", "secret");
var hmac = hash.toString(CryptoJS.enc.Base64);
But I'm still struggling with the digest.
If you post an example, here are the variables I'm using in testing:
username : "alice"
password : "secret"
userSalt : "6s6Q5Rn0xZP0LPf89bNdv+65EmMUrTsey2fIhim/wKU="
nonce : "3da210bdb1163d0d41d3c516314cbd6e"
hmac : "z9NILqJ3QHSG5+GlDnXsV9txjgo="
digest : "B2B3mIzE/+dqcqOJJ/ejSGXRKvE="
This answer uses CryptoJS as you've already had some success with it:
var username = "alice";
var password = "secret";
var userSalt = "6s6Q5Rn0xZP0LPf89bNdv+65EmMUrTsey2fIhim/wKU=";
var nonce = "3da210bdb1163d0d41d3c516314cbd6e";
var hmac = CryptoJS.HmacSHA1(username + ":" + userSalt, password).toString(CryptoJS.enc.Base64);
var digest = CryptoJS.SHA1(hmac + ":" + nonce).toString(CryptoJS.enc.Base64);
console.log(hmac); // --> z9NILqJ3QHSG5+GlDnXsV9txjgo=
console.log(digest); // --> B2B3mIzE/+dqcqOJJ/ejSGXRKvE=
Note it uses the following CryptoJS files:
/rollups/sha1.js
/rollups/hmac-sha1.js
/components/enc-base64-min.js
You can see a live example in this JS paste bin:
https://jsbin.com/luvinayoyi/edit?js,console

Generate Hmac in Javascript from python code

I am trying to generate an hmac hash in javascript.
here is some python code I want to replicate in Javascript:
mac = hmac.new("33fsfsdgvwrg2g223f4f42gf4f34f43f", digestmod=hashlib.sha1)
mac.update(method)
mac.update(url)
mac.update(data)
mac.update(str(timestamp))
r = requests.request(method, url, data=data, headers={
'Content-Type': 'application/json',
'Authorization': " signature="'mac.hexdigest()'" ",
})
This is what I have so far, and it does not seem to be what I need:
var message = "shah me";
var secret = "33fsfsdgvwrg2g223f4f42gf4f34f43f";
var crypto = CryptoJS.HmacSHA1(message, secret).toString(CryptoJS.enc.Base64);
var shaObj = new jsSHA('shah me', "ASCII");
var jssha = shaObj.getHMAC('33fsfsdgvwrg2g223f4f42gf4f34f43f', "ASCII", "SHA-1", "B64");
It looks like your "current solution" is just a copy paste of jsSHA, CryptoJS and OpenSSL libraries giving different results with your key substituted in.
Anyways, you don't need to use both CryptoJS and jsSHA. You should pick one and stick with it.
According to the docs, the python mac.update function is equivalent to appending data to the message. I believe this is the key to your problems, since neither CryptoJS nor jsSHA have an equivalent update function but instead expect you to have the full message to begin with.
The following Python code and the Javascript code that follows it are equivalent:
import hashlib
import hmac
method = 'method'
url = 'url'
data = 'data'
timestamp = 'timestamp'
mac = hmac.new("33fsfsdgvwrg2g223f4f42gf4f34f43f", digestmod=hashlib.sha1)
mac.update(method)
mac.update(url)
mac.update(data)
mac.update(timestamp)
print mac.hexdigest()
Here is the Javascript:
<script src="sha.js"></script>
<script>
var secret = '33fsfsdgvwrg2g223f4f42gf4f34f43f';
var message = 'methodurldatatimestamp';
var shaObj = new jsSHA(message, "ASCII");
document.write(shaObj.getHMAC(secret, "ASCII", "SHA-1", "HEX"));
</script>
Note that the Javascript code puts the full message ('methodurldatatimestamp') in the jsSHA constructor. I believe this is the key to your problem. Hope this helps!

Javascript crypto SJCL GCM not decrypting what I encrypted

I'm currently experimenting with SJCL but I'm having trouble with encryption/decryption. With the lack of good examples I came up with what you see below but it's not working. Can anybody point out what I'm doing wrong? Thanks in advance.
<html>
<!-- sjcl made with: ./configure --with-all -compress=none && make -->
<script type="text/javascript" src="sjcl.js"></script>
<body>
<script>
var p = {
"iv": "PnWtrKCP2DKcLyNC18RKAw==",
"ts": 128,
"mode": "gcm",
"adata": "You can read me",
"cipher": "aes",
"key": "QiJysyALRxUESl18XKl0FcpXQJvFB2Z3Q3A61tdNNM0=" // pbkdf2 key
};
var prp = new sjcl.cipher[p.cipher](sjcl.codec.base64.toBits(p.key));
var plain = "My plaintext";
var ct = sjcl.codec.base64.fromBits(sjcl.mode[p.mode].encrypt(prp, sjcl.codec.bytes.toBits(plain), p.iv, p.adata, p.ts));
var pt = sjcl.codec.base64.fromBits(sjcl.mode[p.mode].decrypt(prp, sjcl.codec.base64.toBits(ct), p.iv, p.adata, p.ts));
document.writeln("ct: " + ct + "<br>");
document.writeln("pt: " + pt + "<br>");
</script>
<hr><pre>
Results in:
encrypted: 5Z2QQ9s6gfORlr6qLvlwjO/J+/TbfSbOs79c4w==
decrypted: AAAAAAAAAAAAAAAA
</pre></body></html>
I think your problem is sjcl.codec.bytes.toBits(plaintext). If you run that by itself, you get [0,0,0]. I think you want sjcl.codec.utf8String.toBits(plaintext) instead.
You would use bytes.toBits if you have an actual byte (octet) array (like [255, 34, 12, 16]), but in this case you're using a string so it needs to be converted differently.
Also, I've had problems with using strings for IVs, you may want to run it through the same conversion as the key before passing it in (base64.toBits(iv)).

Node.js Crypto input/output types

I am trying to figure out the Node.js Crypto library and how to use it properly for my situation.
My Goal is:
key in hex string 3132333435363738313233343536373831323334353637383132333435363738
text in hex string 46303030303030303030303030303030
ciphered text in hex string 70ab7387a6a94098510bf0a6d972aabe
I am testing this through a c implementation of AES 256 and through a website at http://www.hanewin.net/encrypt/aes/aes-test.htm
This is what I have to far, it's not working the way I would expect it to work. My best guess is that the input and output types are incorrect for the cipher function. The only one that works is utf8 if I use hex it fails with a v8 error. Any ideas on what I should convert or change to get it to work.
var keytext = "3132333435363738313233343536373831323334353637383132333435363738";
var key = new Buffer(keytext, 'hex');
var crypto = require("crypto")
var cipher = crypto.createCipher('aes-256-cbc',key,'hex');
var decipher = crypto.createDecipher('aes-256-cbc',key,'hex');
var text = "46303030303030303030303030303030";
var buff = new Buffer(text, 'hex');
console.log(buff)
var crypted = cipher.update(buff,'hex','hex')
The output in crypted in this example is 8cfdcda0a4ea07795945541e4d8c7e35 which is not what I would expect.
Your code is using aes-256-cbc when the website you are deriving test vectors from is using ecb mode. Also, you are calling createCipher, but with ECB you should use createCipheriv with no IV (see nodeJS: can't get crypto module to give me the right AES cipher outcome),
Here is some code that demonstrates this:
var crypto = require("crypto");
var testVector = { plaintext : "46303030303030303030303030303030",
iv : "",
key : "3132333435363738313233343536373831323334353637383132333435363738",
ciphertext : "70ab7387a6a94098510bf0a6d972aabe"};
var key = new Buffer(testVector.key, "hex");
var text = new Buffer(testVector.plaintext, "hex");
var cipher = crypto.createCipheriv("aes-256-ecb", key, testVector.iv);
var crypted = cipher.update(text,'hex','hex');
crypted += cipher.final("hex");
console.log("> " + crypted);
console.log("? " + testVector.ciphertext);
The output of running that code is not exactly what I expect, but the first block of the encrypted output matches your expectation. Probably another parameter that needs to be tweaked.:
$ node test-aes-ecb.js
> 70ab7387a6a94098510bf0a6d972aabeeebbdaed7324ec4bc70d1c0343337233
? 70ab7387a6a94098510bf0a6d972aabe

Categories

Resources