java - 使用 RSA 和另一个 key 加密消息

标签 java encryption

我正在尝试用 Java 加密和解密一条消息,但我总是遇到同样的错误:

java.security.InvalidKeyException: Key is too long for unwrapping

我正在尝试模拟从服务器向客户端发送加密消息。

第一步:客户端生成私钥和公钥。

2nd 客户端将公钥发送给服务器。

第三:服务器生成对称AES key 。

4t:服务器使用他的 AES key 加密他想发送给客户端的文本。

5e:服务端用从客户端收到的公钥加密AES key 。

6e:服务端将密文和加密后的AES key 发送给客户端。

7e:客户端用自己的私钥解密AES key 。

8e:客户端使用解密后的 AES key 解密从服务器接收到的文本。

public class Test {

    private static SecretKey secretKey;
    private static KeyPair keyPair;
    private static final String STRINGTOENCRYPT = "This is a test";
    private static String stringEncripted;
    private static String keyEncripted;
    public static final byte[] IV_PARAM = {0x00, 0x01, 0x02, 0x03,
            0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0A, 0x0B,
            0x0C, 0x0D, 0x0E, 0x0F};


    public static void main(String[] args) {
        generateKeys();
    }

    private static void generateKeys() {

        secretKey = generateSecretKey(128);
        keyPair = generateKeyPair(512);
        stringEncripted = encriptString(STRINGTOENCRYPT);
        keyEncripted = encriptKey(keyPair.getPublic());

        decryptString();

    }

    private static void decryptString() {
        Cipher cipher = null;
        Key keyDecrypted = null;
        try {
            cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.UNWRAP_MODE, keyPair.getPrivate());
            keyDecrypted = cipher.unwrap(keyEncripted.getBytes(), "AES", Cipher.SECRET_KEY);

            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(IV_PARAM);
            cipher.init(Cipher.DECRYPT_MODE, keyDecrypted, iv);
            byte[] stringDecryptedBytes = cipher.doFinal(stringEncripted.getBytes());
            String stringDecrypted = new String(stringDecryptedBytes);

            System.out.println(stringDecrypted);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private static KeyPair generateKeyPair(int lenght) {
        KeyPair keyPublicAndPrivate = null;

        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(lenght);
            keyPublicAndPrivate = keyGen.genKeyPair();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return keyPublicAndPrivate;

    }

    private static SecretKey generateSecretKey(int lenght) {


        SecretKey sKey = null;
        if ((lenght == 128) || (lenght == 192) || (lenght == 256)) {
            try {
                KeyGenerator kgen = KeyGenerator.getInstance("AES");
                kgen.init(lenght);
                sKey = kgen.generateKey();
            } catch (NoSuchAlgorithmException ex) {
                ex.printStackTrace();
            }
        }
        return sKey;

    }

    private static String encriptString(String stringtoencrypt) {

        byte[] stringtoencryptBytes = stringtoencrypt.getBytes();
        Cipher cipher = null;

        String stringEncrypted = null;
        try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(IV_PARAM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);

            byte[] stringEncryptedBytes = cipher.doFinal(stringtoencryptBytes);
            stringEncrypted = new String(cipher.doFinal(stringtoencryptBytes), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }


        return stringEncrypted;
    }


    private static String encriptKey(PublicKey aPublic) {
        String keyEncryptedString = null;
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.WRAP_MODE, aPublic);
            byte[] keyEncriptedBytes = cipher.wrap(secretKey);
            keyEncryptedString = new String(keyEncriptedBytes, "UTF-8");

        } catch (Exception e) {
            e.printStackTrace();
        }
        return keyEncryptedString;
    }

}

知道为什么会这样吗?

最佳答案

问题是您不能假设 RSA 或 AES 加密产生的字节代表字符串。当您使用 new String 生成字符串时,它会自动删除所有不代表字符的字节。如果您想拥有更多控制权并抛出异常(这应该是默认设置),您可以使用 CharsetDecoder

问题是,当您重新转换为字节时,某些字节可能会丢失。这意味着数字可能会小得多,这将以某种方式抛出异常,因为 unwrap 的输入现在太小了。

