RSA encryption and decryption with 4js Genero & Java

Published on

in

, , , ,

I hope this log is of your interest and that it saves you headaches, hours of reading and testing.

Si estás buscando esta información en Español, haz clic aquí.

It all begins with a requirement: encrypt the response of a web service made in 4js Genero and whose reply is to be read, and therefore decrypted, by a program made in Java using RSA keys.

To my surprise, totally opposite to my first thoughts, because of my own experience and statistics, the easiest part of this job was done in Genero. I had to make three technologies work together for this exercise:

  • openssl to create the public and private RSA keys
  • Genero to use those keys to encrypt and decrypt -for testing purposes- what was encrypted with the public key
  • Java to handle the encrypted strings and use the private key to decrypt them.

Now, let’s go step by step on each one.

Creating the RSA keys

RSA is an algorithm that uses a pair of keys, one public, one private. In order to comply with the requirement, the public key will be used to encrypt text strings and the private key to decrypt them.

Genero, contrary to Java, does not count with elements in its platform to create such keys, for that reason I decided to generata the keys using openssl, a tool I do have in my development environtment and also present in a Linux server, where I will deploy the solution.

Initially, I created the RSA keys using the PEM format, following the directions in this guide:

https://rietta.com/blog/openssl-generating-rsa-key-from-command/

However, because of my tests, I had to create them again because the test program in Java I made (got actually) to try decrypting the strings needed the RSA key in PKCS8 format, hence I had to follow this new guide:

https://kb.vander.host/security/how-to-generate-rsa-public-and-private-key-pair-in-pkcs8-format/

Encryption and decryption with Genero

As I commented earlier, Genero (surprisingly, because usually this is the end, I get most of the problems from) can work with RSA keys in PEM or PCKS8 format without telling it in advance what format of key you are dealing with.

The encryption and decryption functions are packed in the XML library. There is no need to instantiate any particylar class, the library contains a static class Encryption that exposes the methods:

RSAEncrypt(key, source_string) RETURNING encoded_string

RSADecrypt(key, encoded_string) RETURNING decoded_string

key is of type STRING and contains the location of the RSA key file.

source_string is of type STRING and contains the base string (text) to be encrypted.

encoded_string is of type STRING and contains an encrypted string to be decrypted.

Evidently the output of each method is of type STRING and contain the encrypted and decrypted strings, correspondingly.

The following is a sample demonstration program. The test base string is «3200.00» that you can see hardcoded, as well as the location of the RSA keys. Note the public key is used for encryption and the private key for decryption:

IMPORT XML

MAIN
DEFINE  v_public_rsa    STRING
       ,v_private_rsa   STRING
       ,v_base_tring    STRING
       ,v_encrypted     STRING
       ,v_decrypted     STRING

   DISPLAY "Encryption with RSA"
   LET v_public_rsa = "publickey.crt"
   LET v_private_rsa = "pkcs8.key"
   LET v_base_tring = "3200.00"

   LET v_encrypted = xml.Encryption.RSAEncrypt(v_public_rsa, v_base_tring)

   DISPLAY "Encrypted string: ", v_encrypted

   DISPLAY "Decryption with RSA"

   LET v_decrypted = xml.Encryption.RSADecrypt(v_private_rsa, v_encrypted)

   DISPLAY "Decrypted string: ", v_decrypted

END MAIN 

This is the output of the sample program:

Encryption with RSA
Encrypted string: Qq8khXMwTxhkc5Wc8svCjrYHmAtQgxRJ978+nnVxmIFqGeaoPlF/REE49hROHMtXrA/RITaW/SZ4gMu2mD0PjketXn3MH/yGGcD21awGM1tKsO0nf4+/HQr9pE4mQ0hAnTLbkwYHZ4iEcjxypsmfoeaPpjj0tMdlxSaAlc4+w8J4krGYG/u8LpMbhxM12u1T+AGY1Y+42K6jtP4Bejyut7OlVUBU71tJjSYwTbZzXVR40xexDz9fTZQG5OemZAZh233tuVMqZEoHX2QZcB9iEDE7PGQIMYgsGuy91x7peE1CHtpgh9isCPxuOxxy++w19Lb0ikTWhsqJ/8yBr/8B7Q==
Decryption with RSA
Decrypted string: 3200.00

As you can see, the resulting encrypted string is a way larger string compared to the original one and it is output as a Base64 encoded string.

Everything OK so far. Now, let’s move to Achilles’ heel… Java.

Decryption with Java

This is the part I thought would be the easiest and fastest, because… it is Java. You my dear reader probably will agree that this platform has all sort of tools, using an RSA key should be a piece of cake. It should be a similar program, a class that allows to use the private key, take it and receive the encrypted string to decrypt it, right?

Before delving into Java, I decided to try the public and private keys using an online service. This is the web page I found the easiest to use:

https://www.devglan.com/online-tools/rsa-encryption-decryption

