I wrote a system that is implemented using CryptoJS.
After writing my code, I discovered crypto.subtle.encrypt which is an AES implementation built into browsers.
I want to change my code away from using CryptoJs and onto using crypto.subtle.encrypt.
Data encoded the old way (CryptoJS) has to be compatible with the new way (crypto.subtle.encrypt).
How can I achieve this?
When I wrote my original code, it looked much like this:
function cryptojs_encrypt(message) {
var key = "my password";
return CryptoJS.AES.encrypt(message, key).toString());
}
Where the "key" passed in is just a string. From what I've been able to read from other stackoverflow questions, CryptoJS converts this string into a "key" and "iv". How exactly is this achieved? I tried looking through the CryptoJS source code but couldn't find what I was looking for.
The way subtle.crypt.encrypt works, is that you have to pass in the key and iv explicitly. Here is my code:
function subtle_encrypt(message) {
var msg = new TextEncoder().encode(message);
var pass = new TextEncoder().encode('my password');
var alg = { name: 'AES-CBC', iv: pass };
crypto.subtle.importKey('raw', pass, alg, false, ['encrypt']).then(function(key){
crypto.subtle.encrypt(alg, key, msg).then(function(ctBuffer){
var string = btoa(ctBuffer);
console.log("result", string);
});
});
}
This works but returns a different result. I need to modify the arguments that go into alg which matches what CryptoJS uses when you pass in a string. How do I do this?
I've created a small library to do just that.
Embed WebCrypto.js (Minified) in your document.
Use it like this:
// Initialize the library
initWebCrypto();
// Encrypt your stuff
WebCrypto.encrypt({
data: btoa("my message"),
password: "my password",
callback: function(response){
if( !response.error ){
console.log(response.result); // Compatible with CryptoJS
}else{
console.error(response.error);
}
}
});
See https://github.com/etienne-martin/WebCrypto.swift/blob/master/www/index.html for more examples.
Source code: https://github.com/etienne-martin/WebCrypto.swift/blob/master/source.js
Hope this helps!
Related
I'm trying to make a JWT generator in JavaScript for educational purposes. There is a jwt.io tool to create and/or validate JWT.
I'm struggling to get my results match the results from the validator. The problem is the signature.
Here's my code:
function base64url(input) {
return btoa(typeof input === 'string' ? input : JSON.stringify(input))
.replace(/=+$/, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
}
const JWT = {
encode(header, payload, secret) {
const unsigned = [base64url(header), base64url(payload)].join('.');
return [unsigned, base64url(sha256.hmac(secret, unsigned))].join('.');
}
};
To encrypt HMAC SHA256 I'm using js-sha256 library with sha256.hmac(key, value) prototype. I compared it with online tools and it works fine.
Now, I test it with the following code:
const jwt = JWT.encode(
{
alg: 'HS256',
typ: 'JWT'
},
123,
'xxx'
);
The result I get is:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz.NzhlNTFmYzUxOGQ2YjNlZDFiOTM0ZGRhOTUwNDFmMzEwMzdlNmZkZWRhNGFlMjdlNDU3ZTZhNWRhYjQ1YzFiMQ
On the other hand, the result from jwt.io is:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz.eOUfxRjWs-0bk03alQQfMQN-b97aSuJ-RX5qXatFwbE
As you can see, the two out of three chunks of JWT are identical in my result and jwt.io result. The signature is different and if you ask me, the signature generated by it is surprisingly short. That tool also marks my own JWT as invalid.
I checked with online HMAC SHA256 generators and it looks like my code creates a valid signature, so:
base64url(sha256.hmac('xxx', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz')) ===
'NzhlNTFmYzUxOGQ2YjNlZDFiOTM0ZGRhOTUwNDFmMzEwMzdlNmZkZWRhNGFlMjdlNDU3ZTZhNWRhYjQ1YzFiMQ'
Is jwt.io just broken or does it do it some other way?
I wouldn't say you're doing it wrong, but you missed a small but important detail.
The result from jwt.io is correct and the hash you calculate is also correct. But the signature you create with your hash is not correct.
The hash you calculate with sha256.hmac(secret, unsigned) is a large number but the return value of the function is a hexadecimal string representation of that large number. For the signature you need to base64url encode the original number instead of it's string representation.
I modified your code, so that it encodes the hash value directly to base64url (node.js version):
const JWT = {
encode(header, payload, secret) {
const unsigned = [base64url(header), base64url(payload)].join('.');
const hash = sha256.hmac(secret, unsigned);
console.log(hash);
var signature = new Buffer.from(hash, 'hex').toString('base64').replace(/\+/g,'-').replace(/\=+$/m,'');
return [unsigned, signature].join('.');
}
};
or, if you don't use node.js, you can use this instead (as suggested by Robo Robok):
const JWT = {
encode(header, payload, secret) {
const unsigned = [base64url(header), base64url(payload)].join('.');
return [unsigned, base64url(sha256.hmac(secret, unsigned).replace(/\w{2}/g, byte => String.fromCharCode(parseInt(byte, 16))))].join('.');
}
};
The result is a token, which is identical to the one created with jwt.io:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.MTIz.eOUfxRjWs-0bk03alQQfMQN-b97aSuJ-RX5qXatFwbE
See also my answer here, in which I explained the steps to compare the results from different tools.
I've faced with following issue: i try to convert some string str to md5 bytestring hash. In PHP we can use md5(str, true), but in JS (nodejs express) i can't find some way to receive the same result. I've included npm module js-md5, but arrayBuffer method of this module returns another result (differes from PHP md5(str, true)).
Could somebody help me, please.
Thanks
var md5 = require('md5');
console.log(md5('text'))
Use CryptoJS module :
NPM link here
And do something like :
// Requires
var crypto = require('crypto');
// Constructor
function Crypto() {
this.hash;
}
// Hash method
Crypto.prototype.encode = function(data) {
this.hash = crypto.createHash('md5').update(data);
var result = this.hash.digest('hex');
return result;
};
// Comparison method (return true if === else false)
Crypto.prototype.equals = function(data, model) {
var bool = false;
var data = data.toUpperCase();
var model = String(model).toUpperCase();
if (data == model){
bool = true;
} else {
bool = false;
}
return bool;
};
// Exports
module.exports = Crypto;
Then instantiate this "tool" object in your code and use methods.
Easy as pie, and the same thing can be done with anothers encryption methods like AES, SHA256, etc.
About the raw_output option (binary answer, padded on 16 bits) you can easily convert the returned var in binary format with a simple function, see this SO post to know how.
Have fun.
Short answer:
const crypto = require('crypto');
const buffer = crypto.createHash('md5').update(str).digest();
Long answer: you need to use NodeJS’ default crypto module (no need for a dependency here), which contains utility function and classes. It is able to create hashes (for instance MD5 or SHA-1 hashes) for you using synchronous or asynchronous methods. A short utility function named crypto.createHash(algorithm) is useful to create a hash with minimal coding. As the docs specifies:
The algorithm is dependent on the available algorithms supported by the version of OpenSSL on the platform. Examples are 'sha256', 'sha512', etc. On recent releases of OpenSSL, openssl list-message-digest-algorithms will display the available digest algorithms.
Now, this createHash function returns a Hash object, which can be used with a stream (you can feed it a file, HTTP request, etc.) or a string, as you asked. If you want to use a string, use hash.update(string) to hash it. This method returns the hash itself, so you can chain it with .digest(encoding) to generate a string (if encoding is set) or a Buffer (if it’s not). Since you asked for bytes, I believe a Buffer is what you want (Buffers are Uint8Array instances).
I'm trying to use the Gmail API to get a user's email, grab the message subject and body, and then display it on a webpage. I'll be doing other stuff with it, but this is the part that I am having difficulty with. I am using Angular.js.
Here is my API call:
function makeApiCall() {
gapi.client.load('gmail', 'v1', function() {
var request = gapi.client.gmail.users.messages.list({
labelIds: ['INBOX']
});
request.execute(function(resp) {
var content = document.getElementById("message-list");
angular.forEach(resp, function(message) {
var email = gapi.client.gmail.users.messages.get({'id': message.id});
// var raw = email.payload.parts;
// console.log(raw);
content.innerHTML += JSON.stringify(email) + "<br>";
})
});
});
}
So gapi.client.gmail.users.messages.list returns an array of my messages, with their ID numbers. That is working.
The call to gapi.client.gmail.users.messages.get({<specific message ID>}) outputs this - {"B":{"method":"gmail.users.messages.get","rpcParams":{},"transport":{"name":"googleapis"}}}.
Not sure what that is, but trying to get the message payload (email.payload.parts), results in undefined. So, how can I get the message content?
Also, I would assume that if I can get the message contents, I would then have to Base64 decode the contents to get some English out of it. Any suggestions for that would be of great help also. I've found this: https://github.com/kvz/phpjs, but since I'm not sure how to go about getting the message contents so that I can try and decode them, so not sure if that php.js is of an help in that regard.
Regarding the Base64 decoding, you can use
atob(dataToDecode)
For Gmail, you'll also want to replace some characters:
atob( dataToDecode.replace(/-/g, '+').replace(/_/g, '/') );
The above function is available to you in JavaScript (see ref). I use it myself to decode the Gmail messages. No need to install extra stuff. As an interesting tangent, if you want to encode your message to Base64, use btoa.
Now, for accessing your message payload, you can write a function:
var extractField = function(json, fieldName) {
return json.payload.headers.filter(function(header) {
return header.name === fieldName;
})[0].value;
};
var date = extractField(response, "Date");
var subject = extractField(response, "Subject");
referenced from my previous SO Question and
var part = message.parts.filter(function(part) {
return part.mimeType == 'text/html';
});
var html = atob(part.body.data.replace(/-/g, '+').replace(/_/g, '/'));
Depending on what your emails look like (single text/plain part? multipart with text/html? attachments, etc?) you may or may not have any "parts" in your email.payload and instead you'll have what you're looking for in "email.payload.body.data" (for single-part messages). This is all assuming you're doing a message.get with the default format ("full"). If you instead want to get the entire email in the message.raw field and deal with it in email libraries for your language you can call message.get(format=raw).
For more info check out the "body" and "parts[]" field documentation for "Message" at https://developers.google.com/gmail/api/v1/reference/users/messages
Ah! I figured it out. parts is an array, so I should have been calling it like: gapi.client.gmail.users.messages.get({'id': <message ID>}).payload.parts[0].body.data
Now my problem is decoding the emails, which is proving successful in plain text emails, but failing in emails from non-personal locations (businesses, social media update emails, etc.). But I'll make a new question to get answers for that.
You need to search where the body for a given mime type is, I have written a recursive function for that:
function searchBodyRec(payload, mimeType){
if (payload.body && payload.body.size && payload.mimeType === mimeType) {
return payload.body.data;
} else if (payload.parts && payload.parts.length) {
return payload.parts.flatMap(function(part){
return searchBodyRec(part, mimeType);
}).filter(function(body){
return body;
});
}
}
So now you can call
var encodedBody = searchBodyRec(this.message.payload, 'text/plain');
See the flatMap method up there? Classic FP method missing in js, here is how to add it (or you can use lodash.js, or underscore.js if you don't want to mess with the native objects)
Array.prototype.flatMap = function(lambda) {
return Array.prototype.concat.apply([], this.map(lambda));
};
I have this code in IndexedDB:
var request = objectStore.add({ entryType: entryType, entryDate: t});
Now I want to know the key of this record that was just added in. How do I do that?
I found this article, and this
code:
var data = {"bookName" : "Name", "price" : 100, "rating":"good"};
var request = objectStore.add(data);
request.onsuccess = function(event){
document.write("Saved with id ", event.result)
var key = event.result;
};
This does not work for me - key shows up as undefined. I think I am missing something basic here!
Go through this code
var data = {"bookName" : "Name", "price" : 100, "rating":"good"};
var request = objectStore.add(data);
request.onsuccess = function(event){
document.write("Saved with id ", event.result)
var key = event.target.result;
};
Hope this code will work to retrieve key of last inserted Record
The spec is written for user agent, not for developer. So it is confusing. Key generator is provided by the user agent.
Any event object that is received by onsuccess handler always have event.target.result. It is the key you are looking for. The key is auto generated if you don't provide it, assuming you set autoIncrement to true.
It is documented in Step 8: as follow:
The result of this algorithm is key.
The trick here is knowing how to search using phrases iteratively, until you land on what you need. I've never heard of IndexedDB before, but seem to have found what you want.
I put "IndexedDB" into a search engine and found this. That yielded the phrase "key generator", so I searched for that as well which led me to this and this.
The StackOverflow link discusses using UUIDs, which of course can be generated in JavaScript, and the last link appears to have examples to do what you want out of the box.
If you're using the idb Promise wrapper for IndexedDB then the new key is just the return value from the add() call:
import { openDB } from 'idb';
const db = await openDB(...);
const tx = db.transaction('mystore', 'readwrite');
const newId = await tx.store.add({ hello: 'world' });
await tx.done;
console.log(`Autogenerated unique key for new object is ${newId}`);
Remember of course, this will only work if you include autoIncrement: true in the options passed to createObjectStore().
The examples I've seen online seem much more complex than I expected (manually parsing &/?/= into pairs, using regular expressions, etc). We're using asp.net ajax (don't see anything in their client side reference) and would consider adding jQuery if it would really help.
I would think there is a more elegant solution out there - so far this is the best code I've found but I would love to find something more along the lines of the HttpRequest.QueryString object (asp.net server side). Thanks in advance,
Shane
There is indeed a QueryString plugin for jQuery, if you're willing to install the jQuery core and the plugin it could prove useful.
I am using this function in case i don't want to use a plugin:
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
return null;
}
Take a look at my post, as it tells you exactly how to do this:
http://seattlesoftware.wordpress.com/2008/01/16/javascript-query-string/
For jQuery I suggest jQuery BBQ: Back Button & Query Library By "Cowboy" Ben Alman
jQuery BBQ leverages the HTML5
hashchange event to allow simple, yet
powerful bookmarkable #hash history.
In addition, jQuery BBQ provides a
full .deparam() method, along with
both hash state management, and
fragment / query string parse and
merge utility methods.
Example:
// Parse URL, deserializing query string into an object.
// http://www.example.com/foo.php?a=1&b=2&c=hello#test
// search is set to ?a=1&b=2&c=hello
// myObj is set to { a:"1", b:"2", c:"hello" }
var search = window.location.search;
var myObj = $.deparam.querystring( search );
Use the String utility from prototypejs.org, called toQueryParams().
Example from their site: http://prototypejs.org/api/string/toQueryParams
'section=blog&id=45'.toQueryParams();
// -> {section: 'blog', id: '45'}
'section=blog;id=45'.toQueryParams();
// -> {section: 'blog', id: '45'}
'http://www.example.com?section=blog&id=45#comments'.toQueryParams();
// -> {section: 'blog', id: '45'}
'section=blog&tag=javascript&tag=prototype&tag=doc'.toQueryParams();
// -> {section: 'blog', tag: ['javascript', 'prototype', 'doc']}
'tag=ruby%20on%20rails'.toQueryParams();
// -> {tag: 'ruby on rails'}
'id=45&raw'.toQueryParams();
// -> {id: '45', raw: undefined}
Also, you may use the alias parseQuery() to obtain the same results.
window.location.search.parseQuery();
Since window.location returns an object, you must obtain the string.
*$(document).ready(function () {
$("#a").click(function () {
window.location.href = "secondpage.aspx?id='0' & name='sunil'& add='asr' & phone='1234'";
});
});*
**then read the query string parameters on another using split method . Here as follows:**
*$(document).ready(function () {
var a = decodeURI(window.location.search);
var id = window.location.search = "id=" + $().val();
var name = a.split("name=")[1].split("&")[0].split("'")[1];
var phone = a.split("phone=")[1].split("&")[0].split("'")[1];
var add = a.split("add=")[1].split("&")[0].split("'")[1];
alert(id+','+name+','+add+','+phone);
});*
If there's any possibility of encountering repeated parameters (e.g. ?tag=foo&tag=bar), most libraries out there won't be sufficient. In that case, you might want to consider this library that I developed from Jan Wolter's very comprehensive parser. I added .plus() and .minus() functions and roundtripping:
https://github.com/timmc/js-tools/blob/master/src/QueryString.js