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

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.

Related

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.

Can a JWT produced with RS256 be decoded by the jwt.io website?

recently I made my first Express server and added user authentication using jwt. Now I'm looking for a way to encrypt that jwt so that users will not be able to see the payload in the https://jwt.io website.
Does anyone know if this encryption algorithm will prevent the users from seeing the payload without the public / private key?
RS256 uses digital signature to ensure Integrity, Authenticity, and Non-repudiation on the produced token. It does not ensure Confidentiality.
If you want to use JWE (JSON Web Encryption) with JWT you can. And since you are both the issuer and recipient of the token I would suggest to use symmetric encryption. Actually you may not use asymmetric crypto because anyone with the possession of the public key would be able to encrypt and you cannot ensure you are actually the one who originally produced the JWE.
You need an implementation/library that supports JWE formatted JWTs. That would give you Integrity, Authenticity, and Confidentiality. e.g.:
npm install jose#4
To encrypt use EncryptJWT
import * as crypto from 'crypto'
import { EncryptJWT } from 'jose'
const secretKey = crypto.randomBytes(32) // you would pass your own 32 byte secret as a Buffer
const jwt = await new EncryptJWT({ 'urn:example:claim': true })
.setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })
.setIssuedAt()
.setIssuer('urn:example:issuer')
.setAudience('urn:example:audience')
.setExpirationTime('2h')
.encrypt(secretKey)
This produces a JWE-formatted JWT like so that, without the knowledge of secretKey, cannot have its payload decrypted.
eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..Wz7DdwAPlbq4cYxn.OMfWJTMuyfLcdN4g541KfcDFKaL5y2bBaFIxuC_-mVa7YLE4M7bVfiO9R2umvpD_acGj5l3gvxulcRnHzBMeRpm4qgbJuWVdA1fYUOguDs1h2xtesZ_9iZUEtcu3hEJ1wVM46ad-9dPebe_VaWwe4XVU5GM.7lDflVFg_Qm3N88xX8Dy1A
To decrypt and validate the JWT Claim Set use jwtDecrypt
import { jwtDecrypt } from 'jose'
const { payload, protectedHeader } = await jwtDecrypt(jwt, secretKey, {
issuer: 'urn:example:issuer',
audience: 'urn:example:audience'
})

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).

Node HmacSHA1 Seed

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 :)

Generate multiple hash values from one stream with Crypto in Node.js

I am working on a Node.js application. Readable stream from a child process' output is being piped into a writable stream from a Crypto module to generate 4 hash values (md5, sha1, sha256 and sha512). This module only allows to generate one hash at a time. After hashes are created, both this stream and hash values should be somehow passed further in order to be recorded to a file. I am currently stuck at creating the first hash value. How can I solve this problem? Part of the code can be seen below.
import Crypto from 'crypto';
let md5 = Crypto.createHash('md5');
//How to generate another hash using the same input
md5.on('readable', () => {
const data = md5.read();
if (data) {
console.log(md5.read());
}
})
...
childProc.stdout.pipe(md5);
You can create multiple hashes from a single stream by piping to multiple transform streams.
import Crypto from 'crypto';
let md5 = Crypto.createHash('md5');
let sha1 = Crypto.createHash('sha1');
md5.on('readable', () => {
const data = md5.read();
if (data) console.log(data.toString('hex'));
})
sha1.on('readable', () => {
const data = sha1.read();
if (data) console.log(data.toString('hex'));
})
childProc.stdin.pipe(md5)
childProc.stdin.pipe(sha1)
This code will print the hash for each algorithm to stdout.

Categories

Resources