Issues in RSA encryption in Java class

2019-04-02 13:17发布

问题:

public class MyEncrypt {

    public void saveToFile(String fileName, BigInteger mod, BigInteger exp) throws IOException {
        ObjectOutputStream oout = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
          try {
                oout.writeObject(mod);
                oout.writeObject(exp);
          } catch (Exception e) {
          throw new IOException("Unexpected error", e);
          } finally {
            oout.close();
          }
    }

    public static void main(String[] args) throws Exception {
                MyEncrypt myEncrypt = new MyEncrypt();
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(128);
        KeyPair kp = kpg.genKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();
        KeyFactory fact = KeyFactory.getInstance("RSA");        
        RSAPublicKeySpec pub = fact.getKeySpec(kp.getPublic(), RSAPublicKeySpec.class);
        RSAPrivateKeySpec priv = fact.getKeySpec(kp.getPrivate(), RSAPrivateKeySpec.class);

        myEncrypt.saveToFile("public.key", pub.getModulus(), pub.getPublicExponent());
        myEncrypt.saveToFile("private.key", priv.getModulus(), priv.getPrivateExponent());
        String encString = myEncrypt.bytes2String(myEncrypt.rsaEncrypt("pritesh".getBytes()));
        System.out.println("encrypted : " + encString);
        String decString = myEncrypt.bytes2String(myEncrypt.rsaDecrypt(encString.getBytes()));
        System.out.println("decrypted : " + decString);
    }   

    PublicKey readKeyFromFile(String keyFileName) throws Exception {
      InputStream in = new FileInputStream(keyFileName);
      ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in));
      try {
        BigInteger m = (BigInteger) oin.readObject();
        BigInteger e = (BigInteger) oin.readObject();
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
        KeyFactory fact = KeyFactory.getInstance("RSA");
        PublicKey pubKey = fact.generatePublic(keySpec);
        return pubKey;
      } catch (Exception e) {
        throw new RuntimeException("Spurious serialisation error", e);
      } finally {
        oin.close();
      }
    }

    PrivateKey readPrivateKeyFromFile(String keyFileName) throws Exception {
      InputStream in = new FileInputStream(keyFileName);
      ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in));
      try {
        BigInteger m = (BigInteger) oin.readObject();
        BigInteger e = (BigInteger) oin.readObject();
        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, e);
        KeyFactory fact = KeyFactory.getInstance("RSA");
        PrivateKey pubKey = fact.generatePrivate(keySpec);
        return pubKey;
      } catch (Exception e) {
        throw new RuntimeException("Spurious serialisation error", e);
      } finally {
        oin.close();
      }
    }

    public byte[] rsaEncrypt(byte[] data) throws Exception {
      byte[] src = new byte[] { (byte) 0xbe, (byte) 0xef };
      PublicKey pubKey = this.readKeyFromFile("public.key");
      Cipher cipher = Cipher.getInstance("RSA");
      cipher.init(Cipher.ENCRYPT_MODE, pubKey);
      byte[] cipherData = cipher.doFinal(data);
      return cipherData;
    }

    public byte[] rsaDecrypt(byte[] data) throws Exception {
      byte[] src = new byte[] { (byte) 0xbe, (byte) 0xef };
      PrivateKey pubKey = this.readPrivateKeyFromFile("private.key");
      Cipher cipher = Cipher.getInstance("RSA");
      cipher.init(Cipher.DECRYPT_MODE, pubKey);
      byte[] cipherData = cipher.doFinal(data);
      return cipherData;
    }

    private String bytes2String(byte[] bytes) {
        StringBuilder string = new StringBuilder();
        for (byte b: bytes) {
                String hexString = Integer.toHexString(0x00FF & b);
                string.append(hexString.length() == 1 ? "0" + hexString : hexString);
        }
        return string.toString();
    }
}

I am getting this error:

Exception in thread "main" java.security.InvalidParameterException: RSA keys must be at least 512 bits long
    at sun.security.rsa.RSAKeyPairGenerator.initialize(RSAKeyPairGenerator.java:70)
    at java.security.KeyPairGenerator$Delegate.initialize(KeyPairGenerator.java:631)
    at java.security.KeyPairGenerator.initialize(KeyPairGenerator.java:340)
    at MyEncrypt.main(MyEncrypt.java:42)

I have make this class from http://www.javamex.com/tutorials/cryptography/rsa_encryption_2.shtml example


