我不是加密专家,当我使用下面的加密方法时,我得到了一些有趣的结果。
服务器是.NET C#,客户端运行JAVA。基本上,我们加密信用卡信息和我拥有的12张信用卡,11与下面的方法完美配合。
然而,其中一张卡片(真实的VISA信用卡)encrypt()
返回并转换为十六进制的结果在字符串的开头有一个负号,如下所示:
-6d9830a52b2c3add7a78fd9897bca19d.....
,它在服务器尝试解密时失败,我认为根据此解释,它应该是肯定的而不是否定的RSA - Encryption with negative exponent
private static byte[] encrypt(String text, PublicKey pubRSA) throws Exception
{
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(Cipher.ENCRYPT_MODE, pubRSA);
return cipher.doFinal(text.getBytes());
}
//Using this encryption method one card could not be decrypted by vPAY due to negative (exponential) symbol.
//It may have the same affect with other cards
public final static byte[] encrypt(String text)
{
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(Base64.decode(pkBase64));
PublicKey pk = keyFactory.generatePublic(x509Spec);
return encrypt(text, pk);
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
有没有人遇到类似的事情并找到了解决方法?
我尝试了其他三种不同的KeySpec
算法和相同的publicKey(源代码是base64格式的字符串),但即使使用之前正在使用的卡,服务器也无法对其进行解密...
更新1
这是将加密结果以字节转换为HEX的方式:
public static String byteToHex(byte[] string)
{
try {
return String.format("%04x", new BigInteger(string));
} catch (Exception e) {
// TODO Auto-generated catch block
return null;
}
}
答案 0 :(得分:3)
您应该直接从byte[]
打印出十六进制字符串。这可以使用以下代码完成:
StringBuilder sb = new StringBuilder(data.length * 2);
for (int i = 0; i < data.length; i++) {
sb.append(String.format("%02X", data[i] & 0xFF));
}
return sb.toString();
无需使用BigInteger
。事实上,使用BigInteger是危险的。一个原因是您已经遇到的问题:默认情况下,BigInteger
转换为byte[]
/从{{1}}转换使用带符号的大端编码。另一件事是RSA签名的输出(作为整数)可能比十六进制的模数大小更小。这就是EJP的解决方案偶尔会失败的原因。
RSA输出已经以字节定义,作为无符号大端编码,其密码大小与密钥大小相同(在标准文档中使用整数到八位字符串编码)。
答案 1 :(得分:2)
public static String byteToHex(byte [] string)
byte[]
不是字符串。这是一个字节数组。不要将自己与不适当的变量名混淆。 String不是二进制数据的容器。
return String.format("%04x", new BigInteger(string));
尝试return new BigInteger(1,string).toString(16),
并查看Javadoc,了解为什么new BigInteger(string)
没有这样做。