努力在 Java 中使用 C# RSA
我正在尝试复制现有的 C# 应用程序,其中一部分使用密钥加密一些数据。
简单的例子:
using System;
using System.Security.Cryptography;
using System.Text;
public class Program {
private static string xmlKey = "<RSAKeyValue><Modulus>{REDACTED MODULUS}</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
public static void Main() {
RSACryptoServiceProvider cipher = new RSACryptoServiceProvider();
cipher.FromXmlString(xmlKey);
Console.WriteLine("KeyExchangeAlgorithm: " + cipher.KeyExchangeAlgorithm);
byte[] input = Encoding.UTF8.GetBytes("test");
byte[] output = cipher.Encrypt(input, true);
Console.WriteLine("Output: " + Convert.ToBase64String(output));
}
}
哪个输出:
KeyExchangeAlgorithm: RSA-PKCS1-KeyEx
Output: {THE ENCRYPTED OUTPUT}
我用下面的代码在Java中复制了这个,但是当它运行正常时,下游系统无法解密数据,所以我做错了什么
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
public class Program {
// I tried "RSA/ECB/PKCS1Padding" but got "java.security.NoSuchAlgorithmException: RSA/ECB/PKCS1Padding KeyFactory not available at java.base/java.security.KeyFactory.<init>(KeyFactory.java:138)"
private static final String ALGORITHM = "RSA";
private static final String MODULUS = "{REDACTED MODULUS}";
private static final String EXPONENT = "AQAB";
// Converted from XML using
// https://superdry.apphb.com/tools/online-rsa-key-converter
private static final String PEM_KEY = "{REDACTED PEM KEY}";
public static void main(final String[] args) throws Exception {
final Cipher cipher = Cipher.getInstance(ALGORITHM);
final PublicKey key = KeyFactory.getInstance(ALGORITHM).generatePublic(getX509Key());
cipher.init(Cipher.ENCRYPT_MODE, key);
System.out.println("Algorithm: " + cipher.getAlgorithm());
final byte[] input = "test".getBytes(StandardCharsets.UTF_8);
final byte[] output = cipher.doFinal(input);
System.out.println("Output: " + Base64.getEncoder().encodeToString(output));
}
private static KeySpec getRSAKey() throws Exception {
return new RSAPublicKeySpec(base64ToInt(MODULUS), base64ToInt(EXPONENT));
}
private static BigInteger base64ToInt(final String str) {
return new BigInteger(1, Base64.getDecoder().decode(str.getBytes()));
}
private static KeySpec getX509Key() throws Exception {
return new X509EncodedKeySpec(Base64.getDecoder().decode(PEM_KEY));
}
}
任何人都可以告诉我做错了什么,请?
I'm trying to replicate an existing C# application, part of which encrypts some data using a key.
Simple example:
using System;
using System.Security.Cryptography;
using System.Text;
public class Program {
private static string xmlKey = "<RSAKeyValue><Modulus>{REDACTED MODULUS}</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
public static void Main() {
RSACryptoServiceProvider cipher = new RSACryptoServiceProvider();
cipher.FromXmlString(xmlKey);
Console.WriteLine("KeyExchangeAlgorithm: " + cipher.KeyExchangeAlgorithm);
byte[] input = Encoding.UTF8.GetBytes("test");
byte[] output = cipher.Encrypt(input, true);
Console.WriteLine("Output: " + Convert.ToBase64String(output));
}
}
Which outputs:
KeyExchangeAlgorithm: RSA-PKCS1-KeyEx
Output: {THE ENCRYPTED OUTPUT}
I've replicated this in Java with the following, but while it runs ok, the downstream system can't decrypt the data, so I've done something wrong
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
public class Program {
// I tried "RSA/ECB/PKCS1Padding" but got "java.security.NoSuchAlgorithmException: RSA/ECB/PKCS1Padding KeyFactory not available at java.base/java.security.KeyFactory.<init>(KeyFactory.java:138)"
private static final String ALGORITHM = "RSA";
private static final String MODULUS = "{REDACTED MODULUS}";
private static final String EXPONENT = "AQAB";
// Converted from XML using
// https://superdry.apphb.com/tools/online-rsa-key-converter
private static final String PEM_KEY = "{REDACTED PEM KEY}";
public static void main(final String[] args) throws Exception {
final Cipher cipher = Cipher.getInstance(ALGORITHM);
final PublicKey key = KeyFactory.getInstance(ALGORITHM).generatePublic(getX509Key());
cipher.init(Cipher.ENCRYPT_MODE, key);
System.out.println("Algorithm: " + cipher.getAlgorithm());
final byte[] input = "test".getBytes(StandardCharsets.UTF_8);
final byte[] output = cipher.doFinal(input);
System.out.println("Output: " + Base64.getEncoder().encodeToString(output));
}
private static KeySpec getRSAKey() throws Exception {
return new RSAPublicKeySpec(base64ToInt(MODULUS), base64ToInt(EXPONENT));
}
private static BigInteger base64ToInt(final String str) {
return new BigInteger(1, Base64.getDecoder().decode(str.getBytes()));
}
private static KeySpec getX509Key() throws Exception {
return new X509EncodedKeySpec(Base64.getDecoder().decode(PEM_KEY));
}
}
Can anyone advise what I've done wrong, please?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
由于在
RSACryptoServiceProvider#Encrypt()
中,第二个参数中传递了true
,因此 OAEP 用作 OAEP 和 MGF1 摘要的填充和 SHA-1,即解密将在 Java 中执行,例如:请注意,
X509EncodedKeySpec()
需要 DER 编码的 X.509/SPKI 密钥,即PEM_KEY
不得包含 PEM 编码的密钥,而只能包含 Base64 编码的正文(即没有页眉、页脚和换行符)。另请注意,OAEP 是概率填充,即即使对于相同的输入数据,每次加密的密文也不同。因此,即使输入数据相同,两个代码的密文也不会匹配,这不是故障。
可以使用以下 C# 代码进行测试:
该代码解密 C# 代码的密文以及 Java 代码的密文。
编辑:
关于评论中提到的Java代码中的密钥导入:如上所述,
PEM_KEY
仅包含PEM密钥的Base64编码主体,没有换行符。除此之外,密钥导入与您的代码相匹配:关于异常 NoSuchAlgorithmException:RSA/ECB/PKCS1Padding KeyFactory 不可用:如果在
时除了算法之外还指定了填充,则会抛出此异常创建 KeyFactory
对象,例如,如果传递的是RSA/ECB/PKCS1Padding
而不是RSA
getInstance()
。相反,在实例化Cipher
对象时应指定填充,例如应传递RSA/ECB/PKCS1Padding
而不是RSA
。如果此处仅指定算法,则将使用依赖于提供者的默认值进行填充,这是应该避免的。请注意,PKCS1Padding
表示 PKCS#1 v1.5 填充,与 C# 代码中使用的 OAEP 不对应。Since in
RSACryptoServiceProvider#Encrypt()
atrue
is passed in the 2nd parameter, OAEP is used as padding and SHA-1 for the OAEP and the MGF1 digest, i.e. the decryption is to be performed in Java e.g. with:Note that
X509EncodedKeySpec()
expects a DER encoded X.509/SPKI key, i.e.PEM_KEY
must not contain the PEM encoded key, but only the Base64 encoded body (i.e. without header, without footer and without line breaks).Note also that OAEP is a probabilistic padding, i.e. the ciphertext is different for each encryption even for the same input data. For this reason, the ciphertexts of both codes will not match, even with identical input data, which is not a malfunction.
A test is possible with the following C# code:
This code decrypts the ciphertext of the C# code as well as the ciphertext of the Java code.
EDIT:
Regarding the key import in the Java code mentioned in your comment: As explained above,
PEM_KEY
contains only the Base64 encoded body of the PEM key without line breaks. Apart from that, the key import matches your code:Regarding the exception NoSuchAlgorithmException: RSA/ECB/PKCS1Padding KeyFactory not available: This is thrown if the padding is specified in addition to the algorithm when the
KeyFactory
object is created, e.g. ifRSA/ECB/PKCS1Padding
is passed instead ofRSA
ingetInstance()
. In contrast, padding should be specified when instantiating theCipher
object, e.g.RSA/ECB/PKCS1Padding
should be passed instead ofRSA
. If only the algorithm is specified here, a provider-dependent default value would be used for the padding, which should be avoided. Note thatPKCS1Padding
denotes PKCS#1 v1.5 padding and does not correspond to OAEP used in the C# code.