admin管理员组

文章数量:1430533

I am trying to create the following flow:

  1. Create a keypair on the client
  2. Send the public key to the server (nodejs)
  3. Encrypt a string on the server using a WebCryptoAPI polyfill
  4. 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:

  1. Create a keypair on the client
  2. Send the public key to the server (nodejs)
  3. Encrypt a string on the server using a WebCryptoAPI polyfill https://github./PeculiarVentures/node-webcrypto-ossl
  4. 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
Add a ment  | 

2 Answers 2

Reset to default 4

SOLVED!

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.

本文标签: javascriptEncrypt in Nodejs and decrypt on the client using WebCrypto APIStack Overflow