3

I need to be able to decrypt a string on the server using C#, but the string was encrypted using public key encryption with cryptico.js on the client. For details, see context at the end.

Cryptico gives me a private RSA key like this (note - 'like' this - I created a new one for this question):

Array ( [n] => 8029845567507477803775928519657066509146751167600087041355508603090505634905205233922950527978886894355290423984597739819216469551137046641801207199138209 [e] => 3 [d] => 5353230378338318535850619013104711006097834111733391360903672402060337089936682996269976597251251223844095913209399106464214877696419418951728015128013411 [p] => 102067954277225510613941189336789903269738979633396754230261162567549753196947 [q] => 78671563708406591396117399809764267229341143260756252277657051641634753921147 [dmp1] => 68045302851483673742627459557859935513159319755597836153507441711699835464631 [dmq1] => 52447709138937727597411599873176178152894095507170834851771367761089835947431 [coeff] => 26458340158787140383846156526777567128582042036682248240414722856369310516021 

...plus a bunch of methods.

I am trying to decrypt it thusly:

                RSAParameters parameters = new RSAParameters();

            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

            parameters.Exponent = encoding.GetBytes("3");

            //dmp1
            parameters.DP =
                encoding.GetBytes("68045302851483673742627459557859935513159319755597836153507441711699835464631");

            //dmq1
            parameters.DQ =
                encoding.GetBytes("52447709138937727597411599873176178152894095507170834851771367761089835947431");

            //d
            parameters.D =
                encoding.GetBytes(
                    "5353230378338318535850619013104711006097834111733391360903672402060337089936682996269976597251251223844095913209399106464214877696419418951728015128013411");

            //p
            parameters.P =
                encoding.GetBytes("102067954277225510613941189336789903269738979633396754230261162567549753196947");

            //q
            parameters.Q =
                encoding.GetBytes("78671563708406591396117399809764267229341143260756252277657051641634753921147");

            //n
            parameters.InverseQ =
                encoding.GetBytes(
                    "8029845567507477803775928519657066509146751167600087041355508603090505634905205233922950527978886894355290423984597739819216469551137046641801207199138209");

            //coeff
            parameters.Modulus =
                encoding.GetBytes("26458340158787140383846156526777567128582042036682248240414722856369310516021");

            RSA rsa = new RSACryptoServiceProvider();
            rsa.ImportParameters(parameters);

            var decryptThis = encoding.GetBytes(ciphertext);

            var result = rsa.DecryptValue(decryptThis);

            resultString = encoding.GetString(result);

But this chucks the Exception 'Bad data'.

Has anyone more experienced with C# got any ideas where I'm going wrong?

Thanks,

G


Details of context: I am attempting to implement a password strength checking function on both the client and server side of an app, but using only code on the server side. To achieve this on the client side, I want to send the putative password to the server, judge its strength, and then return a score which is displayed on the client. This means I only have to maintain password strength checking code on the server. As an extra security measure, I am encrypting the putative password using the cryptico.js library before sending it to the server to be judged.

neubert
  • 15,947
  • 24
  • 120
  • 212
dartacus
  • 654
  • 1
  • 5
  • 16
  • I do hope you haven't shown us a real private key. – AakashM Nov 30 '12 at 12:17
  • Haha, no. Rest assurred this is a test key generated specially for this question. The passphrase for it was 'special angelic megalodon incubator' and it's 512 bits, by the way. These values will also change for the production system. – dartacus Nov 30 '12 at 12:27
  • 2
    I don't know how you generated these strings like `264583401587871403838461.....` but encoding.GetBytes doesn't seem to me right. I would try `BigInteger.Parse("..").ToByteArray()` – L.B Nov 30 '12 at 12:43
  • The strings of numbers are taken from a dump of the RSA private key object created by cryptico - their methodology is described on their github page. I tried BigInteger.Parse("..").ToByteArray(); as suggested, but no joy unfortunately. Thanks for the suggestion. – dartacus Nov 30 '12 at 14:36
  • I think you have InverseQ and Modulus swapped. – Alexander Apr 04 '13 at 11:08

1 Answers1

1

BigInteger and RSAParameter classes store numbers in different format.

BigInteger stores numbers as little-endian - this means that if you create a BigInteger to hold hex number 0xABCD (43981) - ToByteArray will return bytes {0xCD, 0xAB, 0x00} (more on extra zero later).

RSAParameter was designed to store numbers in big-endian format. It means that bytes {0xCD, 0xAB, 0x00} written into, for example, Exponent property, will be interpreted by RSA implementation as number 0xCDAB00 (13478656).

To make matters more complicated - RSAParameters are always positive numbers, while BigInteger supports sign. and same number, 0xFF - will be interpreted differently. For RSA, it is decimal 255. for BigInteger it means -1, since it is interpreting most significant bit as a sign. This is why zero is added - at construction time, BigInteger is aware that we are passing positive value, 43981. But storing this value as two bytes would produce negative number (-21555), since highest bit is set for byte 0xCD (11001101 in binary). So BigInteger adds zero to indicate positive number. Try it yourself:

Console.WriteLine(new BigInteger(new byte[]{0xCD,0xAB}));
Console.WriteLine(BitConverter.ToString(new BigInteger(0xABCD).ToByteArray()));

So, we have large integer passed to us as a decimal string "1234567...", and we want to convert it to representation used by RSA. In order to do this, we would need:

  1. Parse string to BigInteger.
  2. Extract BigInteger bytes.
  3. Make sure BigInteger did not add any leading zeros to compensate sign.
  4. Reverse array back to big endian form, and feed to RSA.

Code (using your parameters):

using System;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;

class App
{
    static void Main()
    {
      var parameters = new RSAParameters();
      parameters.Exponent=B("3"); 
      parameters.Modulus=B("8029845567507477803775928519657066509146751167600087041355508603090505634905205233922950527978886894355290423984597739819216469551137046641801207199138209"); 
      parameters.D=B("5353230378338318535850619013104711006097834111733391360903672402060337089936682996269976597251251223844095913209399106464214877696419418951728015128013411");
      parameters.P=B("102067954277225510613941189336789903269738979633396754230261162567549753196947");
      parameters.Q=B("78671563708406591396117399809764267229341143260756252277657051641634753921147");
      parameters.DP=B("68045302851483673742627459557859935513159319755597836153507441711699835464631");
      parameters.DQ=B("52447709138937727597411599873176178152894095507170834851771367761089835947431");
      parameters.InverseQ=B("26458340158787140383846156526777567128582042036682248240414722856369310516021");

      var rsa = new RSACryptoServiceProvider();
      rsa.ImportParameters(parameters);
      var ciphertext = rsa.Encrypt(Encoding.ASCII.GetBytes("Hello"), false);
      Console.WriteLine(Encoding.ASCII.GetString(rsa.Decrypt(ciphertext, false)));
    }

    static byte[] B(string s)
    {
      var b = BigInteger.Parse(s);
      var ret = b.ToByteArray();
      if (ret[ret.Length - 1] == 0) 
      {
        Array.Resize(ref ret, ret.Length - 1);
      }
      Array.Reverse(ret);
      return ret;
    }
}
Alexander
  • 4,153
  • 1
  • 24
  • 37
  • Thanks for the answer. I'll go back to the code (I've moved onto a different part of this project since Nov 2012) and try this out. – dartacus Apr 09 '13 at 09:21
  • Did anyone get this working? I'm still getting a bad data exception when calling the `rsa.ImportParameters(parameters);` – tronc Jun 20 '14 at 14:55
  • Does it throw exception with data from answer, or with your custom data? Code in answer works without exceptions for me. – Alexander Jun 20 '14 at 18:51