Guy Needham
Guy Needham

Reputation: 390

Encrypting text in Java using the RSA algorithm

I'm trying to use RSA encryption in Java.

I'm generating a public key and using it to encrypt text. My problem is that when I pass in the same text and the same key two time, the encrypted results are different. This means I can't use my encryption to test if entered text is equal to a stored result of a previous encryption.

This is my encryption class:

   import java.security.InvalidKeyException;
   import java.security.KeyPair;
   import java.security.KeyPairGenerator;
   import java.security.NoSuchAlgorithmException;
   import java.security.PublicKey;
   import java.util.Arrays;

   import javax.crypto.BadPaddingException;
   import javax.crypto.Cipher;
   import javax.crypto.IllegalBlockSizeException;
   import javax.crypto.NoSuchPaddingException;
   /**
    * The class encrypts text using an RSA algorithm.
    *
    */
   public class RSAEncryption {
       //RSA algorithm
       private final String ALGORITHM = "RSA";

/**
 * The generateKey method generates a public key for use in RSA encryption.
 * @return key a PublicKey for use in RSA encryption.
 */
public PublicKey generateKey(){
    KeyPair key = null;
    KeyPairGenerator keyGen;
    try {
        keyGen = KeyPairGenerator.getInstance(ALGORITHM); //gets instance of the alogrithm
        keyGen.initialize(1024); //a 1021 bit key
        key = keyGen.generateKeyPair(); //makes a pair
    } catch (NoSuchAlgorithmException e) {

        e.printStackTrace();
    }
    return key.getPublic(); //returns the public key. Private key never stored.
}

    /**
    * The encrypt method takes in text and a key and encrypts the text using the RSA encryption algorithm.
    * @params text a String, the text to encrypt.
    * @params key a PublicKey to use in encryption.
    * @returns encryptedText a byte array representing the result of the encryption.

public byte[] encrypt(String text, PublicKey key){
    byte[] encryptedText = null;
    Cipher cipher;
    try {
        cipher = Cipher.getInstance(ALGORITHM); //gets instance of RSA
        cipher.init(Cipher.ENCRYPT_MODE, key); //in encryption mode with the key
        encryptedText = cipher.doFinal(text.getBytes()); //carry out the encryption
    } catch (NoSuchAlgorithmException e) {          
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {            
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {         
        e.printStackTrace();
    } catch (BadPaddingException e) {           
        e.printStackTrace();
    } catch (InvalidKeyException e) {       
        e.printStackTrace();
    }
    return encryptedText; //return encrypted result
}

/**
 * The authenticate method checks if entered text, once encrypted, matches the stored byte[].
 * @param text a String, the text to encrypt.
 * @param stored a byte[], the result of a prior encryption.
 * @param key a PublicKey, a result of the generateKey method.
 * @return boolean, true if the text is valid, false otherwise.
 */

public boolean authenticate(String text, byte[] stored, PublicKey key){
    byte[] encryptText = encrypt(text,key); //encrypt the entered text
    return Arrays.equals(stored, encryptText); //check if the stored and entered byte[] are the same.
}
} 

I've written JUnit tests for this:

import static org.junit.Assert.*;

import java.security.PublicKey;
import java.util.Arrays;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;


public class RSAEncryptionTest {

RSAEncryption cipher;
String text;

@Before
public void setUp(){
    cipher = new RSAEncryption();
    text = "text";
}

@Test
public void testEncryptionGenerateKeyGeneratesANewKeyWhenCalled(){

    PublicKey key = cipher.generateKey();

    assertEquals(false,key.equals(cipher.generateKey()));
}

@Test
public void testEncryptionEncryptMethodRepeatablyEncrypts(){

    PublicKey key = cipher.generateKey();

    byte[] encrypted = cipher.encrypt(text,key);
    Assert.assertArrayEquals(encrypted, cipher.encrypt(text,key));
    //test fails
}


@Test
public void testEncryptionAuthenticateMethodReturnsTrueWhenValidTextPassedIn(){

    PublicKey key = cipher.generateKey();

    byte[] encrypted = cipher.encrypt(text,key);

    assertEquals(true,cipher.authenticate(text,encrypted,key));
            //test fails
}


@Test
public void testEncryptionAuthenticateMethodReturnsFalseWhenInvalidTextPassedIn(){

    PublicKey key = cipher.generateKey();

    byte[] encrypted = cipher.encrypt(text,key);

    assertEquals(false,cipher.authenticate("text1",encrypted,key)); 
} 

}

The second and third tests fail.

Any ideas how to repeatably encrypt text using RSA?

Upvotes: 2

Views: 4181

Answers (2)

Dev
Dev

Reputation: 12206

The output of an RSA cipher is not the same each time for a given plaintext when using an appropriate padding scheme (generally PKCS#1 or OAEP padding). Encrypting a given plaintext will result in different ciphertext each time. If the cipher generated the same output for a given input every time it would be a security vulnerability.

That being said you can force Java to use a non padded RSA cipher by using the spec "RSA/ECB/NOPADDING" for Cipher.getInstance(String). Doing so will result in your tests passing, but as I said earlier this is not very secure.

Upvotes: 2

mikey
mikey

Reputation: 5160

RSA is a public-key encryption scheme. It sounds like you actually want to use a hashing algorithm (e.g. SHA-256 or SHA-512). I say this because you say:

This means I can't use my encryption to test if entered text is equal to a stored result of a previous encryption.

If this is your goal, you should use a hashing algorithm. By design, RSA encryption should include a padding step to ensure that the ciphertext differs:

To avoid these problems, practical RSA implementations typically embed some form of structured, randomized padding into the value m before encrypting it. This padding ensures that m does not fall into the range of insecure plaintexts, and that a given message, once padded, will encrypt to one of a large number of different possible ciphertexts.

-- http://en.wikipedia.org/wiki/RSA_%28algorithm%29

Upvotes: 3

Related Questions