Node HmacSHA1 Seed - javascript

I'm trying to send a SOAP request via Node, talking to a service which is secured with WSS.
I need to sign the XML response with a SignedInfo element which requires me combining a Nonce Binary secret I generated, with a Nonce binary secret returned from the initial token request - PSHA1 format.
I've been able to validate this using Java, by utilising the following class (Where the secret is my client nonce and the seed is the server nonce):
https://github.com/apache/wss4j/blob/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/P_SHA1.java#L57
With the following Java code:
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec key = new SecretKeySpec(getSharedKey(), "HmacSHA1");
mac.init(key);
String bytesToSign = "<XML_TO_SIGN_GOES_HERE>";
String signature = Base64.encodeBytes(mac.doFinal(bytesToSign.getBytes()));
I need to do this in a Node project though, I've looked at the Crypto API and numerous plugins but I'm unable to generate the same signature.
How do I specify a seed for a HmacSHA1 using node?

I managed to get there in the end, there's an NPM module called psha1 (https://www.npmjs.com/package/psha1).
Using that library I created the following a generateSignature module which looks as follows:
const crypto = require('crypto');
const psha1 = require('psha1');
export const generateSignatureValue = ({
clientSecret,
serverSecret,
messageToSign,
}) => {
const secretKey =
psha1(clientSecret, serverSecret, 256);
const hash =
crypto
.createHmac('sha1', Buffer.from(secretKey, 'base64'))
.update(messageToSign)
.digest('binary');
return Buffer
.from(hash, 'binary')
.toString('base64');
};
export default generateSignatureValue;
This gives me the desired output :)

Related

How would I securely pass a salt to the web crypto API?

I am using the web crypto API to compute a hash:
const encoder = new TextEncoder();
const salt = "foo";
const data = encoder.encode(str + salt);
const hash = await crypto.subtle.digest("SHA-256", data);
What I don't understand is, how would I possibly pass a salt to this in a secure way, without someone being able to read it?
Context: next.js doesn't let you use crypto node module in middleware. I need to use the crypto module for auth, so they suggest using the web crypto API here.

unzip encrypted file with AES-256 node.js

I'm trying to decrypt zip file using crypto provided file is in s3 storage and I have password for the file and no info for IV.
I'm using cryptoDecipher but getting error cryptoDecipher is deprecated. I saw some post saying use createDecipheriv but I don't have any IV that I can use.
below is a sample code -
function demo(entry){
password = 'mypassword';
algorithm = 'aes-256-cbc';
let decrypt = crypto.createDecipher(algorithm,password)
let res = entry.stream().pipe(decrypt);
const uploadParams = {
Body:res,
Bucket:myS3bucket,
key:myfilename,
}
uploadfile(uploadParams)
}
I'm using unzipper to unzip file and getting 'entry as object for file so just using that object in demo function'
help me out as I'm new to streams and crypto lib.

How can I get window.crypto.subtle to output the same signature as the 'crypto' js library?

I'm wanting to use the built in browser crypto object instead of importing the crypto library in my browser project.
I want to not need this.
npm i crypto
The following code illustrates trying to sign a simple message using both libraries, however I get a different result when using the built in crypto object. The outputs are logged to console.
// START CRYPTO LIBRARY METHOD
import crypto from 'crypto';
const HMAC_SHA256_KEY = {
API_KEY: '***',
SECRET_KEY: '***'
};
const queryString = `timestamp=${(new Date().getTime())}`;
const signature = crypto
.createHmac('sha256', HMAC_SHA256_KEY.SECRET_KEY)
.update(queryString)
.digest('hex')
console.log("standard encrypt", signature);
// START NATIVE LIBRARY METHOD
const getUtf8Bytes = (str: string) => {
return new Uint8Array(
[...(unescape(encodeURIComponent(str)))].map(c => c.charCodeAt(0))
);
};
// #ts-ignore
const builtInCrypto = window.crypto || window.msCrypto;
builtInCrypto.subtle.importKey(
'raw',
getUtf8Bytes(HMAC_SHA256_KEY.SECRET_KEY),
{
name: 'HMAC', hash: 'SHA-256'
},
true, ['sign']
).then(key => {
builtInCrypto.subtle.sign(
'HMAC',
key,
getUtf8Bytes(queryString))
.then(signature => {
console.log('builtIn Signature', btoa(String.fromCharCode(...(new Uint8Array(signature)))));
})
})
Clearly I'm doing something wrong, but what? Why am I getting a different output for the native encryption?
For the same values of HMAC_SHA256_KEY.SECRET_KEY and queryString (the last parameter depends on time and therefore has a different value for each run) both scripts return the same HMac but in different encodings, resulting in different output. In the NodeJS / crypto code the HMac is hex encoded, in the JavaScript / Web Crypto API code Base64 encoded.
To make sure that the output is identical, the same encoding must be used in both scripts. Either the HMac in the JavaScript / Web Crypto API code must also be hex encoded, see e.g. here for a suitable method, or the HMac in the NodeJS / crypto code must be Base64 encoded (by passing 'base64' instead of 'hex' as argument of digest).

Encrypt in nodeJS and Decrypt in Javascript

I am encrypting text in node JS by using node-RSA and passing it to client(javascript), in which JSEncrypt library is using,but all the time the decrypted message is coming null. Public key and private Key is developing on nodeJS server, encrypting with Public key and decrypting on javascript side with Private Key .
This is not happening right!!!!
Can anyone tell which library i should use in javascript to decrypt the message coming from nodejs(using Node-RSA).OR any other IDEA!!
We are already using HTTPS but our use case is such that we have a broker between it.. and its not trusted broker, and we are forced to use it.. so we would like to use encryption decryption.. Although we have trusted people in our client side, so we are decrypting at client side.
I used CryptoBrowserify to encrypt at javascript (client side)
import CryptoBrowserify from 'crypto-browserify';
public encryptStringWithRsaPublicKey(data: string, publicKey: string): string {
var encrypted = CryptoBrowserify.publicEncrypt( publicKey,new Buffer(data));
return encrypted.toString('Base64');
}
And crypto to dedcrypt at Nodejs
decrypt = function(privateKey, data) {
var crypto = require('crypto');
var buffer = new Buffer(data, 'base64');
var decrypted = crypto.privateDecrypt(privateKey, buffer);
return decrypted.toString('utf8')
};
Nodejs has its builtin cryto library,it is optimized and tested, recommend to use that: https://nodejs.org/api/crypto.html

How can I get a certificate to verify my RSA digital signature using jsrsasign?

I am using jsrsasign and I have an RSA public and private key that I generated earlier. I used the following code to generate a signature.
var key = new RSAKey();
key.readPrivateKeyFromPEMString($("#private")[0].value); // "private" is a textarea containing the PEM encoded private key.
var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA", "prov": "cryptojs/jsrsa"});
sig.initSign(key);
sig.updateString('message');
var sigValueHex = sig.sign();
This produced the expected hex signature. The documentation for jsrsasign provides the following code to verify signatures.
var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA", "prov": "cryptojs/jsrsa"});
sig.initVerifyByCertificatePEM("-----BEGIN CERTIFICATE-----(snip)");
sig.updateString('message');
var isValid = sig.verify(sigValueHex);
My problem is that I do not have a certificate to verify with. sig.sign() only gives the signature, no certificate. There is also sig.initVerifyByPublicKey(RSAKey) which seemed promising at first. However, RSAKey only has ways to get the private key, not public key.
So, my question is how can I get this certificate? And if I can't, how can I get an RSAKey object from my public key? And if I can't do that, is there a better way to do digital signatures with javascript?
I am using jsrsasign and jQuery. http://kjur.github.io/jsrsasign/
If there is no way to do this with the methods I am using, is there a better way? My main goal here is to be able to send a message to a server and be sure that the message wasn't tampered with and that it came from the correct place. SSL is not an option here. The message does not need to be encrypted, only protected.
You can create the public key with KEYUTIL.getKey(param), where "param" can be a string with either your certificate or your public key.
Here are the docs for that method
Example:
var publicKey = KEYUTIL.getKey(publicKeyString);
var sig = new r.Signature({"alg": "SHA256withRSA", "prov": "cryptojs/jsrsa"});
sig.initVerifyByPublicKey(publicKey);
sig.updateString(message);
var isValid = sig.verify(signature);
Hope that helps.
You could generate an X.509 self-signed key/certificate pair by using openssl.
openssl req -newkey rsa:4096 -x509 -nodes -keyout key.pem -out certificate.pem
Then upload certificate.pem to the server, and use key.pem to sign messages on the client.
The message and signature can be verified on the server.
Here is the sample code of using jsrsasign:
const jsrsasign = require("jsrsasign");
const fs = require('fs');
// sign
let key = fs.readFileSync("./key.pem", "utf-8");
let sig = new jsrsasign.KJUR.crypto.Signature({"alg": "SHA1withRSA"});
sig.init(key);
sig.updateString("message");
const signature = sig.sign();
// verify
let certificate = fs.readFileSync("./certificate.pem", "utf-8");
let ver = new jsrsasign.KJUR.crypto.Signature({"alg": "SHA1withRSA"});
ver.init(certificate);
ver.updateString("message");
ver.verify(signature); // return true
For your reference.

Categories

Resources