Problems encrypting a String using RSA algorithm i

2019-02-25 02:25发布

问题:

I'm trying to adopt RSA algorithm for encrypting String objects, but seems that BigInteger -> String and String -> BigInteger conversions do not work properly. Here's my code:

public class RSAEncryptor {

    private BigInteger n, d, e;

    public RSAEncryptor(int bitlen) {
        SecureRandom r = new SecureRandom();
        BigInteger p = new BigInteger(bitlen / 2, 100, r);
        BigInteger q = new BigInteger(bitlen / 2, 100, r);
        n = p.multiply(q);
        BigInteger m = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE));
        e = new BigInteger("3");
        while (m.gcd(e).intValue() > 1) {
            e = e.add(new BigInteger("2"));
        }
        d = e.modInverse(m);
    }

    public String encrypt(String message) {
        BigInteger plaintext = new BigInteger(message.getBytes());
        return new String(plaintext.modPow(e, n).toByteArray());
    }

    public String decrypt(String message) {
        BigInteger plaintext = new BigInteger(message.getBytes());
        return new String(plaintext.modPow(d, n).toByteArray());
    }
}

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        RSAEncryptor encryptor = new RSAEncryptor(64);

        String source = "1";
        String crypted = encryptor.encrypt(source);
        System.out.println(crypted);
        String decrypted = encryptor.decrypt(crypted);
        System.out.println(decrypted);
    }
}

It prints not what is expected, and a strange thing is that every time the output differs. Am I doing something wrong? Thanks

回答1:

Encrypted message is an arbitrary byte[], it's not guaranteed that it can be correctly converted to String with particular character encoding, so there is no need to convert it to String.

Another trick is that plaintext should be less than n, otherwise algorithm would produce garbage. When this condition is met, it works fine for me.

Yet another possible problem is that when the first byte of message in greater or equal than 0x80, a negative plaintext would be produced. It can be solved by prepending a zero byte to the bytes of message before converting it to BigInteger, and stripping that byte during inverse conversion.



回答2:

Hint: What does getBytes do that's different from the string value of message? Why aren't you just doing BigInteger(message); ?



回答3:

import java.math.BigInteger;     
import java.security.SecureRandom;

public class RSA {  
   private final static BigInteger one      = new BigInteger("1");  
   private final static SecureRandom random = new SecureRandom();

   private BigInteger privateKey;  
   private BigInteger publicKey;  
   private BigInteger modulus;   

   // generate an N-bit (roughly) public and private key  
   RSA(int N) {  
      BigInteger p = BigInteger.probablePrime(N/2, random);  
      BigInteger q = BigInteger.probablePrime(N/2, random);   
      BigInteger phi = (p.subtract(one)).multiply(q.subtract(one));       
      modulus    = p.multiply(q);                                          
      publicKey  = new BigInteger("65537");       
   // common value in practice = 2^16 + 1
      privateKey = publicKey.modInverse(phi);   
   }


   BigInteger encrypt(BigInteger message) {    
      return message.modPow(publicKey, modulus);    
   }  

   BigInteger decrypt(BigInteger encrypted) {     
      return encrypted.modPow(privateKey, modulus);    
   }    

   public String toString() {    
      String s = "";    
      s += "public  = " + publicKey  + "\n";   
      s += "private = " + privateKey + "\n";    
      s += "modulus = " + modulus;    
      return s;    
   }    

   public static void main(String[] args) {    
      int N = Integer.parseInt(args[0]);    
      RSA key = new RSA(N);    
      System.out.println(key);    
      // create random message, encrypt and decrypt   
      BigInteger message = new BigInteger(N-1, random);   
      //// create message by converting string to integer   
      // String s = "test";    
      // byte[] bytes = s.getBytes();    
      // BigInteger message = new BigInteger(s);    
      BigInteger encrypt = key.encrypt(message);    
      BigInteger decrypt = key.decrypt(encrypt);    
      System.out.println("message   = " + message);    
      System.out.println("encrpyted = " + encrypt);    
      System.out.println("decrypted = " + decrypt);    
   }     
}