以下是一些说明如何使其正常工作的更改。我刚刚注释掉了您的代码,以便您进行比较。

import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

public class Test {

    private static SecretKey secretKey;
    private static KeyPair keyPair;
    private static final String STRINGTOENCRYPT = "This is a test";
    private static String stringEncripted;
    private static byte[] ct;
    private static String keyEncripted;
    private static byte[] wrapped;
    public static final byte[] IV_PARAM = {0x00, 0x01, 0x02, 0x03,
            0x04, 0x05, 0x06, 0x07,
            0x08, 0x09, 0x0A, 0x0B,
            0x0C, 0x0D, 0x0E, 0x0F};


    public static void main(String[] args) {
        generateKeys();
    }

    private static void generateKeys() {

        secretKey = generateSecretKey(128);
        keyPair = generateKeyPair(512);
        stringEncripted = encriptString(STRINGTOENCRYPT);
        keyEncripted = encriptKey(keyPair.getPublic());

        decryptString();

    }

    private static void decryptString() {
        Cipher cipher = null;
        Key keyDecrypted = null;
        try {
            cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.UNWRAP_MODE, keyPair.getPrivate());
            // keyDecrypted = cipher.unwrap(keyEncripted.getBytes(), "AES", Cipher.SECRET_KEY);
            keyDecrypted = cipher.unwrap(wrapped, "AES", Cipher.SECRET_KEY);

            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(IV_PARAM);
            cipher.init(Cipher.DECRYPT_MODE, keyDecrypted, iv);
            byte[] stringDecryptedBytes = cipher.doFinal(ct);
            String stringDecrypted = new String(stringDecryptedBytes);

            System.out.println(stringDecrypted);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private static KeyPair generateKeyPair(int lenght) {
        KeyPair keyPublicAndPrivate = null;

        try {
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(lenght);
            keyPublicAndPrivate = keyGen.genKeyPair();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return keyPublicAndPrivate;

    }

    private static SecretKey generateSecretKey(int lenght) {


        SecretKey sKey = null;
        if ((lenght == 128) || (lenght == 192) || (lenght == 256)) {
            try {
                KeyGenerator kgen = KeyGenerator.getInstance("AES");
                kgen.init(lenght);
                sKey = kgen.generateKey();
            } catch (NoSuchAlgorithmException ex) {
                ex.printStackTrace();
            }
        }
        return sKey;

    }

    private static String encriptString(String stringtoencrypt) {

        byte[] stringtoencryptBytes = stringtoencrypt.getBytes();
        Cipher cipher = null;

        String stringEncrypted = null;
        try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(IV_PARAM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);

            // byte[] stringEncryptedBytes = cipher.doFinal(stringtoencryptBytes);
            ct = cipher.doFinal(stringtoencryptBytes);
            stringEncrypted = new String(cipher.doFinal(stringtoencryptBytes), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }


        return stringEncrypted;
    }


    private static String encriptKey(PublicKey aPublic) {
        String keyEncryptedString = null;
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.WRAP_MODE, aPublic);
//            byte[] keyEncriptedBytes = cipher.wrap(secretKey);
//            keyEncryptedString = new String(keyEncriptedBytes, "UTF-8");
            wrapped = cipher.wrap(secretKey);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return keyEncryptedString;
    }

}

请注意,PKCS#1 填充(RSA 加密)和 CBC 都容易受到填充 oracle 攻击。您可能希望对 AES 使用 RSA/OAEP 和 GCM 模式。

关于java - 使用 RSA 和另一个 key 加密消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49710896/

相关文章:

Java如何让两个变量指向同一个原始对象

java - 通过 GLSurfaceView 渲染 View 组件

php - Laravel 使用 key 解密

php - 在移动服务器通信中使用密码作为加密 key 的一部分

java - 渲染 <p :outputPanel> based on selected <h:selectOneRadio > option in JSF

java - mysql在java中使用spring框架创建程序

android - 加密 Android 设备上的信息(合理防止用户访问)

mysql - 加密数据库凭据

java - 在 Jtable 行中插入多行

C++加密和解密Lua文件