public class MyEncrypt {

static final String HEXES = "0123456789ABCDEF"; 
byte[] buf = new byte[1024];      

public void saveToFile(String fileName, BigInteger mod, BigInteger exp) throws IOException {
    ObjectOutputStream oout = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
      try {
            oout.writeObject(mod);
            oout.writeObject(exp);
      } catch (Exception e) {
      throw new IOException("Unexpected error", e);
      } finally {
        oout.close();
      }
}

public static void main(String[] args) throws Exception {       
            MyEncrypt myEncrypt = new MyEncrypt();
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(2048);
    KeyPair kp = kpg.genKeyPair();
    RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
    RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();
    KeyFactory fact = KeyFactory.getInstance("RSA");        
    RSAPublicKeySpec pub = fact.getKeySpec(kp.getPublic(), RSAPublicKeySpec.class);
    RSAPrivateKeySpec priv = fact.getKeySpec(kp.getPrivate(), RSAPrivateKeySpec.class);

    myEncrypt.saveToFile("public.key", pub.getModulus(), pub.getPublicExponent());
    myEncrypt.saveToFile("private.key", priv.getModulus(), priv.getPrivateExponent());
    String encString = myEncrypt.rsaEncrypt("pritesh");
    System.out.println("encrypted : " + encString);
    String decString = myEncrypt.rsaDecrypt(encString);
    System.out.println("decrypted : " + decString);

    String main_file_path = "resume.doc";                
    String main_encrypt_file_path = "encrypt.doc";
    String main_decrypt_file_path = "decrypt.doc";

    myEncrypt.rsaEncrypt(new FileInputStream(main_file_path),new FileOutputStream(main_encrypt_file_path));
            // Decrypt
    myEncrypt.rsaDecrypt(new FileInputStream(main_encrypt_file_path),new FileOutputStream(main_decrypt_file_path));
}   

PublicKey readKeyFromFile(String keyFileName) throws Exception {
  InputStream in = new FileInputStream(keyFileName);
  ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in));
  try {
    BigInteger m = (BigInteger) oin.readObject();
    BigInteger e = (BigInteger) oin.readObject();
    RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PublicKey pubKey = fact.generatePublic(keySpec);
    return pubKey;
  } catch (Exception e) {
    throw new RuntimeException("Spurious serialisation error", e);
  } finally {
    oin.close();
  }
}

PrivateKey readPrivateKeyFromFile(String keyFileName) throws Exception {
  InputStream in = new FileInputStream(keyFileName);
  ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in));
  try {
    BigInteger m = (BigInteger) oin.readObject();
    BigInteger e = (BigInteger) oin.readObject();
    RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, e);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PrivateKey pubKey = fact.generatePrivate(keySpec);
    return pubKey;
  } catch (Exception e) {
    throw new RuntimeException("Spurious serialisation error", e);
  } finally {
    oin.close();
  }
}

public String rsaEncrypt(String plaintext) throws Exception {      
  PublicKey pubKey = this.readKeyFromFile("public.key");
  Cipher cipher = Cipher.getInstance("RSA");
  cipher.init(Cipher.ENCRYPT_MODE, pubKey);
  byte[] ciphertext = cipher.doFinal(plaintext.getBytes("UTF-8"));
  return this.byteToHex(ciphertext);
}

public void rsaEncrypt(InputStream in, OutputStream out) throws Exception {
    try {                    
        PublicKey pubKey = this.readKeyFromFile("public.key");
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey); 
        // Bytes written to out will be encrypted
        out = new CipherOutputStream(out, cipher);

        // Read in the cleartext bytes and write to out to encrypt
        int numRead = 0;
        while ((numRead = in.read(buf)) >= 0){
            out.write(buf, 0, numRead);
        }
        out.close();
    }
    catch (java.io.IOException e){
        e.printStackTrace();
    }
}

public void rsaDecrypt(InputStream in, OutputStream out) throws Exception {
    try {                 
        PrivateKey pubKey = this.readPrivateKeyFromFile("private.key");
        Cipher dcipher = Cipher.getInstance("RSA");
        dcipher.init(Cipher.DECRYPT_MODE, pubKey);
        // Bytes read from in will be decrypted
        in = new CipherInputStream(in, dcipher);

        // Read in the decrypted bytes and write the cleartext to out
        int numRead = 0;
        while ((numRead = in.read(buf)) >= 0) {
            out.write(buf, 0, numRead);
        }
        out.close();
    } catch (java.io.IOException e) {
         e.printStackTrace();
    }
}

