【问题标题】:How to Encrypt with RSA in Android using a HEX Public key from php server如何使用来自 php 服务器的 HEX 公钥在 Android 中使用 RSA 进行加密
【发布时间】:2016-10-22 12:09:16
【问题描述】:

我想在 Android 中使用从我的 php 服务器接收的十六进制格式的公共 RSA 密钥进行加密。
下面是一个 RSA 公钥的例子:

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

生成公钥时显示此错误。

【问题讨论】:

    标签: java php android encryption rsa


    【解决方案1】:

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

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

    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")
    

    取决于服务器的期望。

    【讨论】:

    • 嗨,您使用模数和指数是对的,并且 65537 是正确的模数我尝试了您的建议并且代码运行没有错误,我尝试使用 RSA/ECB/PKCS1Padding 但结果加密似乎和我的 JAVASCRIPT 代码不一样,因为我在 JS 中有一个例子,加密结果和我从 Android 得到的不一样
    • @Oussaki RSA 加密最有可能使用随机填充。因此,您可以对完全相同的事物进行多次加密并获得不同的密文。那是一个安全属性。您需要在一个中加密并在另一个中解密以检查兼容性。
    • 是的,它现在可以工作了,我会在这里分享完整的代码
    【解决方案2】:

    在我的代码中,我错过了两件事:

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

    2 - 使用 N 和 E 两个因子生成公钥

    我的服务器也期望返回一个 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();
        }
    

    这是将字节数组转换为十六进制字符串的函数

     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();
    }
    

    【讨论】:

      猜你喜欢
      • 2019-06-09
      • 1970-01-01
      • 2011-08-16
      • 1970-01-01
      • 2019-08-06
      • 2019-11-27
      • 1970-01-01
      • 2017-09-25
      • 2012-05-07
      相关资源
      最近更新 更多