我需要使用公钥加密网站上的密码。公钥通过Java Web服务提供,其中包含以下信息:密钥是与RSA / ECB / PKCS1Padding算法一起使用的RSA密钥。公钥以JSON格式提供:
{
"kid":"PWD",
"kty":"RSA",
"use":"enc",
"n":"MTA0OTgzNjg0OTMxMzE2NjkwNTU4Mjg3NDIwMDg1NTY0ODEyMjg1MDk2NTcwNzU5NDIzNzM0O
DA3OTA2MzA0MDczNTU0NDQ2Njg3ODY2ODk2NTk0NjYzNTAxMzg0NzE1OTExMjA0MjU1MzMzOTIzMjA
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
zcwMjA3MzQxOTcwNzc4NDAwNzM3MTY2NDMyNzIwNjMwMDQwOTMwOTQ0MTA2OTE1NDEzMDAwNTMyMTE
5ODM0MTA2MjAzMDIyODEwMjYyMDM3MDQ0NzkxNzIzNTU1MjQyNjYxMzE2ODc4OTc5NzY1OTgzMjg4M
zQ0NDc3OTYwNTg3MzE2NTUwMDgx",
"e":"NjU1Mzc"
}
我尝试使用公钥加密密码,但生成的密钥不正确。这是我的代码:
encryptedPassword = EncrypIt("Password01", "MTA1MzQxNzIwODA3NjUwNzg5ND
Y4ODU2Mjc0NDA3ODIwMjQ1ODQ5NDE1MDk1MDIzMTM3MjM0NzAwNzYzNjc2MTgwNjg3ODMxMjA3
NTY0NTcxMjg2MzM4NjQ1NzEwMDcyMjY2MTQyNDIzMTczMjkwMDk0MTc0MTA5MTc5MzI0NjYwMjQ4NzI3NzM0MTQ5NDY0MjUwODkwO
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
c2OTAzNjc3NzQzODM3NzM0MjE2ODM0NjY4MjM4MTQ0OTA3MDQ3MTk1Njc1NzU3OTE2NjEyNzkzMzM2MzI3MDUyNjg0NDI5NDExNjI
2MzA0MzM5", "NjU1Mzc");
public static string EncrypIt(string password, string publicKey, string exponent)
{
UnicodeEncoding ByteConverter = new UnicodeEncoding();
byte[] publicKeyByte = ByteConverter.GetBytes(publicKey);
byte[] passwordByte = ByteConverter.GetBytes(password);
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo = RSA.ExportParameters(false); //Export only public key
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = publicKeyByte;
//RSAKeyInfo.Exponent = exponentBytes; //I tried to set exponent but I have an error
RSA.ImportParameters(RSAKeyInfo);
byte[] encryptedPassword = RSA.Encrypt(passwordByte, false);
return Convert.ToBase64String(encryptedPassword);
}
(JSON和我的代码之间的公钥不同,但不要注意它,我只是从不同的来源复制了信息)
来自 owlstead 的答案帮助我获得了具有正确字符串大小的加密密码,但使用加密字符串的服务将其拒绝并显示以下消息:javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
我选择了正在进行正确加密的java程序代码(见下文)。我需要使用.NET实现相同的加密。
public class EncryptionServiceImpl
{
private static final Charset UTF8 = Charset.forName("UTF-8");
@Resource(name = "briqueAuthentificationClient")
private BriqueAuthentificationClientImpl briqueAuthentificationClient;
protected static final String ALGORITHM_RSA = "RSA";
protected static final String TRANSFORMATION_RSA_ECB_PKCS1PADDING = "RSA/ECB/PKCS1Padding";
private static final Logger LOG = LoggerFactory.getLogger(EncryptionServiceImpl.class);
public EncryptionServiceImpl() {
LOG.info("constructeur EncryptionServiceImpl");
}
/**
* @param briqueAuthentificationClient the briqueAuthentificationClient to set
*/
public void setBriqueAuthentificationClient(final BriqueAuthentificationClientImpl briqueAuthentificationClient) {
this.briqueAuthentificationClient = briqueAuthentificationClient;
}
public String encrypt(final String input) throws GeneralSecurityException {
if (StringUtils.isNotBlank(input)) {
final CertificateDto certificate = this.briqueAuthentificationClient.getCurrentCertificate();
if (certificate != null) {
return new String(this.encryptAndEncode(input.getBytes(), certificate), EncryptionServiceImpl.UTF8);
} else {
throw new RuntimeException("Certificate is null");
}
}
return null;
}
protected byte[] encryptAndEncode(final byte[] input, final CertificateDto currentCertificate)
throws GeneralSecurityException {
// Création de la clé publique
final PublicKey publicKey = this.buildPublicKey(currentCertificate);
// Chiffre
final byte[] inputEncrypted = this.encrypte(input, publicKey);
// Encode
return this.encodeBase64Url(inputEncrypted);
}
protected PublicKey buildPublicKey(final CertificateDto currentCertificate) throws GeneralSecurityException {
if ("RSA".equals(currentCertificate.getKeyType())) {
return this.buildRSAPublicKey(currentCertificate);
}
LOG.error(String.format("Tentative de création d'une clé publique avec un algorithme non connu [%s]",
currentCertificate.getKeyType()));
return null;
}
protected PublicKey buildRSAPublicKey(final CertificateDto currentCertificate) throws GeneralSecurityException {
final BigInteger modulus = new BigInteger(new String(Base64.decodeBase64(currentCertificate.getModulus()),
EncryptionServiceImpl.UTF8));
final BigInteger publicExponent = new BigInteger(new String(Base64.decodeBase64(currentCertificate
.getPublicExponent()), EncryptionServiceImpl.UTF8));
try {
return KeyFactory.getInstance(ALGORITHM_RSA).generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
} catch (InvalidKeySpecException e) {
throw e;
} catch (NoSuchAlgorithmException e) {
throw e;
}
}
protected byte[] encrypte(final byte[] input, final RSAPublicKeySpec rsaPublicKeySpec)
throws GeneralSecurityException {
PublicKey publicKey;
try {
publicKey = KeyFactory.getInstance(ALGORITHM_RSA).generatePublic(
new RSAPublicKeySpec(rsaPublicKeySpec.getModulus(), rsaPublicKeySpec.getPublicExponent()));
} catch (InvalidKeySpecException e) {
throw e;
} catch (NoSuchAlgorithmException e) {
throw e;
}
return this.encrypte(input, publicKey);
}
protected byte[] encrypte(final byte[] input, final PublicKey publicKey) throws GeneralSecurityException {
try {
final Cipher cipher = Cipher.getInstance(TRANSFORMATION_RSA_ECB_PKCS1PADDING);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(input);
} catch (NoSuchAlgorithmException e) {
throw e;
} catch (NoSuchPaddingException e) {
throw e;
} catch (IllegalBlockSizeException e) {
throw e;
} catch (BadPaddingException e) {
throw e;
}
}
protected byte[] decrypte(final byte[] input, final RSAPrivateKeySpec rsaPrivateKeySpec)
throws GeneralSecurityException {
final BigInteger modulus = rsaPrivateKeySpec.getModulus();
final BigInteger privateExponent = rsaPrivateKeySpec.getPrivateExponent();
try {
final PrivateKey privateKey = KeyFactory.getInstance(ALGORITHM_RSA).generatePrivate(
new RSAPrivateKeySpec(modulus, privateExponent));
final Cipher cipher = Cipher.getInstance(TRANSFORMATION_RSA_ECB_PKCS1PADDING);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(input);
} catch (NoSuchAlgorithmException e) {
throw e;
} catch (NoSuchPaddingException e) {
throw e;
} catch (IllegalBlockSizeException e) {
throw e;
} catch (BadPaddingException e) {
throw e;
} catch (InvalidKeySpecException e) {
throw e;
} catch (InvalidKeyException e) {
throw e;
}
}
protected byte[] encodeBase64Url(final byte[] input) {
return Base64.encodeBase64(input, false);
}
protected byte[] decodeBase64Url(final byte[] input) {
return Base64.decodeBase64(input);
}
/**
* Method to connect to an url
*
* @param httpclient the http connection
* @return the response GetMethod
* @throws OAuthException in cas of connection error
*/
private GetMethod connect(final HttpClient httpclient, final String url) {
final GetMethod httpget = new GetMethod(url);
try {
httpclient.executeMethod(httpget);
} catch (final UnknownHostException e) {
throw new RuntimeException("Connection ERROR - Host could not be determined.", e);
} catch (final IOException e) {
throw new RuntimeException("Connection ERROR - Input/Output error.", e);
}
return httpget;
}
}
我在 owlstead 的帮助下完成的步骤在下面的答案中。 当我使用这个Java程序对字符串 Password01 进行编码时,我获得了一个字符串,如:
sy5/XElHvuYA4Rbq8OBydLymT82R+z77jy6MU2sNal7VenzPEbARgy7p3zWgYgG13Cypk+zbnnB5L37fVUhgOgCqCyLtikzxJBNmPyzUK9+beiHJKyONZwifDzQ44hXTeXcZ0bmF9b5dLFy9QS/N5m28vIyBSGY8K2B7EB2FF38=
此加密密码可以在Java端解密
当我使用.NET代码时,字符串就像:
ACZXYj/KudyxKBB510SxSouKaVwssmEUM6Jpreh8jTtrIH9eGb18GIdkBU7rXzMuLYbAhyREbFLbR87zW/2DNa4tN97FOlqr6k1JppJ/SSS/9fGdMvSOAQbWjsxksDH7fRu9dCvK0m0exFtGfXG7Yua9bB1m0dTNiwCZUZM0LnEM
此加密密码无法在Java端解密。失败并显示错误消息:
javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
答案 0 :(得分:3)
您首先需要使用n
和e
的{{3}}执行base 64解码,将ASCII编码的结果转换为字符串,然后解析结果使用Convert.FromBase64String
。然后你可以使用toByteArray
转换为字节,但要注意BigInteger
是小端,BigInteger.parse
,所以你必须反转字节。
您的.NET RSA密文(您可能不应该反向)之前是00h值的字节,这使其成为无效的密文。 RSA密文必须具有密钥的确切长度(以字节为单位)。
答案 1 :(得分:1)
感谢先生们的回答和评论,它帮助我最终解决了我的问题:
对于exponent,我收到一条错误消息:Base-64 char数组或字符串的长度无效
这是因为基数64值应该是4的倍数。如果不是这种情况,我们应该附加等号(=)以达到4的倍数。
因此,在将指数字符串从"NjU1Mzc"
更改为"NjU1Mzc="
后,可以解码该值。
然后我应用了owlstead提供的解决方案。这是最终的代码,工作正常:
//Decode from base 64
byte[] publicKeyByte = Convert.FromBase64String(rsaPublicKey.modulo);
byte[] exponentByte = Convert.FromBase64String(rsaPublicKey.exponent);
//Convert to ASCII string
UTF8Encoding ByteConverter = new UTF8Encoding();
string publicKeyString = System.Text.Encoding.Default.GetString(publicKeyByte);
string exponentString = System.Text.Encoding.Default.GetString(exponentByte);
//Convert to BigInteger
BigInteger publicKeyBI = BigInteger.Parse(publicKeyString);
BigInteger exponentBI = BigInteger.Parse(exponentString);
//Convert back to byte array
byte[] publicKeyByte2 = publicKeyBI.ToByteArray();
byte[] exponentByte2 = exponentBI.ToByteArray();
//We must remove the 129th sign byte which is added when converting to BigInteger
if (publicKeyByte2.Length == 129) Array.Resize(ref publicKeyByte2, 128);
//Create crypto service
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
//Assign RSA key modulus/exponent reversing from little endian to big endian
RSAKeyInfo.Modulus = publicKeyByte2.Reverse().ToArray();
RSAKeyInfo.Exponent = exponentByte2.Reverse().ToArray();
RSA.ImportParameters(RSAKeyInfo);
//Convert password string to byte array
byte[] passwordByte = ByteConverter.GetBytes(clearPassword);
//Encrypt the password and encode 64
encryptedPassword = Convert.ToBase64String(RSA.Encrypt(passwordByte, false));
owlstead遗漏的一点是:该方法返回一个字节数组,其中包含一个额外元素,其值为零以消除正值的歧义 有关此点的更多信息,请参阅Microsoft文档:BigInteger.ToByteArray Method
此代码将密码加密为172个字符的字符串,该字符串以=符号结束,这符合我的预期,并且使用私钥在Java端正确解密。
答案 2 :(得分:0)
我试过这个并正确加密
var input = "String to Encode.";
var mod = "MTA1MzQxNzIwODA3NjUwNzg5NDY4ODU2Mjc0NDA3ODIwMjQ1ODQ5NDE1MDk1MDIzMTM3MjM0NzAwNzYzNjc2MTgwNjg3ODMxMjA3NTY0NTcxMjg2MzM4NjQ1NzEwMDcyMjY2MTQyNDIzMTczMjkwMDk0MTc0MTA5MTc5MzI0NjYwMjQ4NzI3NzM0MTQ5NDY0MjUwODkwOXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXc2OTAzNjc3NzQzODM3NzM0MjE2ODM0NjY4MjM4MTQ0OTA3MDQ3MTk1Njc1NzU3OTE2NjEyNzkzMzM2MzI3MDUyNjg0NDI5NDExNjI2MzA0MzM5==";
var exp = "NjU1Mzc=";
var intValue = int.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(exp)));
var rsaParam = new RSAParameters();
rsaParam.Modulus = Convert.FromBase64String(mod);
rsaParam.Exponent = BitConverter.GetBytes(intValue);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParam);
Console.WriteLine(Convert.ToBase64String(rsa.Encrypt(Encoding.UTF8.GetBytes(input), false)));
}
Console.ReadLine();
我认为问题是指数很奇怪。 Exp = 65537;