public String rsaDecrypt(String hexCipherText) throws Exception {      
  PrivateKey pubKey = this.readPrivateKeyFromFile("private.key");
  Cipher cipher = Cipher.getInstance("RSA");
  cipher.init(Cipher.DECRYPT_MODE, pubKey);      
  String plaintext = new String(cipher.doFinal(this.hexToByte(hexCipherText)), "UTF-8");
  return plaintext;
}

public static String byteToHex( byte [] raw ) {
    if ( raw == null ) {
      return null;
    }
    final StringBuilder hex = new StringBuilder( 2 * raw.length );
    for ( final byte b : raw ) {
      hex.append(HEXES.charAt((b & 0xF0) >> 4))
         .append(HEXES.charAt((b & 0x0F)));
    }
    return hex.toString();
}

public static byte[] hexToByte( String hexString){
    int len = hexString.length();
    byte[] ba = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        ba[i/2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i+1), 16));
    }
    return ba;
}

}

It works well with text file but giving issue on files like docx and video any idea?

回答1:

The error says it all: you're initializing with a keyset of 128 and RSA expects at least 512.



回答2:

You have more than one problem:

  1. Java does not support RSA keys of size less than 512. 2048 bit is a better choice. So change the key length:

    kpg.initialize(2048);
    
  2. String.getBytes() is not the inverse operation to your bytes2String(). After encrypting you convert the bytes to a hexadecimal string. But then you convert this hexadecimal string to its ASCII representation before decrypting, which yields a byte-array that is too long. Instead, use something like this to convert the hexadecimal string back:

    private byte[] string2bytes(String s) {
        int len = s.length();
        byte[] res = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            res[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                            + Character.digit(s.charAt(i+1), 16));
        }
        return res;
    }
    

    and then call that instead of String.getBytes():

    // Note, this line is not completely fixed yet
    String decString = 
      myEncrypt.bytes2String(
        myEncrypt.rsaDecrypt(
          myEncrypt.string2bytes(encString)
        )
      );
    
  3. Finally you have the opposite problem. Your bytes2String() method does not reverse the String.getBytes() operation. You encrypted the output of "pritesh".getBytes(), so that is what you got out of the decrypt operation. Now you have to convert that back to a String. The String(byte[])-constructor will do that for you:

    String decString = 
      new String(
        myEncrypt.rsaDecrypt(
          myEncrypt.string2bytes(encString)
        )
      );
    


回答3:

Final ans

public class MyEncrypt {

public static final int AES_Key_Size = 128;

Cipher pkCipher, aesCipher;
byte[] aesKey;
SecretKeySpec aeskeySpec;

public static void main(String[] args) throws Exception {
    //MyEncrypt.createRSAKeys(); //call to generated RSA keys
    MyEncrypt secure = new MyEncrypt();
    // to encrypt a file
    secure.makeKey();
    secure.saveKey(new File("keys/ase.key"), "keys/public.key");
    secure.encrypt(new File("test/sample.pdf"), new File("test/encrypt_sample.pdf"));

    // to decrypt it again
    secure.loadKey(new File("keys/ase.key"), "keys/private.key");
    secure.decrypt(new File("test/encrypt_sample.pdf"), new File("test/decrypted_sample.pdf"));
}

/**
* Constructor: creates ciphers
*/
public MyEncrypt() throws GeneralSecurityException {
    // create RSA public key cipher
    pkCipher = Cipher.getInstance("RSA");
    // create AES shared key cipher
    aesCipher = Cipher.getInstance("AES");
}

public static void createRSAKeys() throws Exception {
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(512);
    KeyPair kp = kpg.genKeyPair();
    RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
    RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();
    KeyFactory fact = KeyFactory.getInstance("RSA");        
    RSAPublicKeySpec pub = fact.getKeySpec(kp.getPublic(), RSAPublicKeySpec.class);
    RSAPrivateKeySpec priv = fact.getKeySpec(kp.getPrivate(), RSAPrivateKeySpec.class);
    saveToFile("keys/public.key", pub.getModulus(), pub.getPublicExponent());
    saveToFile("keys/private.key", priv.getModulus(), priv.getPrivateExponent());
    System.out.println("RSA public & private keys are generated");
}

private static void saveToFile(String fileName, BigInteger mod, BigInteger exp) throws IOException {
    ObjectOutputStream oout = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
      try {
            oout.writeObject(mod);
            oout.writeObject(exp);
      } catch (Exception e) {
      throw new IOException("Unexpected error", e);
      } finally {
        oout.close();
      }
}

/**
* Creates a new AES key
*/
public void makeKey() throws NoSuchAlgorithmException {
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    kgen.init(AES_Key_Size);
    SecretKey key = kgen.generateKey();
    aesKey = key.getEncoded();
    aeskeySpec = new SecretKeySpec(aesKey, "AES");
}

/**
* Encrypts the AES key to a file using an RSA public key
*/
public void saveKey(File out, String publicKeyFile) throws IOException, GeneralSecurityException {
        try {
            // read public key to be used to encrypt the AES key
            byte[] encodedKey = new byte[(int)publicKeyFile.length()];
            new FileInputStream(publicKeyFile).read(encodedKey);

            // create public key
            //X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
            //KeyFactory kf = KeyFactory.getInstance("RSA");
            //PublicKey pk = kf.generatePublic(publicKeySpec);
            PublicKey pk = this.readPublicKeyFromFile(publicKeyFile);

            // write AES key
            pkCipher.init(Cipher.ENCRYPT_MODE, pk);
            CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), pkCipher);
            os.write(aesKey);
            os.close();    
        } catch (Exception e) {
            //throw new Exception("Saving key exception", e);    
        }

}

