Dojo RSA configuration and usage - javascript

I am new to Dojo and I want to be able to use the RSA module to encrypt some information using a public key that is received from a website.
Is there any information or examples on how to do this?
I have found the library dojox.encoding.crypto.RSAKey but there is no information:
https://dojotoolkit.org/reference-guide/1.9/dojox/encoding/crypto/RSAKey.html
I found Dojo because I was researching: http://www-cs-students.stanford.edu/~tjw/jsbn/
Now I want to learn Dojo but my primary reason to use it is the RSA library.
Ps.: I don't want to be lectured on the dangers of RSA on the client side, neither why I shouldn't be doing RSA on JavaScript side.

EDITED: replaced example values with provided ones.
I'm not familiar with RSA, but general use of dojo's RSAKey module would be as follow (according to its source code):
require([
"dojo/dom",
"dojox/encoding/crypto/RSAKey"
],function(dom, RSAKeyModule){
var RSAKey = new RSAKeyModule();
var n= "8efebfa74157b9447e1bc729d5e2a459ee888e87dc7ed764b473e513edba‌​7696a957871ff7a4941e‌​d360d1b42a9788bdc52a‌​8b659357dc8f252e6cc5‌​f5bbf5c659cc9e9837df‌​4ca6eee1c47889b055ac‌​3802bb9491e88483491b‌​08dff9e9472d99341134‌​bcfc4ecf38915553bda0‌​8f943089377a95c7118f‌​ebcef2841288aedb1b8b‌​a22e211da2ab527d26d7‌​accf2e05421260a23f06‌​cf2b13e0ffd51e8f401b‌​c113768027ad29c37156‌​4d179c82639061272e4f‌​940bf50ba6490933f788‌​715f8c268dd2c85a461e‌​899ba416a51557fec7a9‌​a4f1ed3df95cf5bd14bb‌​529dd331b9a79828366a‌​9589deb32e730369cd62‌​352ef7fdd9297e1193f4‌​a33e01289a6f"
var e = "10001";
RSAKey.setPublic(n, e);
var encrypted = RSAKey.encrypt("abc");
dom.byId("result").innerHTML = encrypted;
});
<script src="https://ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js"></script>
<div id="result"></div>
See dojo/request/xhr module - could be helpful for receiving key.

Related

Creating JWT Using SuiteScript 2.x for DocuSign API Integration

