Common secret using ConcatKDH in node-jose - node.js

I'm trying to generate a common secret key using ConcatKDF algorithm.
In the following example alice is trying to generate a common secret using bobs public key using node-jose library:
const index = require('node-jose/lib/algorithms/index');
const keystore = jose.JWK.createKeyStore();
const bobKey = await keystore.generate('EC', 'P-256');
const aliceKey = await keystore.generate('EC', 'P-256');
const bobPublicKey = bobKey.toJSON();
const alicPrivateKey = aliceKey.toJSON(true);
const props = {
public: bobPublicKey,
length: 256
};
const result = await index.derive('ECDH-CONCAT', alicPrivateKey, props);
It fails with the following error:
Error: invalid EC public key
at validatePublic (node_modules\node-jose\lib\algorithms\ecdh.js:47:29)
at nodejs (node_modules\node-jose\lib\algorithms\ecdh.js:164:13)
at Object.main [as derive] (node_modules\node-jose\lib\algorithms\helpers.js:110:42)
at fn (node_modules\node-jose\lib\algorithms\ecdh.js:207:29)
at Object.exports.derive (node_modules\node-jose\lib\algorithms\index.js:73:10)
at Context.<anonymous> (test\unit\security\ecdh-test.js:108:32)
Could someone please let me know if I'm missing something here?
Thanks

Related

Implementing JWE encryption for a JWS signed token in Node.JS with Jose 4.11

I have difficulty manipulating the Jose Node.JS documentation to chain the creation of a JWS and JWE. I cannot find the proper constructor for encryption. It looks like I can only encrypt a basic payload not a signed JWS.
Here is the code sample I try to fix to get something that would look like
const jws = await createJWS("myUserId");
const jwe = await encryptAsJWE(jws);
with the following methods
export const createJWS = async (userId) => {
const payload = {
}
payload['urn:userId'] = userId
// importing key from base64 encrypted secret key for signing...
const secretPkcs8Base64 = process.env.SMART_PRIVATE_KEY
const key = new NodeRSA()
key.importKey(Buffer.from(secretPkcs8Base64, 'base64'), 'pkcs8-private-der')
const privateKey = key.exportKey('pkcs8')
const ecPrivateKey = await jose.importPKCS8(privateKey, 'ES256')
const assertion = await new jose.SignJWT(payload)
.setProtectedHeader({ alg: 'RS256' })
.setIssuer('demolive')
.setExpirationTime('5m')
.sign(ecPrivateKey)
return assertion
}
export const encryptAsJWE = async (jws) => {
// importing key similar to createJWS key import
const idzPublicKey = process.env.IDZ_PUBLIC_KEY //my public key for encryption
...
const pkcs8PublicKey = await jose.importSPKI(..., 'ES256')
// how to pass a signed JWS as parameter?
const jwe = await new jose.CompactEncrypt(jws)
.encrypt(pkcs8PublicKey)
return jwe
}
The input to the CompactEncrypt constructor needs to be a Uint8Array, so just wrapping the jws like so (new TextEncoder().encode(jws)) will allow you to move forward.
Moving forward then:
You are also missing the JWE protected header, given you likely use an EC key (based on the rest of your code) you should a) choose an appropriate EC-based JWE Key Management Algorithm (e.g. ECDH-ES) and put that as the public key import algorithm, then proceed to call .setProtectedHeader({ alg: 'ECDH-ES', enc: 'A128CBC-HS256' }) on the constructed object before calling encrypt.
Here's a full working example https://github.com/panva/jose/issues/112#issue-746919790 using a different combination of algorithms but it out to help you get the gist of it.

How to get transaction detail for smart contract, with web3.js

