如何使用来自 php 服务器的 HEX Public 密钥在 Android 中使用 RSA 进行加密

How to Encrypt with RSA in Android using a HEX Public key from php server

我想使用从我的 php 服务器收到的 Android 中的 public RSA 密钥以 HEX 格式进行加密。
这是 RSA public 密钥的示例:

b74420f5a4d9abfd2072c9d936dd53e2de2aa790822ad1608807bda3e176b335c51902ca2177824198181ce8bea85de132aaea1104fd043e4ad2c0af705bda966b5d2f92a6ab5170d161eb1e8f7a6b1d5fba673f8a4dcebe55407ef9707782c91b17527af820a2c3a3b586341ae54ef03739074d4738e3ff35257bdfb9233c53

收到后我尝试用它来加密一个字符串。
这是我在 ANDROID 中的代码示例:

        try {
        String arg = "b74420f5a4d9abfd2072c9d936dd53e2de2aa790822ad1608807bda3e176b335c51902ca2177824198181ce8bea85de132aaea1104fd043e4ad2c0af705bda966b5d2f92a6ab5170d161eb1e8f7a6b1d5fba673f8a4dcebe55407ef9707782c91b17527af820a2c3a3b586341ae54ef03739074d4738e3ff35257bdfb9233c53";

        byte[] bytes = org.apache.commons.codec.binary.Hex.decodeHex(arg.toCharArray());
        System.out.println(new String(bytes, "UTF-8"));

        String message = "oussaki";

        byte[] publicBytes = org.apache.commons.codec.binary.Hex.decodeHex(arg.toCharArray());

        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
        try {

            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey pubKey = keyFactory.generatePublic(keySpec);
            // Encrypt the message
            byte[] encryptedBytes = null;
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);

            encryptedBytes = cipher.doFinal(message.getBytes());
            System.out.println(encryptedBytes);

            byte[] b2 = new byte[encryptedBytes.length + 1];
            b2[0] = 1;
            System.arraycopy(encryptedBytes, 0, b2, 1, encryptedBytes.length);
            String s = new BigInteger(b2).toString(36);
            System.out.println("Encrypted text" + s);

        } catch (BadPaddingException | IllegalBlockSizeException | InvalidKeyException
                | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeySpecException k) {
            k.printStackTrace();
        }
    } catch (DecoderException e) {
        e.printStackTrace();
    }

在 运行 代码之后,它显示了一个无效的密钥格式:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format

生成 public 密钥时显示此错误。

首先,这不是我所知道的任何格式的 RSA public 密钥,当然也不是 X509EncodedKeySpec class 期望的格式。它看起来像是 RSA 模数的原始转储。您缺少 public 指数。有人可能猜到指数是 65537,但你需要验证一下。

您可以使用 sign-magnitude BigInteger 构造函数 将您的 RSA 模数转换为 BigInteger,然后通过 创建一个 RSA public 密钥]RSAPublicKeySpec class,而不是 X509EncodedKeySpec class。以下是使用您的代码作为基础的几行示例:

byte[] publicBytes = org.apache.commons.codec.binary.Hex.decodeHex(arg.toCharArray());

BigInteger modulus = new BigInteger(1, publicBytes);
BigInteger publicExponent = BigInteger.valueOf(65537L);

RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);

另一个问题:您在 Cipher.getInstance() 中使用默认值。不要那样做。找出服务器期望的 RSA 填充方案并明确指定。例如,您可能有

而不是 Cipher.getInstance("RSA")
Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")

Cipher.getInstance("RSA/ECB/PKCS1Padding")

取决于服务器的期望。

在我的代码中我遗漏了两件事:

1 - 选择正确的填充方案。

2 - 使用 N 和 E 两个因素生成 public 密钥

我的服务器还需要返回一个 HEX 值,我必须将 Byte 数组转换为 HexString

这是完整的代码:

   try {
        String arg = "b74420f5a4d9abfd2072c9d936dd53e2de2aa790822ad1608807bda3e176b335c51902ca2177824198181ce8bea85de132aaea1104fd043e4ad2c0af705bda966b5d2f92a6ab5170d161eb1e8f7a6b1d5fba673f8a4dcebe55407ef9707782c91b17527af820a2c3a3b586341ae54ef03739074d4738e3ff35257bdfb9233c53";
        String message = "plain text";
        byte[] publicBytes = org.apache.commons.codec.binary.Hex.decodeHex(arg.toCharArray());

        BigInteger modulus = new BigInteger(1, publicBytes);
        BigInteger publicExponent = BigInteger.valueOf(65537L);

        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(keySpec);

        try {
            // decrypts the message
            byte[] encryptedBytes = null;
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            encryptedBytes = cipher.doFinal(message.getBytes());

            System.out.println( "Encrypted text : "+ convertToHexString(encryptedBytes));
        } catch (BadPaddingException | IllegalBlockSizeException | InvalidKeyException
                | NoSuchPaddingException | NoSuchAlgorithmException k) {
            k.printStackTrace();
        }
    } catch (DecoderException e) {
        e.printStackTrace();
    }

这是将 Byte 数组转换为 HexString 的函数

 private static String convertToHexString(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {
        int halfbyte = (data[i] >>> 4) & 0x0F;
        int two_halfs = 0;
        do {
            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));
            halfbyte = data[i] & 0x0F;
        } while(two_halfs++ < 1);
    }
    return buf.toString();
}