I'm updating a NetSuite integration for DocuSign to convert all scripting to SuiteScript 2.0 and update the authentication to utilize the JWT grant method, seeing as the current method looks like it will be deprecated for new applications in a couple weeks, and completely some time next year. I do have a couple questions about it all, though, that I'm having trouble answering.
For starters, when setting the IAT and EXP values for the body, am I getting this value based on the current server time (that varies depending on which time zone the account's datacenter happens to reside), or should it be based on getting the UTC time instead? The documentation didn't seem clear enough to me on that matter.
Additionally, I am unclear how to go about creating the signature. Retrieving and preparing the necessary data, aside form the aforementioned issue, is not a problem. But, arranging the data to be encoded based on what I've seen available within the 2.0 modules as compared to examples I've seen for creating the signature don't seem to align so well. For example, this is an approximation of what I have seen:
var header = { typ : "JWT" , alg : "RS256" };
var body = { iss : "abc123" , sub : "def456" , iat : 123456789 , exp : 123456789 + (60 * 60) , scope : "signature" };
var encHeader = base64UrlEncode(JSON.stringify(header));
var encBody = base64UrlEncode(JSON.stringify(body));
// the key pairs are stored elsewhere, and the functions represent a way to retrieve the key contents
var publickKey = retrievePublicKey();
var privateKey = retrievePrivateKey();
var signature = RS256Encode(
encHeader + "." + encBody ,
publicKey ,
privateKey
);
The examples show different encryption functions that all take in three arguments in same way demonstrated above. However, when looking at what is available within the native SuiteScript 2.0 modules, I don't see anything quite comparable. I see that the crypto module (which gets pulled into other modules it seems) does have the ability to perform the required RSA SHA256 encoding, but not quite sure what the deal is to make sure that all the pieces are taken in together correctly.
I've taken a look at the JavaScript libraries for JWT creation at https://jwt.io/, but I have no experience with attempting to incorporate node modules into a SuiteScript project, if it's even possible.
So, is there a way to natively construct a JWT within SuiteScript, or am I going to have to find a way to be able to reference a node module in a script?
-- EDIT --
Ok, so it looks like I can use "Asynchronous Module Definition" (AMD) to import modules into a script by creating a JSON config file and adding the following to a script file's JSDoc header:
*#NAmdConfig /path/to/myModule.json
I figure that since I need it to be a relative path given that this project will be distributed to other accounts, something like this ought to work if the JSON config file is in the same directory:
*#NAmdConfig ./nodeModules.json
But I'm having a heck of a time determining exactly how to setup the JSON config. I can't quite seem to find anything that really helps determine how to build it correctly. The biggest issue is determining whether or not a module is AMD or non-AMD, and in the case of non-AMD whether to attempt to import the scripts under the CJS section or the ESM section.
For any additional reference, I used NPM to install the jose module since it's compatible with JavaScript/Node and has the encryption methods I need.
npm install jose
Right now, the path for it in the source project looks something like this:
SuiteScripts/Project_Name/lib/node_modules/jose
And the script file that will be referencing the module is located under this path:
SuiteScripts/Project_Name/lib
/**
*#NApiVersion 2.x
*/
define(['N/encode', 'N/crypto/certificate'], function(encode, cert){
function signIt(payload, ttl){
if(typeof payload.exp == 'undefined'){
var secondsSinceEpoch = Math.round(Date.now() / 1000);
var expAt = secondsSinceEpoch + (ttl || 60);
payload["exp"] = expAt;
payload["iat"] = secondsSinceEpoch;
}
log.debug({
title:'payload',
details: JSON.stringify(payload)});
var header = encode.convert({
string: JSON.stringify({
type:'JWT',
alg:'RS256'
}),
inputEncoding: encode.Encoding.UTF_8,
outputEncoding: encode.Encoding.BASE_64_URL_SAFE
}).replace(/=+$/, '');
var body = encode.convert({
string: JSON.stringify(payload),
inputEncoding: encode.Encoding.UTF_8,
outputEncoding: encode.Encoding.BASE_64_URL_SAFE}).replace(/=+$/, '');
var signer = cert.createSigner({
certId:'custcertificate_sample', //from Setup -> Company -> Certificates
algorithm: cert.HashAlg.SHA256
});
signer.update(header +'.'+ body);
var sig = signer.sign({
outputEncoding:encode.Encoding.BASE_64_URL_SAFE
}).replace(/=+$/, '');
return [header, body, sig].join('.');
}
return {
signIt: signIt
}
});
The uploaded key was generated like:
openssl genrsa -out private.pem 4096
openssl rsa -in private.pem -out public.pem -pubout
openssl req -key private.pem -new -x509 -days 3650 -subj "/C=CA/ST=Courtenay/O=Rule of Tech/OU=Information unit/CN=jwt.kotn.com" -out cert.pem
and then I just used a text editor to concatenate the cert and private key from cert.pem and private.pem:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----

Need browser equivalent of this specific implementation of crypto.createHmac method

So, I have this piece of code that's written in Node.js
crypto.createHmac('sha256', secret).update(orderedParams).digest('hex')
I wish to bring this piece of code in the browser but that doesn't work since the 'crypto' library is not supported on the browser. Can somebody just help me re-create the same method in the browser?
An HMAC can be determined by most crypto libraries, e.g. CryptoJS or WebCrypto API.
The following example uses CryptoJS:
var secret = 'my secret';
var orderedParams = 'the ordered params';
// Short
var hmac3 = CryptoJS.HmacSHA256(orderedParams, secret).toString();
console.log(hmac3.replace(/(.{48})/g,'$1\n'));
// Progressive
var hmac2 = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, secret).update(orderedParams).finalize().toString();
console.log(hmac2.replace(/(.{48})/g,'$1\n'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
You can try to use crypto-browserify.
It's a reimplementation of crypto, made it so that it can run on the Browser.

TweetNaCl.js minimal Public-key signatures example

I am trying to understand how to implement a minimal basic Public-key signature example based on the demo located here, using pure javascript.
My research has not yielded a simple javascript example that I can use to understand its inner workings, and the documentation is over my head at the moment.
I tried looking at the source code of the demo, but it is not revealing its secrets.
The library's examples does not have an example for this either.
Cryptography is something very new to me, so any baseline example of how to create their Public-key example with pure javascript in node.js would be greatly appreciated!
Pseudocode-ish:
const nacl = require('tweetnacl')
let message = "This is my unencrypted message"
let naclPair = nacl.sign.keyPair()
let signedMessage = nacl.sign(message, naclPair.secretKey)
let decrypted = nacl.sign.open(signedMessage, naclPair.publicKey) // is this right?
console.log(decrypted) // should this print the decrypted message?
As a side note, I'm more familiar with node.js require, than I am with ES6 import, if that has any bearing on answers here and could help demonstrate how to use this library.
TweetNaCl.js is a port to JavaScript of TweetNaCl. TweetNacl in turn is a compact implementation of NaCl, which provides various encryption and signature algorithms essentially based on Curve25519. There are NaCl-compatible implementations or wrappers for many platforms, so that any of these documentations can be used for an introduction, e.g. the clear documentation of the Libsodium fork.
The documentation of TweetNaCl.js also gives a short overview of the functionality: nacl.sign(message, secretKey) creates a signed message consisting of the 64 bytes signature with attached message. nacl.sign.open(signedMessage, publicKey) verifies the message using the signature and returns the message if verification is successful. The algorithm used for signing is Ed25519.
As already noted in the comments, you do not distinguish clearly between encryption (purpose: secrecy) and signing (purpose: authentication / integrity). In particular, secrecy of the message is not the purpose of signing. This becomes apparent e.g. when the return of nacl.sign() contains the unencrypted message (see code snippet below). However, it is true that encryption with the private key is performed during signing (but not for the purpose of keeping it secret).
The following implementation is a pure JavaScript implementation:
var keyPair = nacl.sign.keyPair();
var secretKey = keyPair.secretKey;
var publicKey = keyPair.publicKey;
var msgStr = "The quick brown fox jumps over the lazy dog";
var msg = nacl.util.decodeUTF8(msgStr);
var signature = nacl.sign(msg, secretKey);
var signatureB64 = nacl.util.encodeBase64(signature);
console.log(signatureB64.replace(/(.{64})/g,'$1\n')); // Display signature plus message (Base64 encoded)
var signatureMsgPart = signature.slice(64);
console.log(nacl.util.encodeUTF8(signatureMsgPart)); // Display message from nacl.sign() return value: signing is not for encryption!
var verifiedMsg = nacl.sign.open(signature, publicKey);
console.log(nacl.util.encodeUTF8(verifiedMsg)); // Display message after successfull verification
<script src="https://cdn.jsdelivr.net/npm/tweetnacl-util#0.15.0/nacl-util.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tweetnacl#1.0.1/nacl.min.js"></script>
and applies the package tweetnacl-util-js for encoding.
By the way, in the implementation you posted only the Utf8 encoding/decoding was missing:
let message = "This is my unencrypted message"
let naclPair = nacl.sign.keyPair()
let signedMessage = nacl.sign(nacl.util.decodeUTF8(message), naclPair.secretKey)
let decrypted = nacl.sign.open(signedMessage, naclPair.publicKey) // is this right? -> Yes
console.log(nacl.util.encodeUTF8(decrypted)) // should this print the decrypted message? -> Yes, but the 'verified' message is printed!
<script src="https://cdn.jsdelivr.net/npm/tweetnacl-util#0.15.0/nacl-util.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tweetnacl#1.0.1/nacl.min.js"></script>
Please see the following links for public key encryption and symmetric encryption with TweetNaCl.js. This is about keeping a message secret.
By the way, using the example page(Public-key signatures) to test the code need to use nacl.sign.detached(message, secretKey) not nacl.sign(msg, secretKey) 😂
reference
TweetNaCl.js Public-key signatures example err

How do I decode a BASE64, PCKS-8 representation of a private key in NetSuite or javascript?

I'm working on a suitescript to integrate NetSuite with the Walmart Marketplace APIs. And, as the another OP here says it right their documentation pretty much says if you don't use Java you're on your own.
I'm looking for a way to do the same either in suitescript or javascript.
Instruction from Walmart's API documentation:
Sign the byte array representation of this data by:
Decoding the Base 64, PKCS-8 representation of your private key. Note that the key is encoded using PKCS-8. Libraries in various languages offer the ability to specify that the key is in this format and not in other conflicting formats such as PKCS-1. Use this byte representation of your key to sign the data using SHA-256 With RSA. Encode the resulting signature using Base 64.
And, a java code from their documentation to do the same:
public static String signData(String stringToBeSigned, String encodedPrivateKey) {
String signatureString = null;
try {
byte[] encodedKeyBytes = Base64.decodeBase64(encodedPrivateKey);
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(encodedKeyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey myPrivateKey = kf.generatePrivate(privSpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(myPrivateKey);
byte[] data = stringToBeSigned.getBytes("UTF-8");
signature.update(data);
byte[] signedBytes = signature.sign();
signatureString = Base64.encodeBase64String(signedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return signatureString;
}
For reference, here's the similar thing asked for dot net. Any help would be appreciated.
I tried developing a SAML connector in Javascript once and found several libraries that deal with different key file formats etc. I got fairly far along but the time to run some of the scripts was incredible (imagine trying to login but the process taking two minutes to decide your login was valid)
At that point I switched to an external system and managed the SSO with Netsuite's inbound SSO.
It doesn't look like things have improved that much with NS in the crypto department even with SS 2.0.
I'd tend to package this into two parts. Generate your files in Suitescript and pass them through a java based web service that handles the signing requirements. Minimizes the amount of Java you have to write and keeps your transaction extraction/formatting scripts under easy control.
I found a library (jsrsasign) that will do the Walmart signature from NetSuite server side in under 4 seconds! (Marketplace has gone to OAuth2, but I'm stuck with signing as a Drop Ship Vendor)
/**
*#NApiVersion 2.x
*#NScriptType ScheduledScript
*/
define(['N/log', 'N/https', '../lib/jsrsasign-master/jsrsasign-all-min'],
function(log, https) {
function execute(context) {
var pkcs8Der = {Your Walmart Private Key};
var pkcs8Pem = [
'-----BEGIN PRIVATE KEY-----',
pkcs8Der.match(/.{0,64}/g).join('\n'),
'-----END PRIVATE KEY-----'
].join('\n');
var tStamp = Date.now()
var stringToSign = [
tStamp,
{Your Walmart Comsumer Id},
{Request URL},
{Request Method (All Caps)}
].join('\n') + '\n';
var sig = new KJUR.crypto.Signature({"alg": "SHA256withRSA"});
sig.init(pkcs8Pem);
var sigVal = hextob64(sig.signString(stringToSign));
log.audit({title: 'Signature', details: sigVal});
log.audit({title: 'Timestamp', details: tStamp});
}
return {
execute: execute,
};
}
);
I had to add the following code to the jsrsasign-all-min.js library file for the Scheduled Script to load the module:
var navigator = {}, window = undefined;

RSA Verification Javascript

I am trying to verify an RSA signature in Javascript but I can't seem to get it to work. I think I have to do something with my key and signatures but I am very confused.
Here is a link to this library
var publicKey = "MIIBIjBNBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoPNkJzHqbY/6mAjJwb4zUbOiOjvmg3b8fvydYdGXdv04r6vzgn/FD5NPJM7bojAxi6sZ8vV+fYVIQey6HnrLSsdU/QXhT3p22a+kB4ym8SbKsOy2fWqL950nZCPYW/DC9txHy+ceFuKMAarFWAMJRe+MaVIbDIAAi8tMNjZ204GkmqveyAeA6JppzthAuiX69H8Zb3Hbs49CHNwLnSpKz5HBTfcgWqHkar2HlEFccvWC++Kq47MIkEcKScS/oneDb/TiL5ClOas1gMxfwiVtkFI6zNxxJOJDSTlY66oHCVCfTruk2pQbtOtwJEGrOwq6B536QL/EkeEKMgiqlpZJbQIDAQAB";
var stringToVerify = "aaa";
var signature = "hXyRmdQOCiVBNgDdGtiWF/gJwIk0Hs+MZtfEU4sFMEu05xsBjR9uymOJ/8FwhKCB0p+Kc1jqtsZxQqtxC0Du2EYyvjs0j5bbU9ZugZw0+9VHqKm0UA23djmZ1MT6nXt2ZEUEsS0La9yrfEnig/swAku1fQorsxG5FK5GFRjaacNIF+O0GOr0cbzEvlaAof6T6JFMueIw/iZykivs8XohSlghdPzoNmVueY9JF1XbtHZayau17jGhFTbeNNxbDBanPo593eZdgi5aTZMYHbxHx87cfU1sE5cjSioPQLsG9cQwVaWrrZa9BnB8IhR8Rv0NdRXYNTcVhc+sVHJN/QghNQ==";
var KJUR = require("cloud/jsrsasign-4.7.0/npm/lib/jsrsasign.js");
var verifier = new KJUR.crypto.Signature({alg: "SHA1withRSA", prov: "cryptojs/jsrsa"});
verifier.init(publicKey);
verifier.updateString(stringToVerify);
console.log(verifier.verify(signature));
Thanks in advance
You need to base 64 decode both the public key and the signature. They key and signature should then be re-encoded as hexadecimals. Then you hopefully should be able to use this method to generate a public RSAKey object.
I'm saying hopefully, as the the API description is just horrible. Personally I would not recommend to use such an API.

Categories

Resources