I am using Web3.js with Node JS. I need to get the transaction details for a smart contract under BNB. I developed my codes, but no matter what I did, I couldn't get it to work. Can you help me? Where am I doing wrong?
.env
bnbProvider = https://bsc-dataseed1.binance.org:443
web3crypto.js
const Web3 = require('web3')
const thisWeb3 = new Web3(new Web3.providers.HttpProvider(process.env.bnbProvider))
const minimumABI = [ABI OBJECT]
const tb = new thisWeb3.eth.Contract(minimumABI, process.env.contractId)
my function
exports.checkTransaction = async transactionId => {
const transactionData = await thisWeb3.eth.getTransaction(transactionId)
console.log(transactionData)
return transactionData
}
and Error
Error: Returned error: invalid argument 0: hex string has length 40, want 64 for common.Hash

Is it possible to create JWT tokens using key vault keys?

i want to us Key Vault key to create JWT token and then validate it.
Im using this code:
public static async Task<string> SignJwt()
{
var tokenHandler = new JwtSecurityTokenHandler();
var signinKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("this is my custom Secret key for authentication"));
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("id", "1") }),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(signinKey, SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
and it works fine. I was googling a lot and found this snippet for SigningCredentials using Identity extension nuget:
new SigningCredentials(new KeyVaultSecurityKey("https://myvault.vault.azure.net/keys/mykey/keyid", new KeyVaultSecurityKey.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)), "RS256")
{
CryptoProviderFactory = new CryptoProviderFactory() { CustomCryptoProvider = new KeyVaultCryptoProvider() }
});
But it is not clear for me, what really AuthenticationCallback is and how to implement that and if i will be able to use that in Azure in web app or azure function?
Firstly, a JWT token consists of 3 parts (Header, Payload and Signature) and all those 3 parts are Base64UriEncoded.
To get the Signature you need to generate header and payload, then combine them by dot.**
Below is the sample code to verify JWT using Azure kay Vault.
const key = await this.keyClient.getKey(this.KEY_NAME);
const cryptClient = new CryptographyClient(key, new DefaultAzureCredential());
const util =require('util')
const base64 = require('base64url');
const JWT=""
const jwtHeader = JWT.split('.')[0];
const jwtPayload = JWT.split('.')[1];
const jwtSignature = JWT.split('.')[2];
const signature = base64.toBuffer(jwtSignature)
const data = util.format('%s.%s', jwtHeader, jwtPayload);
const hash = crypto.createHash('sha256');
const digest = hash.update(data).digest()
const verified =await cryptClient.verify("RS256",digest,signature)
Here are few SO threads with related discussions. SO1, SO2 and SO3

Using node.js crypto to verify signatures

I am trying to use AWS lambda to verify signatures created with sec256r1 in swift.
Message: "some text to sign"
Has been hashed with sha256 too
signatures will be in base64
encoding:MEYCIQCPfWhpzxMqu3gZWflBm5V0aetgb2/S+SGyGcElaOjgdgIhALaD4lbxVwa8HUUBFOLz+CGvIioDkf9oihSnXHCqh8yV
and public key will look like so:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXIvPbzLjaPLd8jgiv1TL/X8PXpJN
gDkGRj9U9Lcx1yKURpQFVavcMkfWyO8r7JlZNMax0JKfLZUM1IePRjHlFw==
-----END PUBLIC KEY-----
To clarify,
I am trying to use lambda to verify signatures that come from the client side, and encrypt data with their public key if need be.
Here is code:
const crypto = require('crypto');
const verify = crypto.createVerify('SHA256');
verify.write('some text to sign');
verify.end();
const l1 = "-----BEGIN PUBLIC KEY-----\n"
const l2 =
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXIvPbzLjaPLd8jgiv1TL/X8PXpJNgDkGRj9U9Lcx1yKURpQFVavcMkfWyO8r7JlZNMax0JKfLZUM1IePRjHlFw=="
const l3 = "\n-----END PUBLIC KEY-----"
const publicKey = l1 + l2 + l3
const signature = "MEYCIQCPfWhpzxMqu3gZWflBm5V0aetgb2/S+SGyGcElaOjgdgIhALaD4lbxVwa8HUUBFOLz+CGvIioDkf9oihSnXHCqh8yV";
console.log(verify.verify(publicKey, signature));// Prints: true or false
Here's how to inegrate with Nodejs.Crypto. First, the RSA private and public keys need to be generated. There are several ways to do that, here's an a way to do this online with encrypt.JS. You can use getSignatureByInput function below after private and public keys have been stored into the filesystem which generates a unique signature given a string input:
const crypto = require('crypto')
const fs = require('fs')
const getSignatureByInput = (input) => {
let privatePem = fs.readFileSync('PRIVATE_KEY_FILE_PATH_GOES_HERE')
let key = privatePem.toString('ascii')
let sign = crypto.createSign('RSA-SHA256')
sign.update(input)
let signature = sign.sign(key, 'hex')
return signature
}
Thereafter, to verify a signature, you can use the following function:
const getSignatureVerifyResult = (input) => {
let signatureSignedByPrivateKey = getSignatureByInput(input)
let pem = fs.readFileSync('PUBLIC_KEY_FILE_PATH_GOES_HERE')
let publicKey = pem.toString('ascii')
const verifier = crypto.createVerify('RSA-SHA256')
verifier.update(input, 'ascii')
const publicKeyBuf = new Buffer(publicKey, 'ascii')
const signatureBuf = new Buffer(signatureSignedByPrivateKey, 'hex')
const result = verifier.verify(publicKeyBuf, signatureBuf)
return result;
}
getSignatureVerifyResult will return true/false depending on whether the signature are verified. Keep in mind that there's a plethora of algorithms to choose when it comes to signing.
Please see the fuller solution at this StackOverflow post which shows how to use the verify.update() and verify.verify() methods in node.js.

