Encrypt RSA in C # decrypt in java

8

I am building two applications one in C# and one in Java, which will use RSA as part of the encryption and decryption that should be handled.

When I encrypt with java and decrypt in c# works perfect, the problem is when I encrypted in c# and I'm going to decrypt in java throws me:

  

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

I am using RSA with 512 as size to generate the keys on both sides, if increase to 1048 the error changes to the size should not be greater than 128.

I copy the classes with the methods I'm working with:

Java:

public String decrypt(String message, RSAPrivateKey key) {
    try {

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, key);

        byte[] dataEncripted =Base64.decodeBase64(message.getBytes());
        byte[] data = cipher.doFinal(dataEncripted);
        return new String(data,"UTF-8");
    } catch (Exception e) {
        e.printStackTrace();
    }

C #:

public static string Encrypt(string data, string publicKey)
    {
        var sb = string.Empty;
        try
        {
            var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(publicKey);
            byte[] encryptedByteArray;
            byte[] dataToEncrypt;

                dataToEncrypt = Encoding.ASCII.GetBytes(data);
                encryptedByteArray = rsa.Encrypt(dataToEncrypt, false);
                sb = Convert.ToBase64String(encryptedByteArray);


        }

What I have noticed is that in the process that works for me from java - > c# when I encrypt the bit array that is generated ranges from 0 to 63, that is 64 bits, but in the opposite process c# - > java the encrypted data is an array of bits that generates c# from 0 to 64 or 65 bits .

How could I do so that in c# the RSA Provider generates me an array of bytes as in java , or how could I solve this problem?

    
asked by Ivan Fontalvo 04.09.2016 в 03:40
source

3 answers

3

Check the keys since it could be a problem of how they are storing or reading

This is an example that could help you

Java, is made with bouncy castle

package javaapplication1;

import static com.sun.org.apache.xml.internal.serialize.OutputFormat.Defaults.Encoding;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.security.interfaces.RSAPrivateKey;
import javax.crypto.Cipher;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.asn1.*;
import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;


public class JavaApplication1 {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException,    NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
     //String de prueba encriptado en C#, las llaves las genere en c# tambien

     String hola = "3ges2OQM8SNQimvZc8LrKbtgRhXutgWj7U9QpLYLRFNmlC12FqZoS/RcHMRZ4nj/AGmBbWRqSJW8060FLKUgCszXs4K54Rhy0+P09WADPI9WJZAsPySbEboqhQldqFroXYQciGY67hzafGhtQmY04ig8/s66n/zymnXeyV34/sg=";
        String filename = "\private.Key.PEM";

        PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream(filename)));
        try {
            Security.addProvider(new BouncyCastleProvider());
            KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
            PemObject pemObject = pemReader.readPemObject();
            byte[] content = pemObject.getContent();
            PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
            PrivateKey privateKeyJ = factory.generatePrivate(privKeySpec);
            System.out.println(decrypt(hola, (RSAPrivateKey) privateKeyJ));
        } finally {
            pemReader.close();
        }    
        System.in.read();    
    }

    public static String decrypt(String message, RSAPrivateKey key) {
        try {

            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, key);

            byte[] dataEncripted = Base64.decode(message.getBytes());
            byte[] data = cipher.doFinal(dataEncripted);
            return new String(data, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

***** C # in this console code the keys are created and stored in files

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {

            var rsa = new RSACryptoServiceProvider();
            JavaKey(rsa);
            StringBuilder sb = new StringBuilder();            
            ExportPrivateKey(rsa, new StringWriter(sb));
            System.IO.File.WriteAllText("private.Key.PEM", sb.ToString());

            string pubkey = System.IO.File.ReadAllText("public.Key.cs.xml"); //rsa.ToXmlString(false);
            string prikey = System.IO.File.ReadAllText("private.Key.cs.xml"); //rsa.ToXmlString(true);

            var encr = Encrypt("hola", pubkey);
            Console.WriteLine(encr);
             Console.WriteLine(Dencrypt(encr      , prikey));

        }
        public static string Encrypt(string data, string publicKey)
        {
            var sb = string.Empty;
            try
            {
                var rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(publicKey);
                byte[] encryptedByteArray;
                byte[] dataToEncrypt;

                dataToEncrypt = Encoding.ASCII.GetBytes(data);
                encryptedByteArray = rsa.Encrypt(dataToEncrypt, false);
                sb = Convert.ToBase64String(encryptedByteArray);


            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return sb;
        }
        public static string Dencrypt(string data, string privateKey)
        {
            var sb = string.Empty;
            try
            {
                var rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(privateKey);
                byte[] encryptedByteArray;
                byte[] dataToEncrypt;

                dataToEncrypt = Convert.FromBase64String(data);
                encryptedByteArray = rsa.Decrypt(dataToEncrypt, false);
                sb = Encoding.UTF8.GetString(encryptedByteArray);


            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return sb;
        }


        private static void JavaKey(RSACryptoServiceProvider rsa)
        {           
           string serializedPublic = rsa.ToXmlString(false);
            string serializedPrivate = rsa.ToXmlString(true);

            System.IO.File.WriteAllText("public.Key.cs.xml", serializedPublic);
            System.IO.File.WriteAllText("private.Key.cs.xml", serializedPrivate);
        }


        #region http://stackoverflow.com/a/23739932/890839
        private static void ExportPrivateKey(RSACryptoServiceProvider csp, TextWriter outputStream)
        {
            if (csp.PublicOnly) throw new ArgumentException("CSP does not contain a private key", "csp");
            var parameters = csp.ExportParameters(true);
            using (var stream = new MemoryStream())
            {
                var writer = new BinaryWriter(stream);
                writer.Write((byte)0x30); // SEQUENCE
                using (var innerStream = new MemoryStream())
                {
                    var innerWriter = new BinaryWriter(innerStream);
                    EncodeIntegerBigEndian(innerWriter, new byte[] { 0x00 }); // Version
                    EncodeIntegerBigEndian(innerWriter, parameters.Modulus);
                    EncodeIntegerBigEndian(innerWriter, parameters.Exponent);
                    EncodeIntegerBigEndian(innerWriter, parameters.D);
                    EncodeIntegerBigEndian(innerWriter, parameters.P);
                    EncodeIntegerBigEndian(innerWriter, parameters.Q);
                    EncodeIntegerBigEndian(innerWriter, parameters.DP);
                    EncodeIntegerBigEndian(innerWriter, parameters.DQ);
                    EncodeIntegerBigEndian(innerWriter, parameters.InverseQ);
                    var length = (int)innerStream.Length;
                    EncodeLength(writer, length);
                    writer.Write(innerStream.GetBuffer(), 0, length);
                }

                var base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray();
                outputStream.WriteLine("-----BEGIN RSA PRIVATE KEY-----");
                // Output as Base64 with lines chopped at 64 characters
                for (var i = 0; i < base64.Length; i += 64)
                {
                    outputStream.WriteLine(base64, i, Math.Min(64, base64.Length - i));
                }
                outputStream.WriteLine("-----END RSA PRIVATE KEY-----");
            }
        }

        private static void EncodeLength(BinaryWriter stream, int length)
        {
            if (length < 0) throw new ArgumentOutOfRangeException("length", "Length must be non-negative");
            if (length < 0x80)
            {
                // Short form
                stream.Write((byte)length);
            }
            else
            {
                // Long form
                var temp = length;
                var bytesRequired = 0;
                while (temp > 0)
                {
                    temp >>= 8;
                    bytesRequired++;
                }
                stream.Write((byte)(bytesRequired | 0x80));
                for (var i = bytesRequired - 1; i >= 0; i--)
                {
                    stream.Write((byte)(length >> (8 * i) & 0xff));
                }
            }
        }

        private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bool forceUnsigned = true)
        {
            stream.Write((byte)0x02); // INTEGER
            var prefixZeros = 0;
            for (var i = 0; i < value.Length; i++)
            {
                if (value[i] != 0) break;
                prefixZeros++;
            }
            if (value.Length - prefixZeros == 0)
            {
                EncodeLength(stream, 1);
                stream.Write((byte)0);
            }
            else
            {
                if (forceUnsigned && value[prefixZeros] > 0x7f)
                {
                    // Add a prefix zero to force unsigned if the MSB is 1
                    EncodeLength(stream, value.Length - prefixZeros + 1);
                    stream.Write((byte)0);
                }
                else
                {
                    EncodeLength(stream, value.Length - prefixZeros);
                }
                for (var i = prefixZeros; i < value.Length; i++)
                {
                    stream.Write(value[i]);
                }
            }
        }
        #endregion
    }
}
    
answered by 11.10.2016 в 01:35
1

I tried to reproduce your problem, but it has been impossible for me.

When I encrypt something I get the 64 bytes that must be obtained.

var data = "123";
var sb = string.Empty;
var rsa = new RSACryptoServiceProvider(512);

byte[] encryptedByteArray;
byte[] dataToEncrypt;

dataToEncrypt = Encoding.ASCII.GetBytes(data);
encryptedByteArray = rsa.Encrypt(dataToEncrypt, false); // Tiene 64bytes. rsa.KeySize / 8
sb = Convert.ToBase64String(encryptedByteArray);

I see two weak points in the code:

  • In c # you are using ASCII encoding and in Java UTF8. You should use it.
  • In Java the bytes are ordered backwards that in c # you may have to do an Array. Reverse link

I hope I have helped.

    
answered by 11.10.2016 в 19:38
1

Unfortunately, C # does not provide any simple way to do this. But this will correctly decode a public key x509 (make sure base 64 decode the x509key parameter first).

This solution sets the size of the key that will be used in bits.

public static string EncryptRsa(string stringPublicKey, string stringDataToEncrypt)
    {
        byte[] publicKey = Convert.FromBase64String(stringPublicKey);
        using (RSACryptoServiceProvider rsa = DecodeX509PublicKey(publicKey))
        {
            byte[] dataToEncrypt = Encoding.UTF8.GetBytes(stringDataToEncrypt);
            byte[] encryptedData = rsa.Encrypt(dataToEncrypt, false);
            return Convert.ToBase64String(encryptedData);
        }
    }

    public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
    {
        byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };

        MemoryStream ms = new MemoryStream(x509key);
        BinaryReader reader = new BinaryReader(ms);

        if (reader.ReadByte() == 0x30)
            ReadASNLength(reader); //skip the size 
        else
            return null;

        int identifierSize = 0; //total length of Object Identifier section 
        if (reader.ReadByte() == 0x30)
            identifierSize = ReadASNLength(reader);
        else
            return null;

        if (reader.ReadByte() == 0x06) //is the next element an object identifier? 
        {
            int oidLength = ReadASNLength(reader);
            byte[] oidBytes = new byte[oidLength];
            reader.Read(oidBytes, 0, oidBytes.Length);
            if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1? 
                return null;

            int remainingBytes = identifierSize - 2 - oidBytes.Length;
            reader.ReadBytes(remainingBytes);
        }

        if (reader.ReadByte() == 0x03) //is the next element a bit string? 
        {
            ReadASNLength(reader); //skip the size 
            reader.ReadByte(); //skip unused bits indicator 
            if (reader.ReadByte() == 0x30)
            {
                ReadASNLength(reader); //skip the size 
                if (reader.ReadByte() == 0x02) //is it an integer? 
                {
                    int modulusSize = ReadASNLength(reader);
                    byte[] modulus = new byte[modulusSize];
                    reader.Read(modulus, 0, modulus.Length);
                    if (modulus[0] == 0x00) //strip off the first byte if it's 0 
                    {
                        byte[] tempModulus = new byte[modulus.Length - 1];
                        Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
                        modulus = tempModulus;
                    }

                    if (reader.ReadByte() == 0x02) //is it an integer? 
                    {
                        int exponentSize = ReadASNLength(reader);
                        byte[] exponent = new byte[exponentSize];
                        reader.Read(exponent, 0, exponent.Length);

                        RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024);
                        RSAParameters RSAKeyInfo = new RSAParameters();
                        RSAKeyInfo.Modulus = modulus;
                        RSAKeyInfo.Exponent = exponent;
                        RSA.ImportParameters(RSAKeyInfo);
                        return RSA;
                    }
                }
            }
        }
        return null;
    }

    public static int ReadASNLength(BinaryReader reader)
    {
        //Note: this method only reads lengths up to 4 bytes long as 
        //this is satisfactory for the majority of situations. 
        int length = reader.ReadByte();
        if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte 
        {
            int count = length & 0x0000000f;
            byte[] lengthBytes = new byte[4];
            reader.Read(lengthBytes, 4 - count, count);
            Array.Reverse(lengthBytes); // 
            length = BitConverter.ToInt32(lengthBytes, 0);
        }
        return length;
    }
    
answered by 04.10.2018 в 17:05