Using this with PHP generated keys

Nov 30, 2010 at 2:47 PM

I would like to use this in conjunction with a PHP script in which:

  1. The keys are generated with PHP.
  2. The public key is passed to a Silverlight app
  3. The SL app encrypts using public key
  4. PHP decrypts using private key.

Is there a compatible PHP RSA implemenattion to achieve this?

Thanks

Tom

 

Coordinator
Nov 30, 2010 at 3:13 PM
Edited Nov 30, 2010 at 3:30 PM

I would have to look around.  As long as the PHP function generates valid keys I don't see how it would be a problem.  For encryption with this library you only need the public exponent (E) value and the public modulus (N) value.  So you could export your keys base-64 encoded from your PHP code, then decode them to a byte array and populate the N and E values of an RSAParameters object to load them into this library.

Could you generate a test key with your PHP class and post it here so I can see how it stores the keys?  Then I could provide you with the proper C# or VB code to import the key into the RSACrypto class.  Also, it is important to know what kind of padding is being used by your PHP implementation (PKCS, OAEP, etc).

Nov 30, 2010 at 4:24 PM

I have found these two PHP implemetations:

 http://scripts.ringsworld.com/development-tools/rsa1.3/rsa.php.html

http://phpseclib.sourceforge.net/

The first one produces this:

Keys:
P = 9277
Q = 7817
N = 72518309 - modulo
PI = 72501216
E = 7829 - public key
D = 52174205 - private key

Debug: 13535351


Test ASCII(32) - ASCII(126): Message: AAA Encoded: 53648687 Decoded: AAA Success: True
The second one is more complicated and I don't have a sample output.
Thanks
Tom
Coordinator
Nov 30, 2010 at 4:53 PM
Edited Nov 30, 2010 at 4:55 PM

I'll take a look at those two implementations and see what I can come up with, but I can tell you that the keys being produced by the first script are waaaay waay too small.

Edit:  I looked at the ringsworld script and you don't want to use that one.  All it's doing is picking two prime numbers from the list of known primes smaller than 10k and using them, which provides you with grossly insufficient encryption and it's not really a good script.  I'll look at the other and see if I can find a better script for you.

Dec 1, 2010 at 1:21 AM

This is what I get from the second one (phpseclib):

