Forge.min.js RSA encryption in C# - javascript

I am trying to use Forge.min.js encryption method to encrypt one of my key, the encryption look like this in Javascript
EncryptPassword: function (Password, exponent,modulus)
{
modulus = new forge.jsbn.BigInteger(modulus,16);
exponent = new forge.jsbn.BigInteger(exponent, 16);
var publicKey = forge.pki.setRsaPublicKey(modulus, exponent);
return publicKey.encrypt(Password);
}
This is the function that i used in forge.min.js. My problem is that i cant load the js file in c# console, whenever i load the Js file using the method below :
MSScriptControl.ScriptControlClass sc = new MSScriptControl.ScriptControlClass();
sc.Language = "javascript";
sc.AddCode(File.ReadAllText(#"C:\forge-min.js"));
when i load the forge-min.js file using the above method in c# console, it goes to exception with message "Expected Identifier". My question is is there a method to encrypt in c# that produce the same result. Or is there a method that is able to load forge-min.js
*Note: forge-min.js file is in here https://cdnjs.cloudflare.com/ajax/libs/forge/0.9.1/forge.min.js

Related

AES Encryption/Decryption in Node JS similar to Java

I am trying to replicate the Java code for AES Encryption and Decryption in Node JS.
Java Code
SecretKeySpec skeySpec;
String key = "a4e1112f45e84f785358bb86ba750f48";
public void encryptString(String key) throws Exception {
try {
skeySpec = new SecretKeySpec(key.getBytes(), "AES");
cipher = Cipher.getInstance("AES");
cipher.init(1, skeySpec);
byte encstr[] = cipher.doFinal(message.getBytes());
String encData = new String(encstr, "UTF-8");
System.out.println(encData);
} catch (NoSuchAlgorithmException nsae) {
throw new Exception("Invalid Java Version");
} catch (NoSuchPaddingException nse) {
throw new Exception("Invalid Key");
}
}
Node JS
var encryptKey = function (text) {
var cipher = crypto.createCipher('aes256', 'a4e1112f45e84f785358bb86ba750f48');
var crypted = cipher.update(text,'utf8', 'hex')
crypted += cipher.final('hex');
console.log(crypted);
return crypted;
}
I am unable to get the exact cipher-text in Node JS, which i am getting in Java.
Your code actually uses different encryption parameters in the 2 cases. AES, being a block cipher, takes: the plain text to encrypt, the initialization vector, also called IV (which is used in conjunction with the plaintext), and the encryption key.
In Java, the IV is, apparently, generated automatically on init() - from the Java SE platform docs for Cipher.init:
The generated parameters can be retrieved using getParameters or getIV
(if the parameter is an IV).
In Node.js, if using the deprecated createCipher function, the IV is generated automatically based on the provided key, probably in a different way than in Java, so you will get a different cipher text. However, you should be using the non-deprecated variant crypto.createCipheriv: https://nodejs.org/docs/latest-v12.x/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options
In order to exactly reproduce the cipher text, you should:
Use the same encryption algorithm on both sides - it's best to specify this exactly, for example aes-256-cbc, or an authenticated encryption scheme such as aes-256-gcm, which is harder to use but provides message authentication.
Use the same IV on both sides, by providing it in the initialization params in Java, and by using createCipheriv in Node; though beware, you should always randomize it in production! See https://stackoverflow.com/a/20888967/6098312
As a closing remark, when using block encryption, you'll usually be generating securely-random IVs, which means the ciphertexts will always differ from one another, even for the same plaintext. This is a good thing! It protects your payload from an attacker who observes the encrypted data and makes conclusions based on message repetitions.
Finally after reviewing Java Docs and Node JS Crypto Docs managed to get the result.
We have to use crypto.createCipheriv() instead of crypto.createCipher with a iv.
Here iv will be null.
Code :
let crypto = require('crypto');
var iv = new Buffer.from(''); //(null) iv
var algorithm = 'aes-256-ecb';
var password = 'a4e1112f45e84f785358bb86ba750f48'; //key password for cryptography
function encrypt(buffer){
var cipher = crypto.createCipheriv(algorithm,new Buffer(password),iv)
var crypted = Buffer.concat([cipher.update(buffer),cipher.final()]);
return crypted;
}
console.log(encrypt(new Buffer('TextToEncrypt')).toString())

RSA Encryption with OAEP between Java and JavaScript

I am trying to encrypt a short string in JavaScript and decrypt it in Java. The decryption fails, and I think it's because of a difference in the block mode and/or padding between the two platforms. I tried encrypting the same string in both Java and JavaScript, and got different results, which indicates that there is indeed a difference. Here is the Java code that creates the key:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair keyPair = kpg.generateKeyPair();
And here is the Java code that I used to test the encryption:
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] bytes = cipher.doFinal("asdf".getBytes());
I send the public key across to the JavaScript process, and convert it to an ArrayBuffer, with the variable name publicKey. I have verified that the key on the JavaScript side matches the key on the Java side (by exporting it with crypto.subtle.exportKey and examining the bytes). Here is the JavaScript code that I used to test the encryption:
crypto.subtle.importKey('spki', publicKey,
{hash: 'SHA-256', name: 'RSA-OAEP'}, true,
['encrypt'])
.then((key) => {
crypto.subtle.encrypt({name: 'RSA-OAEP'}, key,
new TextEncoder().encode('asdf'))
.then((buffer) => {
});
});
The contents of the byte array in Java and the array buffer in JavaScript are not the same. The settings that I am unsure of are the parameter to Cipher#getInstance on the Java side and the parameters to importKey and encrypt on the JavaScript side. Are there any settings that will work between Java and JavaScript using the built-in classes? Or should I look at third-party libraries (e.g., Bouncy Castle)?
This is old, but here's an alternate solution if you want to use subtle crypto in javascript, and have control over the java decryption.
Here's how you decrypt in Java assuming you used the original JS code in the question to encrypt:
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, privKey, oaepParams);
byte[] decrypted = cipher.doFinal(encryptedBytes)
The issue with the Cipher RSA/ECB/OAEPWithSHA-256AndMGF1Padding is that it uses SHA-1 for the MGF1 Padding by default. Javascript uses SHA-256, which causes the mismatch. By specifying the MGF1ParamterSpec, we can force Java to use the same hashing algorithm as Javascript default.
In addition to #Chip's answer - which was really helpful - I would like to add the following case:
Assume you want to use the following for decryption in Javascript (webcrypto):
window.crypto.subtle.decrypt(
{
name: "RSA-OAEP",
hash: { name: "SHA-512" }
//label: Uint8Array([...]) //optional
},
privateRsaKey, //CryptoKey object containing private RSA key
encdata //ArrayBuffer containing to be decrypted data
)
.catch(function(err){
console.error(err);
})
Then you have to use the following OAEPParameterSpec for encryption in Java (and likely vice versa but I did not try that):
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1",
new MGF1ParameterSpec("SHA-512"), PSource.PSpecified.DEFAULT);
Since #Chip referred to the MGF1 Padding I only, I assumed one would have to use
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-512", "MGF1",
new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
but apparently one has to change both hash functions to SHA-512 as shown in my first OAEPParameterSpec code block.
It looks as though the built-in encryption/decryption in JavaScript and Java do not have compatible settings for RSA encryption. A viable solution appears to be the forge library from github (forge on github). The key settings are described on the github page as follows (RSA examples):
// encrypt data with a public key using RSAES-OAEP/SHA-256/MGF1-SHA-1
// compatible with Java's RSA/ECB/OAEPWithSHA-256AndMGF1Padding
var encrypted = publicKey.encrypt(bytes, 'RSA-OAEP', {
md: forge.md.sha256.create(),
mgf1: {
md: forge.md.sha1.create()
}
});

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;

