如何使用来自 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();
}
我想使用从我的 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();
}