plaintext terrafrost ciphertext rc�>��Д�[W�3g���b+��E7sSr��H^��k��t���8 ~��BI���PH�!� ��Q_�,w��Ų�:�������S�QSs*�V�%��X[U^�d�&�A�Q������Je�SV%A privatekey -----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQCFIxIS/ln/3q/l9hAnXYbYmYIcLt0ftUK4ZxRR3+vWv/qWhaji0gg4mQ2KnpIT vw76LGHebMoVtcmlyXiFkwb0IDz7lyIoBdYAZrH7RDHVghe9vE3sd0d5cNUPvrpDsEqgJUOLxZyO xgPzhNbJTapWt+VRselzT7b2TmR7FB0rRwIDAQABAoGAN9CbHDFb48Qk9ABzhz1TcezxOgV/mHJy NUKppyV8BrzXPlFsmyK4ZZnsrmeKEBMyITmIEiqDkhRzJDjReRZJfGYUS49VnDGoeh+ViNpmc8tW OM8Qp7sPw45odQsLNkxn3DxgxYRxHzyA5blxTle90rtkgatljWWymFbnWYoFzkkCQQCuIncoqP7m Os+flEH6/mDyfHPjJjn/lCCJmoyVgqwXqzA0Rpcs0BBBxR2ludKUIqf+uf8gR4vkN4qhc96gGBIN AkEAw7pwH2mFVjz2zakEaf73uergsmiH16XxX8w09DwxHaqyO8x6Jx5cfgno2SZNTZWixFHz21UQ YKlXCFHylM4howJAJSPgumFhDIXaprSeZgQ/dTiqFvaeRC65yd+WwITAY8P1YhD0SXCPJxf9vM0n ZsUFg8Tz71STN7fI76qg/xBjOQJAA5fBhSIXu3yE/0Nx/Ewc2gHQvBWlILVAGAUUl8MfF69RdD2N pHgvNHUUHCBS3PC5OydR9Z27HjsdiN6QOGEMRwJAZAyqSV4fVjFuM2z7ncCBj1WkrndjqAvD6jYq b/+2X0tERYnSIvgVuA7mppocNeUHNX77iaRXVVdevo08PYl68A== -----END RSA PRIVATE KEY----- publickey -----BEGIN PUBLIC KEY----- MIGJAoGBAIUjEhL+Wf/er+X2ECddhtiZghwu3R+1QrhnFFHf69a/+paFqOLSCDiZDYqekhO/Dvos Yd5syhW1yaXJeIWTBvQgPPuXIigF1gBmsftEMdWCF728Tex3R3lw1Q++ukOwSqAlQ4vFnI7GA/OE 1slNqla35VGx6XNPtvZOZHsUHStHAgMBAAE= -----END PUBLIC KEY-----

This is the PHP that generates that reult:

<?php
 include('Crypt/RSA.php');
     $rsa = new Crypt_RSA();
     extract($rsa->createKey());
     $plaintext = 'terrafrost';
     $rsa->loadKey($privatekey);
     $ciphertext = $rsa->encrypt($plaintext);
     $rsa->loadKey($publickey);
  echo "plaintext\n";
  echo $plaintext . "\n";
  echo "ciphertext\n";
  echo $ciphertext . "\n";
  echo "privatekey\n";
  echo $privatekey . "\n";
  echo "publickey\n";
  echo $publickey . "\n";
     //echo $rsa->decrypt($ciphertext);
?>

Thanks

Tom

 

Coordinator
Dec 1, 2010 at 2:03 AM

Yeah, I read through the documentation.  It outputs the keys in PKCS format.  I'll see if I can make some time to add FromPKCSString and ToPKCSString methods for import and export with your format.

Dec 15, 2010 at 7:54 PM

Have you had a chance to work on this?

Thanks

Tom

 

Coordinator
Dec 15, 2010 at 11:38 PM
I haven't yet... does the PHP class give you the option to get the individual values of E, N, D, P, Q, etc?

On Wed, Dec 15, 2010 at 2:54 PM, tomgiam <notifications@codeplex.com> wrote:

From: tomgiam

Have you had a chance to work on this?

Thanks

Tom

Read the full discussion online.

To add a post to this discussion, reply to this email (scrypt@discussions.codeplex.com)

To start a new discussion for this project, email scrypt@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com


Dec 16, 2010 at 12:10 AM

This is what I found:

modulus:89979131010449733055731466340030907952127252056298539795841872593459072051342594531780082610242068977643798454881825942618699992740578749483225107013308859741019218001531035719593250396156028152262862911046028991221178995860352661026311379005297199898136068198714959613491299221573590319458982292437895709103 publicExponent:65537 privateExponent:21033618979814302003659094318160329429583128637296391804206745626619970151617689980116130820588499576384378173074592572759181590380787439798632965186747813095301267575945307456262025117911210273938814254988913926800994014139728673331310581904153882197950321757972752809290356116633762688045994518752311500673 prime1:6981384724685165285311905681392000646045029090147325946660754838416001970994014258157839770287540914399112130115210037520827812684712848623637788616456423 prime2:12888436113869581963543389635573832471681442123297061567185508328523417428790764396458355309355072164865101450360012953643353563326932558769700636861575161 exponent1:4321860318067684526151476652879363690902740058537424065519835887351810671303205097391118258698686580680934101818883782172780340699627462067375813997562499 exponent2:6561525107027459331577966571416465819577510664873376248365724482066372011407355449407709002496787811171156902074581262467163459426020489545616396064611673 coefficient:6278752410248971113625124675118189646279456642714705920966863304047908505791722264256221676174899220169939851443143535459213350606776240941336128359124133

Does that help?

Thanks

Tom

 

Coordinator
Dec 16, 2010 at 1:08 AM

Sure does.  The two values you need are "Modulus" and "Public Exponent".  Use the string values for those numbers, and then this should work. 
Create a new RSAParameters object:

RSAParameters objP = new RSAParameters();

Then, you'll have two string values that represent your numbers, strModulus and strExponent.  So you'll setup the parameters like so:
objP.E = System.Text.Encoding.UTF8.GetByes(strExponent);
and
objP.N = System.Text.Encoding.UTF8.GetBytes(strModulus);

Then, you will import those into an RSACrypto object.

RSACrypto objCrypt = new RSACrypto();
objCrypt.ImportParameters(objP);

That should be all there is to it.  Now you should be able to encrypt with Scrypt and decrypt in your PHP app. 
Make sure you know what kind of padding is used by the PHP scrypt though.  By default Scrypt uses OAEP but you can change that to PKCS1.
One other thing to note... I believe .NET uses a different endian order for storing byte arrays...so compatibility with other encryption can be shakey.
I haven't tested this, but for instance, if you encrypt data with Java, it's possible that after you load the byte data, you may have to reverse the
array before decrypting.  You may run into that with your PHP code as well.  If you encrypt with Scrypt and your decrypted data does not
produce the correct result, try calling an Array.Reverse(); on the encrypted data before sending it to your PHP code and see if that changes
anything.  Give this a shot and please let me know either way if it works or doesn't.  If it's not working, I can try to help you out further.

Coordinator
Dec 16, 2010 at 1:28 AM

It just dawned on me that the above may not be entirely accurate.  You can't just load the byte data for the large number because it doesn't represent the actual
number itself.  I made another change and made parts of the internal BigInteger class public.  The BigInteger class will allow you to transform the string representation
of your number into the actual numerical value.  There is a constructor that accepts a string and a radix.  Radix is the base of your number, so in your case it's Base 10.
You'll want to do the following:

BigInteger modulus = new BigInteger(strModulus, 10);
BigInteger pubExp = new BigInteger(strExponent, 10);

Now, instead of using the text encoding to get the bytes, you'll set the parameters as follows:

objP.N = modulus.getBytesRaw();
objP.E = pubExp.getBytesRaw();

Sorry about the casing on the method, but I didn't write the original BigInteger class so I followed the convention used for his original getBytes method which used a
camel cased method instead of pascal casing.  Anyway, give the above a try.  BigInteger is in the Utils namespace in the library.  The latest changeset includes the
changes if you want the code, and the 1.0.2.1 Beta release has the "Release" compiled binary and associated XML documentation file.

Jan 20, 2011 at 12:44 AM

Sorry, I was away for vacation and just now getting back to this.

I tried this code and got and "Invalid key" on the next to last line: byte[] decBytes = objCrypt.Decrypt(encBytes);


            string imodulus = "89979131010449733055731466340030907952127252056298539795841872593459072051342594531780082610242068977643798454881825942618699992740578749483225107013308859741019218001531035719593250396156028152262862911046028991221178995860352661026311379005297199898136068198714959613491299221573590319458982292437895709103";
            string ipublicExponent = "65537";
 
            RSAParameters objP = new RSAParameters();
            BigInteger modulus = new BigInteger(imodulus, 10);
            BigInteger pubExp = new BigInteger(ipublicExponent, 10);

            objP.N = modulus.getBytesRaw();
            objP.E = pubExp.getBytesRaw();
        
            RSACrypto objCrypt = new RSACrypto();
            objCrypt.ImportParameters(objP);
            //Convert your  text to a byte array
            byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes("terrafrost");  
            //Encrypt your raw bytes and return the encrypted bytes
            byte[] encBytes = objCrypt.Encrypt(rawBytes);
            //Now decrypt your encrypted bytes
            byte[] decBytes = objCrypt.Decrypt(encBytes);
            //Convert your decrypted bytes back to a string
            string result = System.Text.Encoding.UTF8.GetString(decBytes, 0, decBytes.Length);

Thanks

Tom

 

Coordinator
Jan 20, 2011 at 1:40 PM

Not a problem.  The reason you got the error is because you're trying to call a Decrypt operation but the parameters
you've supplied are for a Public Key.  I guess I'm not sure I understand what you're trying to do.  You should be Encrypting
with the public key and Decrypting with the private key.  Decrypting with the public key does not offer you any security.

With Scrypt it is certainly possible to encrypt with the private and decrypt with the public...I added the feature for those fringe
cases where someone might want to do it... in which case you would call DecryptPublic( ) not Decrypt( ), but I would advise against it. 
If what you're tryinng to do is verify the source of the data, you should use Sign and Verify.  But if you're truly encrypting with PHP and
decrypting with Silverlight, then you need to supply the parameters for the PRIVATE key in Silverlight.

I see what you're trying to do above is do both an Encrypt and a Decrypt, but that's not going to work exactly like that.  You supplied the Public key
data only, so you can encrypt just fine...but if you want to Decrypt that in Silverlight, you're going to need to supply the Private key variables as well. 
Since they are missing, that's why you got the "Invalid Key" error when you tried to Decrypt.

Jan 20, 2011 at 3:59 PM
Yes, in my application I will only encrypt with Silverlight using the public key received from PHP and decrypt with PHP using the private key. I was just experimenting with decrypt to verify it all on SL, but I understand now why that won't work.

So to test it I will send the encrypted string to PHP and decrypt there.

Just for curiosity how do you specify the private key to use in SL?

BTW, is the source to your online demo program available?

Thanks
Tom


dustinhorne <notifications@codeplex.com> wrote:

>From: dustinhorne
>
>Not a problem. The reason you got the error is because you're trying to call a Decrypt operation but the parameters
>you've supplied are for a Public Key. I guess I'm not sure I understand what you're trying to do. You should be Encrypting
>with the public key and Decrypting with the private key. Decrypting with the public key does not offer you any security.
>
>With Scrypt it is certainly possible to encrypt with the private and decrypt with the public...I added the feature for those fringe
>cases where someone might want to do it... in which case you would call DecryptPublic( ) not Decrypt( ), but I would advise against it.
>If what you're tryinng to do is verify the source of the data, you should use Sign and Verify. But if you're truly encrypting with PHP and
>decrypting with Silverlight, then you need to supply the parameters for the PRIVATE key in Silverlight.I see what you're trying to do above is do both an Encrypt and a Decrypt, but that's not going to work exactly like that. You supplied the Public key
>data only, so you can encrypt just fine...but if you want to Decrypt that in Silverlight, you're going to need to supply the Private key variables as well.
>Since they are missing, that's why you got the "Invalid Key" error when you tried to Decrypt.
>
>
Coordinator
Jan 20, 2011 at 4:49 PM

For the private key, you'll also need to specify the D value in the parameters.  That's the "privateExponent" value from your example above. 
I'll give you more information a bit later this afternoon when i've got time to put it together.  If you want to pass the encrypted data to your
PHP code as a string, you'll need to Base64 encode it in Silverlight and then pass it to your scrypt.  On the PHP side you'll need to do a
Base64 Decode to get the data to decrypt.

I'll also try to put up the source for my sample application for download this afternoon.

Thanks,

Dustin

Jan 20, 2011 at 11:25 PM

Dustin,

I have a question.  in the output from the PHP script above it says that the public key is:

MIGJAoGBAIUjEhL+Wf/er+X2ECddhtiZghwu3R+1QrhnFFHf69a/+paFqOLSCDiZDYqekhO/Dvos Yd5syhW1yaXJeIWTBvQgPPuXIigF1gBmsftEMdWCF728Tex3R3lw1Q++ukOwSqAlQ4vFnI7GA/OE 1slNqla35VGx6XNPtvZOZHsUHStHAgMBAAE=

Shouldn't I be using that somewhere on the SL side?

I tried this code:

 string imodulus = "89979131010449733055731466340030907952127252056298539795841872593459072051342594531780082610242068977643798454881825942618699992740578749483225107013308859741019218001531035719593250396156028152262862911046028991221178995860352661026311379005297199898136068198714959613491299221573590319458982292437895709103";
            string ipublicExponent = "65537";
            //string iprivateExponent = "21033618979814302003659094318160329429583128637296391804206745626619970151617689980116130820588499576384378173074592572759181590380787439798632965186747813095301267575945307456262025117911210273938814254988913926800994014139728673331310581904153882197950321757972752809290356116633762688045994518752311500673";
            RSAParameters objP = new RSAParameters();
            BigInteger modulus = new BigInteger(imodulus, 10);
            BigInteger pubExp = new BigInteger(ipublicExponent, 10);
            //BigInteger privExp = new BigInteger(iprivateExponent, 10);

            objP.N = modulus.getBytesRaw();
            objP.E = pubExp.getBytesRaw();
            //objP.D = privExp.getBytesRaw();
        
            RSACrypto objCrypt = new RSACrypto();
           
            objCrypt.ImportParameters(objP);
            //Convert your  text to a byte array
            byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes("terrafrost");  
            //Encrypt your raw bytes and return the encrypted bytes
            byte[] encBytes = objCrypt.Encrypt(rawBytes);

            string encString = System.Convert.ToBase64String(encBytes);

but the encString does not match anything from the PHP output and when I try to decrypt it from the PHP side it complains.

I'm just trying to encode a string with the public key generated from the PHP script so that the PHP script can decode it.

Thanks

Tom

 

Coordinator
Jan 21, 2011 at 1:34 PM

Regrading the public key... it displays like that because it's Base64 encoded. 

For your decryption, let's try something.  There could be an endienness issue, meaning your
PHP scrypt expects the byte data to be in a different order.  After you encrypt the data, try an
Array.Reverse on the encrypted bytes before you Base64 encode and send to your PHP scrypt. 
See if that makes your data decrypt properly.  Also, make sure you're using the same padding scheme.

Jan 23, 2011 at 1:44 AM

I tried Array.Reverse, but I'm still getting decryption errors.

I looked at the padding scheme in the PHP script and it is OAEP, which I believe is the same as the C# code.

I'm pretty sure that the cipher strength is 1024 in the PHP script.

Is there anything else I can check?

Thanks

Tom

 

Coordinator
Jan 26, 2011 at 6:37 PM
Edited Jan 26, 2011 at 6:38 PM

I don't know what to tell you... either you're doing something incorrectly or the PHP script you're using does not support
OAEP padding in the standard way.  I have actually seen the latter in other scripts, where people don't actually pad the
data to the correct length before encrypting, or they pad it to the correct length but they use some kind of static
padding instead of truly generating dynamic padding as you're supposed to do with OAEP.  To give you a bit more
explanation on that, dynamic padding is used to alter the encrypted value.  The padding is added to your original data
to force the data to always be of a specific length before encrypting.  The padding bytes are added along with markers
to verify the type of padding and where the padding starts or ends. 

Now, I won't get into the details of how the random
padding is generated because there is a bit more involved than just generating a random number, but the essence of it
is this... the padding is such that once you've decrypted the data, you can identify the padding and easily remove it to
produce the original value.  The catch is that by using random padding, you can encrypt the same string thousands of times
and the result will be different every time because the actual value being encrypted includes the random pattern (the padding). 
This makes it difficult for anyone trying to crack your encryption because they can't determine the actual length of your
unencrypted data since it's always padded to a fixed length depending on your key size.  Also, imagine this:

A cracker knows you've encrypted your data without padding.  Let's say they have access to the encrypted data, and at some
later point also have access to the unencrypted data.  If no padding was used for the encrypted data, the result after encryption
is the same every time, so the cracker can simply generate new keys (or follow a pattern) over and over, re-encrypting the known
string each time.  Eventually they will find the key-pair that performs the encryption and decryption.

By padding the original data, the encrypted value is different each time, so even if the cracker does happen to generate the same
key that you used, the odds that they'll also generate the same random padding that will make the encrypted values match are
infintecimal.

Ok, I know that doesn't answer your question, but it should explain a bit more what's going on with the padding.  I think you
may need to consult a security expert or do some more research and find a compatible PHP encryption library, or get a better
understanding of what's going on behind the scenes so you can troubleshoot.

~Dustin