Decrypt AES on server side c# encrypted on client side javascript using sjcl

I am trying to decrypt some text encrypted client-side and having a difficult time making this happen.
I am using the Standford Javascript Crypto Library (sjcl) on client side which generates a json string that I can send to the server, something like this...
sjcl.encrypt("my_key", "something, something");
and produces this json string...
"{"iv":"tfyF7oMXjauOkRnwaffU3A==","v":1,"iter":10000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"bq5G63+hk0U=","ct":"KahF3hQ3FZZ2l7APxLB4nRvw03Xt/qD04AChoA=="}"
Now I receive this json string server-side, and tried to do this...
dynamic data = JObject.Parse(json);
var key = Encoding.ASCII.GetBytes("my_key");
var iv = Convert.FromBase64String(data.iv.Value);
byte[] cypher = Convert.FromBase64String(data.ct.Value);
string plainText = null;
using (AesCryptoServiceProvider provider = new AesCryptoServiceProvider())
{
provider.Key = key;
provider.IV = iv;
ICryptoTransform decryptor = provider.CreateDecryptor(provider.Key, provider.IV);
using (MemoryStream ms = new MemoryStream(cypher))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
plainText = sr.ReadToEnd();
}
}
}
}
return plainText;
The server side code above crashes on plainText = sr.ReadToEnd; and throws a "The input data is not a complete block". I know that AES requires the cypherText to have a block size of 128 and since the text varies in size, how do I guarantee that it has a block size of 128 and not screw up the intended value? Does setting the padding necessary here?
Also, what's the corresponding mode for "ccm" in the AESCryptoServiceProvider? Do the "iter" & "salt" need to be in play in order to decrypt correctly?
Thanks in advance!
Jaime

Encrypting with EasyRSA in PHP and Decrypting with Node-RSA in JS

Problem
I'm trying to create a simple trustless file sharing application. I'm using EasyRSA (https://github.com/paragonie/EasyRSA) to create a key-pair and then encrypt my data with the public key. I'm sending the private key to my JS wherein I'm using Node-RSA (https://github.com/rzcoder/node-rsa). Here, I try to decrypt using the previously created private key. But this happens:
Error
Uncaught Error: Error during decryption (probably incorrect key). Original error: Error: Incorrect data or key
at NodeRSA.module.exports.NodeRSA.$$decryptKey (drop.js:23265)
at NodeRSA.module.exports.NodeRSA.decrypt (drop.js:23213)
at window.onload (drop.js:22270)
My JS code looks something like this:
var NodeRSA = require('node-rsa');
window.onload = function() {
var key_val = $('#key').val();
var ciphertext = $('#encrypted').val();
var key = new NodeRSA();
key.importKey($('#key').val(), 'pkcs1');
var ciphertext_base = key.encrypt(ciphertext, 'base64');
var decrypted_base = key.decrypt(btoa(ciphertext));
console.log(decrypted);
}
I think this has got something to do with the incompatibility between the formats/key-sizes/algos used to create the keys. If someone could help me 'hack' this and make it work, I would be very grateful to you.

Categories

Resources