简体   繁体   English

NET的Java RSA / ECB / PKCS1Padding加密

[英]Java RSA/ECB/PKCS1Padding encryption with .NET

I need to encrypt a password on a web site using a public key. 我需要使用公共密钥来加密网站上的密码。 The public key is delivered through a Java web service with the information: The key is a RSA key to be used with RSA/ECB/PKCS1Padding algorithm. 公用密钥通过具有以下信息的Java Web服务传递:密钥是将与RSA / ECB / PKCS1Padding算法一起使用的RSA密钥。 The public key is delivered as JSON in the form: 公钥以JSON形式提供:

{
    "kid":"PWD",
    "kty":"RSA",
    "use":"enc",
    "n":"MTA0OTgzNjg0OTMxMzE2NjkwNTU4Mjg3NDIwMDg1NTY0ODEyMjg1MDk2NTcwNzU5NDIzNzM0O
    DA3OTA2MzA0MDczNTU0NDQ2Njg3ODY2ODk2NTk0NjYzNTAxMzg0NzE1OTExMjA0MjU1MzMzOTIzMjA
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    zcwMjA3MzQxOTcwNzc4NDAwNzM3MTY2NDMyNzIwNjMwMDQwOTMwOTQ0MTA2OTE1NDEzMDAwNTMyMTE
    5ODM0MTA2MjAzMDIyODEwMjYyMDM3MDQ0NzkxNzIzNTU1MjQyNjYxMzE2ODc4OTc5NzY1OTgzMjg4M
    zQ0NDc3OTYwNTg3MzE2NTUwMDgx",
    "e":"NjU1Mzc"
 }

I tried to encrypt the password using the public key but the key generated is not correct. 我尝试使用公共密钥加密密码,但是生成的密钥不正确。 Here is my code: 这是我的代码:

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);
    }

(The public key is different between the JSON and my code but don't pay attention to it, I just copied information from different sources) (JSON和我的代码之间的公钥有所不同,但是不用注意,我只是从不同来源复制信息)

  • The encrypted password I obtained is far too long: The encrypted password should be 172 characters (I know because I have a small Java program that allows me to encrypt passwords correctly) but I get 1100 characters. 我获得的加密密码太长了:加密密码应该为172个字符(我知道,因为我有一个小的Java程序,可以正确加密密码),但是我得到了1100个字符。
  • I do not use the exponent: Should I ? 我不使用指数:应该吗?
  • Could I not use the JSON to directly configure the RSACryptoServiceProvider correctly ? 我不能使用JSON直接正确配置RSACryptoServiceProvider吗?

The answer from owlstead helped me to get an encrypted password with the right string size but the service using the encrypted string reject it with the message: javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes owlstead的答案帮助我获得了具有正确字符串大小的加密密码,但是使用该加密字符串的服务拒绝了该消息,并显示以下消息: javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes

I otained the code of the java program wich is doing the correct encryption (see below). 我获得了执行正确加密的Java程序的代码(请参见下文)。 I need to achieve the same encryption using .NET. 我需要使用.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;
    }

}

The steps I accomplished with the help of owlstead are in the answer below. 我在owlstead的帮助下完成的步骤在下面的答案中。 When I use this Java program to encode the string Password01 I obtain a string like: 当我使用此Java程序对字符串Password01进行编码时,我将获得类似以下的字符串:

sy5/XElHvuYA4Rbq8OBydLymT82R+z77jy6MU2sNal7VenzPEbARgy7p3zWgYgG13Cypk+zbnnB5L37fVUhgOgCqCyLtikzxJBNmPyzUK9+beiHJKyONZwifDzQ44hXTeXcZ0bmF9b5dLFy9QS/N5m28vIyBSGY8K2B7EB2FF38=  

This encrypted password can be decrypted on Java side 可以在Java端解密此加密密码

When I use the .NET code the string is like: 当我使用.NET代码时,字符串类似于:

ACZXYj/KudyxKBB510SxSouKaVwssmEUM6Jpreh8jTtrIH9eGb18GIdkBU7rXzMuLYbAhyREbFLbR87zW/2DNa4tN97FOlqr6k1JppJ/SSS/9fGdMvSOAQbWjsxksDH7fRu9dCvK0m0exFtGfXG7Yua9bB1m0dTNiwCZUZM0LnEM  

This encrypted password failed to be decrypted on Java side. 此加密密码无法在Java端解密。 Failed with the error message: 出现错误消息失败:

javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes

You first need to perform base 64 decoding using Convert.FromBase64String of n and e , convert the result from ASCII encoding to a string and then parse the result using BigInteger.parse . 您首先需要使用ne Convert.FromBase64String执行base 64解码, 将结果从ASCII编码转换为字符串 ,然后使用BigInteger.parse解析结果。 Then you can convert using toByteArray to bytes, but beware that BigInteger is little endian, and RSAParameters expects big endian , so you have to reverse the bytes. 然后,您可以使用toByteArray转换为字节,但是请注意, BigInteger是小字节序, 并且RSAParameters期望使用大字节序 ,因此您必须反转字节。

Your .NET RSA ciphertext (which you probably shouldn't reverse) is preceded by a 00h valued byte, which makes it an invalid ciphertext. 您的.NET RSA密文(您可能不应该反向)前面带有一个00h值的字节,这使其成为无效的密文。 RSA ciphertext must be of the exact length in bytes of the key. RSA密文必须具有密钥的确切长度(以字节为单位)。

Thank you sirs for the answers and comments, it help me to finally solve my issue: 谢谢您先生的回答和评论,它可以帮助我最终解决我的问题:

For exponent, I got an error message: Invalid length for a Base-64 char array or string This is because a base 64 value should be a multiple of 4. If this is not the case we should append equal sign (=) to reach a multiple of 4. Thus after changing the exponent string from "NjU1Mzc" to "NjU1Mzc=" the value can be decoded. 对于指数,我收到一条错误消息:Base-64 char数组或字符串的长度无效这是因为base 64值应为4的倍数。如果不是这种情况,我们应附加等号(=)以达到因此,可以将指数字符串从"NjU1Mzc"更改为"NjU1Mzc="

Then I applied the solution provided by owlstead. 然后,我应用了owlstead提供的解决方案。 Here is the final code which works fine: 这是正常工作的最终代码:

//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));

The missing point from owlstead is that: the method returns a byte array with an extra element whose value is zero to disambiguate positive values See Microsoft documentation for more information on this point: BigInteger.ToByteArray Method owlstead缺少的一点是:该方法返回一个字节数组,该数组带有一个值为零的多余元素以消除正值的歧义。有关此点,请参阅Microsoft文档以获取更多信息: BigInteger.ToByteArray方法

This code encrypts the password as a 172 characters string which ends with a = sign which is what I expected and it is correctly decrypted on Java side with the private key. 此代码将密码加密为172个字符的字符串,并以=号结尾(这是我期望的),并在Java端使用私钥正确对其进行了解密。

I tried this and it encrypts correctly 我试过了,它正确加密

    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();

I think the issue is the exponent is strange. 我认为问题是指数很奇怪。 Exp = 65537; Exp = 65537;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM