Rsa encryption in javascript and decryption in java

If you need to encrypt data in your application at UI Client and pass it to backend safely, you can use RSA encryption algorithm. RSA is an asymmetric encryption algorithm. It uses a pair of keys, a public key and a private key, to encrypt and decrypt data. The keys are generated using a cryptographic algorithm. The public key can be shared with anyone, while the private key must be kept secret.

Generate RSA key pair

There are multiple ways you can generate RSA key pair we are going to use nodejs crypto module to generate RSA key pair.

const crypto = require('crypto');
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
    modulusLength: 2048,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs8',
        format: 'pem'
    }
});
console.log('Public Key:', publicKey);
console.log('Private Key:', privateKey);

The pem files will look like this

Public Pem File

-----BEGIN PUBLIC KEY-----
<public key details>
-----END PUBLIC KEY-----

Private Pem File

-----BEGIN PRIVATE KEY-----
<private key details>
-----END PRIVATE KEY-----

Encrypt data using public key in Javascript

Below code takes Public Key string and encrypt the data in Browser Javascript

function base64ToArrayBuffer(base64) {
    const binaryString = atob(base64);
    let bytes = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i++) {
        bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes.buffer;
}

function arrayBufferToBase64(buffer) {
    let binary = '';
    let bytes = new Uint8Array(buffer);
    const len = bytes.byteLength;
    for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
}

function publicPemToBuffer(pem) {
    const b64Lines = pem.replace('-----BEGIN PUBLIC KEY-----', '')
        .replace('-----END PUBLIC KEY-----', '')
        .replace(/\n/g, '');
    return base64ToArrayBuffer(b64Lines);
}

async function importPublicKey(pem) {
    var keyBuffer = publicPemToBuffer(pem);
    return await window.crypto.subtle.importKey(
        'spki',
        keyBuffer,
        {
            name: 'RSA-OAEP',
            hash: 'SHA-256'
        },
        true,
        ['encrypt']
    );
}

async function encryptString(publicKeyPem, plainText) {
    const publicKey = await importPublicKey(publicKeyPem);
    const encoded = new TextEncoder().encode(plainText);
    const encrypted = await  window.crypto.subtle.encrypt(
        {
            name: 'RSA-OAEP'
        },
        publicKey,
        encoded
    );
    return arrayBufferToBase64(new Uint8Array(encrypted));
}

const publicKey = `-----BEGIN PUBLIC KEY-----
<public key details>
-----END PUBLIC KEY-----`;

encryptString(publicKey, 'Hello World').then((encrypted) => {
    console.log(encrypted);
});

Decrypt data using private key in Java

Below code takes Private Key string and decrypt the data in Java

Note: Do not share or publish private key

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.util.Base64;

class Scratch {

    public static PrivateKey getPrivateKey (String privateKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String privateKeyPEM = privateKeyStr.replace("-----BEGIN PRIVATE KEY-----", "")
                .replaceAll(System.lineSeparator(), "")
                .replace("-----END PRIVATE KEY-----", "");
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyPEM);
        // Generate PrivateKey object
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

    public static String decrypt(String encryptedDataStr, String privateKeyStr) throws Exception {
        PrivateKey privateKey = getPrivateKey(privateKeyStr);
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
        OAEPParameterSpec oaepParams = new OAEPParameterSpec(
                "SHA-256",
                "MGF1",
                new MGF1ParameterSpec("SHA-256"),
                PSource.PSpecified.DEFAULT
        );
        cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
        byte[] encryptedData = Base64.getDecoder().decode(encryptedDataStr);
        byte[] decryptedData = cipher.doFinal(encryptedData);
        return new String(decryptedData);
    }

    public static void main(String[] args) throws Exception {
        String privateKeyString = "-----BEGIN PRIVATE KEY-----\n" +
                "<private key details>\n" +
                "-----END PRIVATE KEY-----";
        String encryptedDataStr = "<encrypted data>";
        String decryptedString = decrypt(encryptedDataStr, privateKeyString);
        System.out.println("Decrypted String: " + decryptedString);
    }
}