I found it amusing that this site uses Java as the back end, so it was perfect for a flight-test. How did I know it uses Java? Quite simple, during my tests I provoked an error, and instead of getting and encrypted/decrypted string, I got a Java exception message.

I tried encrypting the same string using the public RSA key. For demonstration purposes, I will share the public and private keys, do not use them for production.

This is the public key:

I copied and pasted the public key text in the corresponding text area on the web site:

And as you can see, my problems began.

Why is it that Genero could encrypt the text with the key as it was, and the web site (that uses Java) couldn’t?

Here is the hour-and-headache saving part (given you found my log quickly): You have to remove the head and the tail:

-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----

Then the «adjusted» key must look like this:

And as you can see now, in the output text area we finally got a Base64-encoded string as expected:

bQALzE/FVRXIafWrDh7Ollw8Vn3AxhQDj9kPnHN2tFZqhVgpfeR1Ue+Hs/4+SUipHT0VH9jjk4ipcN5OMpTtpHd0S52+D5haIn+BlnhzdwyTMvaBszwBfEztKhRWWH9yEake8ffCYZwl030FU8tCJuZQ9B3sALAHn4CuiPdb0YAkJMvY19EfjPIAQ+Zr4zsuc4dbFByO/XeeshWUodzNuWqstBPehq3nQG43ne8JRKfy6+mq0VAzU2ujhdlEzkIGQqPoflwV0R6zROtiYp98jStvD337cGd3SNSE8T2Jx6YFnTtEIiBUSVXVIvx5lQs55HVQecmc+yoFWjyUKNCz6w==

Naturally, the next step in line was to decrypt that string using the private key, so I copied and pasted it in the text area for that purpose:

Same error I had with the public key. Genero was able to use the private key as it was generated by openssl. Anyways, I removed the head and the tail and tried again:

Perfect. So far, so good. Now that I validated that encryption and decryption was possible outside Genero, it was time to try directly with Java. This is the source code for the testing program:

import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;

public class RSADecode {

  public static void main(String[] args) throws Exception {
      
    String privateKeyFile = "pkcs8.key";
        String ciphertext = "RQ4yuY7m1bem31cDcjgi8L/wJZrXoPkKI7fvsuLMFa71RcMWvf3yVzSCU7Ef32EoiqvtOVVJAngiMEq1LYK2CR2FKksKeetajeMB0nV2u87RxJU4nWlvHXsD8H+BXgostfkAqbueP/EI2G8vNeVcjv+7/pcjnzFGRQTtG1iBN/2gm8X/G9cjYUNsd/cxFf4C9pQxEvK0J5sFO13pdulD5hzK/eB61iilYDQ1cmCWZpYHB+rTPaRRXFMQviSwV+G88AAnBBcM8Lbo/GKexZANHCSvR0j8JUKHuVHLieUa6JWOZqGyfznaBEnkx3xhi0qGbSueJFOKum/K47eLq1wL8Q==";

        // Read the private key from the file
        byte[] encodedPrivateKey = readFile(privateKeyFile);

        // Decode the private key from PEM format
        encodedPrivateKey = Base64.getMimeDecoder().decode(encodedPrivateKey);

        // Create a key specification from the decoded private key
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);

        // Create a key factory to generate a PrivateKey object
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        // Decode the ciphertext
        byte[] decodedCiphertext = Base64.getDecoder().decode(ciphertext);

        // Initialize the cipher with the private key and perform the decryption
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] plaintext = cipher.doFinal(decodedCiphertext);

        // Print the decrypted message
        System.out.println("Decrypted message: " + new String(plaintext, StandardCharsets.UTF_8));
    }

    private static byte[] readFile(String filename) throws Exception {
        try (FileInputStream fis = new FileInputStream(filename)) {
            byte[] data = new byte[fis.available()];
            fis.read(data);
            return data;
        }
    }

}

This is what I got as output during execution:

The file that contains the private key I set for the program is the same I use for Genero, hence, it includes the head and tail lines I had to remove before using it on the website. So, I created a new private key file, copy from the original but without the head and tail lines. I adjusted the program to use that file and tried again:

Excellent, it worked.

There is something else I want to share. The RSA keys are express in more than one line inside the files:

You can use the keys in several lines or a single line in Java, both ways work.

Hey… up to this point, did you notice that the encrypted strings in Genero and Java are not the same despite the source string and the public key did not change?

Before reading any documentation, I tried decrypting in Genero and Java, the encrypted strings created outside each other, having the same base string «3200.00» and the same private key. It worked like a charm.

Why?

RSA is not a HASH algorithm, therefore, even if you use the same input, it will not output the same result, that would break the algorithm security. So, it is correct that the output string changes every time the algorithm runs with the same input.

So, time to finish the log. I hope this works for you.

Si estás buscando esta información en Español, haz clic aquí.

One response to “RSA encryption and decryption with 4js Genero & Java”

  1. […] If you want to read this in English, click here. […]

Deja un comentario

Descubre más desde Crónicas de Programación

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo