2

I'm trying to encrypt data in my C# client with public RSA key from the Java server. To do this I generated KeyPair in Java

KeyPairGenerator.java

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
Key pub = kp.getPublic();
Key pvt = kp.getPrivate();

String outFile = "rsa_key";
FileOutputStream outPvt = new FileOutputStream(outFile + ".key");
outPvt.write(pvt.getEncoded());
outPvt.close();

FileOutputStream outPub = new FileOutputStream(outFile + ".pub");
outPub.write(pub.getEncoded());
outPub.close();

It gave me two files (rsa_key.key and rsa_key.pub) and then I encoded public key with Base64 so C# could read it:

PublicKeyBase64.java

String keyFile2 = "rsa_key.pub";
Path path2 = Paths.get(keyFile2);
byte[] bytes2 = Files.readAllBytes(path2);
X509EncodedKeySpec ks2 = new X509EncodedKeySpec(bytes2);
KeyFactory kf2 = KeyFactory.getInstance("RSA");
PublicKey pub = kf2.generatePublic(ks2);

Base64.Encoder encoder = Base64.getEncoder();
String outFile = "en_rsa_key";
Writer out = new FileWriter(outFile + ".pub");
out.write(encoder.encodeToString(pub.getEncoded()));
out.close();

Then I created my C# class to encrypt data

Encrypter.cs

class Encrypter
{
    private const string PATH = "..\\..\\key\\en_rsa_key.pub";
    private string PublicKey;

    public Encrypter()
    {
        PublicKey = File.ReadAllText(PATH);
        Console.WriteLine(PublicKey.Length);
    }

    public string encryptData(string dataToEncrypt)
    {
        Asn1Object obj = Asn1Object.FromByteArray(Convert.FromBase64String(PublicKey));
        DerSequence publicKeySequence = (DerSequence)obj;

        DerBitString encodedPublicKey = (DerBitString)publicKeySequence[1];
        DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(encodedPublicKey.GetBytes());

        DerInteger modulus = (DerInteger)publicKey[0];
        DerInteger exponent = (DerInteger)publicKey[1];
        RsaKeyParameters keyParameters = new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
        RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
        rsa.ImportParameters(parameters);
        //Console.WriteLine(dataToEncrypt);
        byte[] encryptedData = rsa.Encrypt(Encoding.UTF8.GetBytes(dataToEncrypt), true);
        //Console.WriteLine(Convert.ToBase64String(encryptedData));
        return Convert.ToBase64String(encryptedData);
    }
}

And at last my Decrypter class


public class Decrypter {

    private static final String PATH = "I:\\rsa_key.key";
    private File privateKeyFile = new File(PATH);
    private RSAPrivateKey privateKey;

    public Decrypter() throws Exception {
        DataInputStream dis = new DataInputStream(new FileInputStream(privateKeyFile));
        byte[] privateKeyBytes = new byte[(int)privateKeyFile.length()];
        dis.read(privateKeyBytes);
        dis.close();
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privSpec);
    }

    public String decrypt(String data) throws Exception{ 
        Cipher cipher = Cipher.getInstance("RSA"); 
        cipher.init(Cipher.ENCRYPT_MODE, privateKey); 
        return new String(cipher.doFinal(data.getBytes())); 
    }
}

I send encrypted data in C# via WebSocket and receive it without any problem because data while sending and receiving is the same. The problem is that data length is 344 characters (bytes) so when I decrypt my data it shows me and error: javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes I'm using 2048 bit key so it's 256B - 11B for padding that's why it's 245B. But the problem is that it always generates 344B and it doesn't depend on message to encode length. For example encoding "A" gives 344B and "Hello world" also 344B.

In this topic people say to use symmetric key, Encrypt the data with the symmetric key and then Encrypt the symmetric key with rsa. But it won't help because any encrypted string in my code has 344B size, so I won't be able to decrypt encrypted symmetric key.

What's wrong with my code?

kszulchs
  • 114
  • 10
  • The 334 bytes you see I guess is because you are looking at the Base64 encoded output text length not the actual `encryptedData` buffer length - which will not exceed 256 bytes with a 2048 bit RSA key. As you say the way to deal with this is to use RSA to encrypt say an AES-256 key which at 32 bytes is no problem for RSA. – Alex K. Jul 20 '18 at 12:03
  • Length of my public key is 393 - I checked it in class constructor just after reading key file – kszulchs Jul 20 '18 at 12:09
  • Length property returns number of characters so each character is a byte - 393 bytes then – kszulchs Jul 20 '18 at 12:15
  • 1
    The length of the raw .pub file is not the length of the key, the file content will likely be PKCS#8 encoded, so Base64 with --delimiters-- which is longer than the binary key data it actually encodes. – Alex K. Jul 20 '18 at 12:19
  • 1
    yes, but it doen't define public key length. Check https://8gwifi.org/RSAFunctionality?keysize=2048 . It also generates that long public key. – kszulchs Jul 20 '18 at 12:20
  • The data is 334-bytes which is 2672-bits and the key is 2048-bits. RSA data must be shorter than the key but it is not so0 that will not work. – zaph Jul 20 '18 at 12:20
  • The key is 2048 bits, but the pub file & the data in your link are that 2048 bits Base64 encoded to get a textual representation of the otherwise unprintable key, this representation is always longer than the binary data it encodes. – Alex K. Jul 20 '18 at 12:23
  • 1. The code `kpg.initialize(2048);` defines the key size. 2. Why are you using RSA (asymmetric encryption) and not AES (symmetric encryption)? AES does not have a size limitation and is much faster. Asymmetric encryption is generally used to encrypt keys and symmetric encryption to encryption data. 3. If you mis use RSA with data that that is to large then the solution is hybrid encryption. 4. Why not just rely on SSL/TLS/HTTPS to secure the data in transit? – zaph Jul 20 '18 at 12:23
  • I'd like to encrypt symmetric key but I won't be able with this code – kszulchs Jul 20 '18 at 12:25
  • Why must you use "this code"? It will not work for data longer then the key and the data is longer them the key. – zaph Jul 20 '18 at 12:27
  • That's why I put https://stackoverflow.com/questions/10007147/getting-a-illegalblocksizeexception-data-must-not-be-longer-than-256-bytes-when topic in my post. I can't do steps in this topic because anything I encode is 344bytes large. I don't have to use this code. But I didn't see code that works in my case. Also I'm not using HTTPS because I can't create better websocket client in C#. It should be STOMP client and I couldn't establish connection and had many problems with that. – kszulchs Jul 20 '18 at 12:33
  • `cipher.init(Cipher.ENCRYPT_MODE` in a method named `decrypt` sounds suspect. Your C# code is doing RSA with OAEP-SHA1 padding, what does your Java code expect? – bartonjs Jul 20 '18 at 16:17

0 Answers0