custom private key with crypto in node.js

from: https://nodejs.org/api/crypto.html#crypto_class_ecdh
const alice_key = alice.generateKeys();
will generate a random private key and the corresponding public key.
But I would like to set my own private key: e8f32e723decf...
If I use :
alice.setPrivateKey("e8f32e723decf");
the object alice_key is not affected, so later:
const bob_secret = bob.computeSecret(alice_key, 'hex', 'hex');
will be wrong. Is there a way to do something like:
const alice_key = alice.generateKeys("e8f32e723decf");
First of all I suppose your hex string is missing a leading 0, so it should be 0e8f32e723decf.
Then it depends on your node.js version, the implementation of ECDH.setPrivateKey() changed from 5.1 to 5.2
node.js 5.0
You need to generate the keys and override them
You need to have the public and the private key
Working online example
const crypto = require('crypto');
// this is just to generate a private/public key pair
const warmup = crypto.createECDH('secp521r1');
warmup.generateKeys();
const warmup_private_key = warmup.getPrivateKey();
const warmup_public_key = warmup.getPublicKey();
// convert it to hex string to match the example
// you would store these strings somewhere I guess
private_key = warmup_private_key.toString('hex');
public_key = warmup_public_key.toString('hex');
// now let's create the ciphers
const alice = crypto.createECDH('secp521r1');
const bob = crypto.createECDH('secp521r1');
----------
// Bob gets created keys
bob.generateKeys();
// Generate Alice's keys - that's really annoying since you will override it
alice.generateKeys();
// now set the keys:
alice.setPrivateKey(private_key, "hex");
alice.setPublicKey(public_key, "hex");
// Exchange and generate the secret...
const alice_secret = alice.computeSecret(bob.getPublicKey());
const bob_secret = bob.computeSecret(alice.getPublicKey());
console.log("alice's shared secret: " + alice_secret.toString('hex') + "\n");
console.log("bob's shared secret: " + bob_secret.toString('hex') + "\n");
console.log('shared secrets match: ' + alice_secret.equals(bob_secret));
node.js >= 5.2
const crypto = require('crypto');
const alice = crypto.createECDH('secp256k1');
const bob = crypto.createECDH('secp256k1');
bob.generateKeys();
alice.setPrivateKey('0e8f32e723decf', 'hex');
const alice_secret = alice.computeSecret(bob.getPublicKey());
const bob_secret = bob.computeSecret(alice.getPublicKey());
console.log(alice_secret.equals(bob_secret));

Resources