解密错误:在 Android 上加密消息并在 Intelij IDEA 上解密

问题描述 投票:0回答:1

我的服务器在 Intelij Idea Windows 上,客户端在 Android 上。我将 rsa 公钥从服务器发送到客户端,客户端加密公钥消息并发送给服务器。解密消息时服务器出错。

我尝试不在 Intelij 上使用服务器,而是在 Android 上使用服务器,一切正常。我也尝试交换服务器和客户端,服务器 - Android,客户端 - Intelij,也没有成功,但结果不同:服务器获取并成功解密消息,但消息中还有许多其他符号。

这是代码,其中服务器 - Intelij 和客户端 - Android。

服务器:

ServerSocket serverSocket = new ServerSocket(5556);
Socket socket = serverSocket.accept();
System.out.println("Client connected");

DataOutputStream dOut = new DataOutputStream(socket.getOutputStream());
DataInputStream dIn  = new DataInputStream(socket.getInputStream());

//Create keys
Map<String, Object> RsaKey = rsa.initKey();
String RsaPrivateKey = rsa.getPrivateKey(RsaKey);
String RsaPublicKey = rsa.getPublicKey(RsaKey);

//send key
dOut.writeInt(RsaPublicKey.getBytes().length);
dOut.write(RsaPublicKey.getBytes());
Thread.sleep(1000);
System.out.println(RsaPublicKey);

//read encrypted message
int length_pk = dIn.readInt();
byte[] message = new byte[length_pk];
dIn.readFully(message, 0, message.length);

String st1 = new String(message);
System.out.println(st1 + " " + length_pk + " " + st1.length());

String str = new String(rsa.decryptByPrivateKey(message, RsaPrivateKey));
System.out.println(str);

客户:

Socket client = new Socket("192.168.1.103", 5556);
DataOutputStream dOut = new DataOutputStream(client.getOutputStream());
DataInputStream dIn = new DataInputStream(client.getInputStream());

//read key from server
int length_pk1 = dIn.readInt();
byte[] publickeyserver1 = new byte[length_pk1];
dIn.readFully(publickeyserver1, 0, publickeyserver1.length);
String RsaPublicKeyServer = new String(publickeyserver1);

//test key rsa
String str = "Who is there?";
byte[] encbyte = rsa.encryptByPublicKey(str.getBytes(), RsaPublicKeyServer);

dOut.writeInt(encbyte.length);
dOut.write(encbyte);

Rsa 库:

public static final String KEY_ALGORITHM = "RSA";
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
private static final String PUBLIC_KEY = "RSAPublicKey"; 
private static final String PRIVATE_KEY = "RSAPrivateKey";

public static byte[] decryptBASE64(String key) throws Exception {
   return Base64.getDecoder().decode(key);
}
public static byte[] decryptByPrivateKey(byte[] data, String key)throws Exception {
   byte[] keyBytes = decryptBASE64(key);

   PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
   KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
   Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

   Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
   cipher.init(Cipher.DECRYPT_MODE, privateKey);
   return cipher.doFinal(data);
}
public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
   byte[] keyBytes = decryptBASE64(key);
   X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
   KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
   Key publicKey = keyFactory.generatePublic(x509KeySpec);

   Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
   cipher.init(Cipher.ENCRYPT_MODE, publicKey);

   return cipher.doFinal(data);
}
public static String getPrivateKey(Map<String, Object> keyMap)throws Exception {
   Key key = (Key) keyMap.get(PRIVATE_KEY);
   return encryptBASE64(key.getEncoded());
}
public static String getPublicKey(Map<String, Object> keyMap)throws Exception {
   Key key = (Key) keyMap.get(PUBLIC_KEY);
   return encryptBASE64(key.getEncoded());
}
public static Map<String, Object> initKey() throws Exception {
   KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
   keyPairGen.initialize(1024);

   KeyPair keyPair = keyPairGen.generateKeyPair();
   RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
   RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

   Map<String, Object> keyMap = new HashMap<String, Object>(2);

   keyMap.put(PUBLIC_KEY, publicKey);
   keyMap.put(PRIVATE_KEY, privateKey);
   return keyMap;
}

堆栈跟踪

线程“main”java.lang.RuntimeException 中出现异常: javax.crypto.BadPaddingException:解密错误 Main.main(Main.java:110) 引起:javax.crypto.BadPaddingException: 解密错误位于 java.base/sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:369) 在 java.base/sun.security.rsa.RSAPadding.unpad(RSAPadding.java:282) 在 java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:366) 在 java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:400) 在 java.base/javax.crypto.Cipher.doFinal(Cipher.java:2206) 处 main.java.rsa.decryptByPrivateKey(rsa.java:134) 在 Main.main(Main.java:103)

我已经使用Rsa库很长时间了,我不认为这是一个问题。我认为问题出在IDE之间。

android encryption rsa
1个回答
0
投票

正如我在评论中所建议的那样,问题是您未能将完整的“算法/模式/填充”转换规范指定为

Cipher.getInstance()
。相反,您使用
keyFactory.getAlgorithm()
作为规范。 KeyFactory 算法恰好返回“RSA”,当您使用
Cipher.getInstance("RSA")
时,您将以特定于提供者的默认值结束。在 Android 上,填充默认值为“NoPadding”,在 Java 上,填充默认值为“PKCS1Padding”。两边都使用RSA/ECB/PKCS1Padding会更好。比这更好的是更现代但更复杂的“RSA/ECB/OAEPwithSHA-256andMGF1Padding”。
OAEPwithSHA-256andMGF1Padding
方案本身有参数,尽管您可以使用默认值,但它们是特定于提供者的,因此应该避免。返回一个初始化的、可互操作的、基于现代 RSA 的
Cipher
实例,其中包含几行代码:

    private static final String RSA_CIPHER_SPEC = "RSA/ECB/OAEPwithSHA-256andMGF1Padding";


    private static Cipher getRsaCipher(int direction, Key key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
        Cipher cipher = Cipher.getInstance(RSA_CIPHER_SPEC);
        OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
        cipher.init(direction, key, oaepSpec);

        return cipher;
    }

这可以在您的加密/解密方法中使用,如下所示:

    public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
        byte[] keyBytes = decryptBASE64(key);

        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

        Cipher cipher = getRsaCipher(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
        byte[] keyBytes = decryptBASE64(key);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509KeySpec);

        Cipher cipher = getRsaCipher(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

Android 和 Java 端。

还可以对您的代码进行其他改进,例如消除 Base64 编码/解码。这似乎增加了并发症,却没有任何好处。

© www.soinside.com 2019 - 2024. All rights reserved.