private PublicKey readPublicKeyFromFile(String keyFileName) throws Exception {
  InputStream in = new FileInputStream(keyFileName);
  ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in));
  try {
    BigInteger m = (BigInteger) oin.readObject();
    BigInteger e = (BigInteger) oin.readObject();
    RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PublicKey pubKey = fact.generatePublic(keySpec);
    return pubKey;
  } catch (Exception e) {
    throw new RuntimeException("Spurious serialisation error", e);
  } finally {
    oin.close();
  }
}

private PrivateKey readPrivateKeyFromFile(String keyFileName) throws Exception {
  InputStream in = new FileInputStream(keyFileName);
  ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in));
  try {
    BigInteger m = (BigInteger) oin.readObject();
    BigInteger e = (BigInteger) oin.readObject();
    RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, e);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PrivateKey pubKey = fact.generatePrivate(keySpec);
    return pubKey;
  } catch (Exception e) {
    throw new RuntimeException("Spurious serialisation error", e);
  } finally {
    oin.close();
  }
}

/**
* Decrypts an AES key from a file using an RSA private key
*/
public void loadKey(File in, String privateKeyFile) throws GeneralSecurityException, IOException {
            try {
                // read private key to be used to decrypt the AES key
               byte[] encodedKey = new byte[(int)privateKeyFile.length()];
               new FileInputStream(privateKeyFile).read(encodedKey);

               // create private key
               //PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
               //KeyFactory kf = KeyFactory.getInstance("RSA");
               //PrivateKey pk = kf.generatePrivate(privateKeySpec);
               PrivateKey pk = this.readPrivateKeyFromFile(privateKeyFile);

               // read AES key
               pkCipher.init(Cipher.DECRYPT_MODE, pk);
               aesKey = new byte[AES_Key_Size/8];
               CipherInputStream is = new CipherInputStream(new FileInputStream(in), pkCipher);
               is.read(aesKey);
               aeskeySpec = new SecretKeySpec(aesKey, "AES");     
            } catch (Exception e) {

            }

   }

/**
 * Encrypts and then copies the contents of a given file.
 */
public void encrypt(File in, File out) throws IOException, InvalidKeyException {
    aesCipher.init(Cipher.ENCRYPT_MODE, aeskeySpec);

    FileInputStream is = new FileInputStream(in);
    CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), aesCipher);

    copy(is, os);

    os.close();
}

/**
 * Decrypts and then copies the contents of a given file.
 */
public void decrypt(File in, File out) throws IOException, InvalidKeyException {
    aesCipher.init(Cipher.DECRYPT_MODE, aeskeySpec);

    CipherInputStream is = new CipherInputStream(new FileInputStream(in), aesCipher);
    FileOutputStream os = new FileOutputStream(out);

    copy(is, os);

    is.close();
    os.close();
}

/**
 * Copies a stream.
 */
private void copy(InputStream is, OutputStream os) throws IOException {
    int i;
    byte[] b = new byte[1024];
    while((i=is.read(b))!=-1) {
        os.write(b, 0, i);
    }
}

}