为什么在 C# 和 Java 中生成 512 位 RSA 密钥时,公钥字节的长度不同?
C#代码示例:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512);
XDocument xdocPub = XDocument.Parse(rsa.ToXmlString(false));
string publicKey = xdocPub.Element("RSAKeyValue").Element("Modulus").Value;
byte[] publicArray = Convert.FromBase64String(publicKey);
Console.WriteLine("Public Key Byte Array Length: " + publicArray.Length);
Console.WriteLine("Public Key Base64 Length: " + publicKey.Length);
Console.WriteLine("Public Key Text: " + publicKey);
结果:
Public Key Byte Array Length: 64
Public Key Base64 Length: 88
Public Key Text: uELcIjgR6GpiayZ1wHruHOtO8dpg+xmg85VAvunWUEL+aifoyk9+CA/dez4UmuIwLEgRlpVoIIOD0e5i9v8lrQ==
Java 代码示例:
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(new RSAKeyGenParameterSpec(512, RSAKeyGenParameterSpec.F4));
KeyPair pair = keyGen.genKeyPair();
final byte[] arrayPublic = ((RSAPublicKey) pair.getPublic()).getModulus().toByteArray();
System.out.println("Public Key [origin] lenght: " + arrayPublic.length);
String publicBase64 = Base64.getEncoder().encodeToString(arrayPublic);
System.out.println("Public Key [base64] lenght : " + publicBase64.length());
System.out.println("Public Key [base64]: " + publicBase64);
结果:
Public Key [origin] lenght: 65
Public Key [base64] lenght : 88
Public Key [base64]: ANuun1/CnOmk2ghsydz4cKvPCd7q0YqqeyjzLqtKx7QHqv1tFjJYsXquYGcY8zkaPsPBrRlCAJov7+J88GaFDKc=
我注意到在Java生成的key的字节数组中,key数组的开头有一个空字节。
最有趣的是,如果我从 C# 应用程序发送一个长度为 64 字节的密钥,那么在 Java 应用程序中,我会在以下代码中收到错误“指数大于模数”:
try
{
final KeyFactory kfac = KeyFactory.getInstance("RSA");
final BigInteger modulus = new BigInteger("здесь вставляется byte[] массив публичного ключа от C#");
final RSAPublicKeySpec kspec1 = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4);
RSAPublicKey publicKey = (RSAPublicKey) kfac.generatePublic(kspec1);
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
同时,如果我在将密钥从 C# 应用程序发送到 Java 应用程序之前在密钥的开头添加一个零字节,那么从 Java 应用程序端来看,加密初始化和发送数据的加密没有问题。但在这种情况下,我无法使用相同的加密实例对 C# 应用程序中的传入数据进行解码。我收到“错误数据”错误。
告诉我我做错了什么以及为什么键的长度不同?
UPD 1:添加了 Java 测试
PublicKey 取自 C# 中生成的第一个示例。
public class Test
{
public static void main(String[] args)
{
final String base64key = "uELcIjgR6GpiayZ1wHruHOtO8dpg+xmg85VAvunWUEL+aifoyk9+CA/dez4UmuIwLEgRlpVoIIOD0e5i9v8lrQ==";
try
{
byte[] publicKeyArray = Base64.getDecoder().decode(base64key);
final KeyFactory kfac = KeyFactory.getInstance("RSA");
final BigInteger modulus = new BigInteger(1, publicKeyArray);
final RSAPublicKeySpec kspec1 = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4);
RSAPublicKey publicKey = (RSAPublicKey) kfac.generatePublic(kspec1);
byte[] array = publicKey.getModulus().toByteArray();
System.out.println("Public Key [Array] lenght : " + array.length);
String publicBase64 = Base64.getEncoder().encodeToString(array);
System.out.println("Public Key [base64] lenght : " + publicBase64.length());
System.out.println("Public Key [base64]: " + publicBase64);
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
}
}
由于 RSAPublicKey 被初始化为 Java Base64,因此密钥与输入不匹配。虽然长度是65字节。但这仅适用于signum = 1
.
Public Key [Array] lenght : 65
Public Key [base64] lenght : 88
Public Key [base64]: ALhC3CI4EehqYmsmdcB67hzrTvHaYPsZoPOVQL7p1lBC/mon6MpPfggP3Xs+FJriMCxIEZaVaCCDg9HuYvb/Ja0=
UPD 2:C# 生成 RSA 密钥的问题由Bouncy Castle库解决。该库有源代码,所以我只使用了 RSA 部分并在我的项目中实现了它。
额外字节
BigInteger.toByteArray
在结果中包含数字的符号位:包括。是的,开头的零字节可以根据需要截掉。你可以这样做
System.arraycopy
:请参阅@Maarten Bodewes对英文相关问题的回答(How insert bits into block in java cryptography?),有一种现成的方法可以“标准化”带有前导零的字节数组,它还考虑了以下情况该数组短于 64(它似乎只发生在私钥上)。
不良数据
构造函数
BigInteger(byte[] val)
还需要数字的带符号表示,并尝试从密钥中读取符号位。尝试使用单独使用符号位的构造函数BigInteger(int signum, byte\[\] magnitude)
:关于 en.SO 的相关问题: