I am trying to create the following flow:
- Create a keypair on the client
- Send the public key to the server (nodejs)
- Encrypt a string on the server using a WebCryptoAPI polyfill
- Send the encrypted data back to the client for decryption
I am struggling (for a long time time) with the data types.
Below is the code, first to generate the keys (client):
// some reusable settings objects
const crypto = window.crypto.subtle;
let publicKeyToExport = {};
let privateKeyToStore = {};
// function called to create a keypair
const generateKeypair = () => {
crypto.generateKey({
name : 'RSA-OAEP',
modulusLength : 2048, //can be 1024, 2048, or 4096
publicExponent : new Uint8Array([0x01, 0x00, 0x01]),
hash : {name: 'SHA-256'}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
}, true, ['encrypt', 'decrypt']
).then((key) => {
publicKeyToExport = key.publicKey;
privateKeyToStore = key.privateKey;
console.log(key);
}).catch((err) => {
console.error(err);
});
};
Then to export:
// function to export the generate publicKey
const exportPublicKey = (publicKey) => {
crypto.exportKey('jwk', publicKey)
.then((keydata) => {
fetch('/key2', {
method : 'POST',
mode : 'cors',
body : JSON.stringify(keydata),
headers : new Headers({
'Content-Type' : 'application/json'
})
}).then(res => res.json())
.catch(err => console.error(err))
.then(res => console.log(res));
console.log(keydata);
})
.catch((err) => {
console.log(err);
});
};
Save the key:
app.post('/key2', (req, res) => {
webcrypto.subtle.importKey(
'jwk', req.body,
{
name : 'RSA-OAEP',
hash : {name : 'SHA-256'},
},
false,
['encrypt']
).then((publicKey) => {
keyStorage.setItem('alicePubKey', publicKey);
if(publicKey == keyStorage.getItem('alicePubKey'));
res.json({ 'success' : 'key received and saved' });
console.log('saved key from client: ' + publicKey);
return;
})
.catch((err) => {
console.error(err);
});
});
Encrypt on server:
app.get('/challenge', (req, res) => {
let challengeFromServer = null;
let key = keyStorage.getItem('alicePubKey');
let buf = new Buffer.from('decryptthis!');
webcrypto.subtle.encrypt(
{
name : 'RSA-OAEP'
}, key, buf
)
.then((encrypted) => {
console.log('challenge created: ' + encrypted);
res.json({'challenge' : new Uint8Array(encrypted) })
})
.catch((err) => {
console.error(err);
})
Get encrypted data and decrypt - not working :)
const requestChallenge = () => {
fetch('/challenge')
.then((res) => {
return res.json();
})
.then((data) => {
console.log(data);
console.log(ArrayBuffer.isView(data.challenge))
console.log(new ArrayBuffer(data.challenge))
crypto.decrypt({
name : 'RSA-OAEP'
}, privateKeyToStore, new ArrayBuffer(data.challenge))
.then((decrypted)=>{
console.log(decrypted)
})
.catch(err => console.error(err));
})
.catch(err => console.error(err));
};
The following lines are the issue I think!
console.log(ArrayBuffer.isView(data.challenge)) // false
console.log(new ArrayBuffer(data.challenge)) // empty
Small update:
res.json(
{'challenge' : encrypted , // {} empty
'uint' : new Uint8Array(encrypted), // {0: 162, 1: 252, 2: 113, 3: 38, .......
'stringify' : JSON.stringify(encrypted), // "{}" empty
'toString' : encrypted.toString() // "[object ArrayBuffer]"
});
I am trying to create the following flow:
- Create a keypair on the client
- Send the public key to the server (nodejs)
- Encrypt a string on the server using a WebCryptoAPI polyfill https://github./PeculiarVentures/node-webcrypto-ossl
- Send the encrypted data back to the client for decryption
I am struggling (for a long time time) with the data types.
Below is the code, first to generate the keys (client):
// some reusable settings objects
const crypto = window.crypto.subtle;
let publicKeyToExport = {};
let privateKeyToStore = {};
// function called to create a keypair
const generateKeypair = () => {
crypto.generateKey({
name : 'RSA-OAEP',
modulusLength : 2048, //can be 1024, 2048, or 4096
publicExponent : new Uint8Array([0x01, 0x00, 0x01]),
hash : {name: 'SHA-256'}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
}, true, ['encrypt', 'decrypt']
).then((key) => {
publicKeyToExport = key.publicKey;
privateKeyToStore = key.privateKey;
console.log(key);
}).catch((err) => {
console.error(err);
});
};
Then to export:
// function to export the generate publicKey
const exportPublicKey = (publicKey) => {
crypto.exportKey('jwk', publicKey)
.then((keydata) => {
fetch('/key2', {
method : 'POST',
mode : 'cors',
body : JSON.stringify(keydata),
headers : new Headers({
'Content-Type' : 'application/json'
})
}).then(res => res.json())
.catch(err => console.error(err))
.then(res => console.log(res));
console.log(keydata);
})
.catch((err) => {
console.log(err);
});
};
Save the key:
app.post('/key2', (req, res) => {
webcrypto.subtle.importKey(
'jwk', req.body,
{
name : 'RSA-OAEP',
hash : {name : 'SHA-256'},
},
false,
['encrypt']
).then((publicKey) => {
keyStorage.setItem('alicePubKey', publicKey);
if(publicKey == keyStorage.getItem('alicePubKey'));
res.json({ 'success' : 'key received and saved' });
console.log('saved key from client: ' + publicKey);
return;
})
.catch((err) => {
console.error(err);
});
});
Encrypt on server:
app.get('/challenge', (req, res) => {
let challengeFromServer = null;
let key = keyStorage.getItem('alicePubKey');
let buf = new Buffer.from('decryptthis!');
webcrypto.subtle.encrypt(
{
name : 'RSA-OAEP'
}, key, buf
)
.then((encrypted) => {
console.log('challenge created: ' + encrypted);
res.json({'challenge' : new Uint8Array(encrypted) })
})
.catch((err) => {
console.error(err);
})
Get encrypted data and decrypt - not working :)
const requestChallenge = () => {
fetch('/challenge')
.then((res) => {
return res.json();
})
.then((data) => {
console.log(data);
console.log(ArrayBuffer.isView(data.challenge))
console.log(new ArrayBuffer(data.challenge))
crypto.decrypt({
name : 'RSA-OAEP'
}, privateKeyToStore, new ArrayBuffer(data.challenge))
.then((decrypted)=>{
console.log(decrypted)
})
.catch(err => console.error(err));
})
.catch(err => console.error(err));
};
The following lines are the issue I think!
console.log(ArrayBuffer.isView(data.challenge)) // false
console.log(new ArrayBuffer(data.challenge)) // empty
Small update:
res.json(
{'challenge' : encrypted , // {} empty
'uint' : new Uint8Array(encrypted), // {0: 162, 1: 252, 2: 113, 3: 38, .......
'stringify' : JSON.stringify(encrypted), // "{}" empty
'toString' : encrypted.toString() // "[object ArrayBuffer]"
});
Share
Improve this question
edited Mar 23, 2018 at 18:04
dendog
asked Mar 23, 2018 at 17:11
dendogdendog
3,3685 gold badges33 silver badges70 bronze badges
4
- to encrypt and decrypt you need to have a shared key between both partie – Abslen Char Commented Mar 23, 2018 at 17:14
- assymetric keys work just fine @AbdeslemCharif – dendog Commented Mar 23, 2018 at 17:43
- Why not just use HTTPS, it provided plete e2e encryption. – zaph Commented Mar 23, 2018 at 17:49
- @zaph this is part of a bigger project for authentication – dendog Commented Mar 23, 2018 at 17:50
2 Answers
Reset to default 4SOLVED!
The issue was with data types.
The way to solve this if anyone has the issue is to ensure on your server you send your ciphertext as a Buffer, my express app:
res.write(new Buffer(encrypted), 'binary')
res.end(null, 'binary')
And on the client receive it and decode, as below:
const decryptedReadable = new TextDecoder().decode(decrypted)
Happy coding.
Consider taking a look at a higher level library that deals with the exchange problems for you; for example js-jose https://github./square/js-jose/tree/master/examples or PKIjs https://pkijs/examples/CMSEnvelopedExample.html.