Geeks With Blogs
Alex's Blog-o-monium

I've been doing a bit of external API integration work lately. Sounds mundane (and it is for the most part) - but there was one notable "gem" I had to tackle. To make long story short, there's this external source written in PHP and exposed as a web service. The operations are simple and there isn't much to it, except one little detail - this service uses PHP's encryption to pass data securely between peers using SSL certificates: the operations are "openssl_seal" and "openssl_open".

Now, I'm coding in .NET and I figure: surely, there must be some sort of equivalent to support for all this and after quickly reading up on what PHP does behind the scenes, off to MSDN I go.

First alarming sign: .NET does not have native (Cryptography namespace) support for RC4 ciphers (this is used in one of the steps - and there are multiple steps, more on this later) nor is there such thing as a "pem" file reader ("pem" file is where the certificate key data is stored). This is going to be more challenging than I expected.

Alright, let's apply some google-fu and see what pops up. Searches like ".NET equivalent of PHP 'openssl_seal'" and such turned up absolutely nothing (hopefully, this will change once I'm done typing this up) but I do find an OpenSSL library implementation in .NET (well, it's more of a managed wrapper around C/C++: http://sourceforge.net/projects/openssl-net/) - it doesn't seem to have been updated for about a year but it's worth a try.

The documentation wasn't the most detailed (argh!):


openssl

..and few hours of looking, the site turned up some useful bits but not a quick-'n-easy equivalent for "openssl_seal" command. This whole thing appears to be PHP only feature (as far as I can tell) which uses some of the OpenSSL functionality.

So, what does this command do exactly? The official PHP site (http://php.net/manual/en/function.openssl-seal.php) doesn't share too much in terms of reverse-engineering, this is about all it states:

open_seal_php

But there are user comments I found more useful, one gave some good pseudo flow:

open_seal_php2

And there's a link to a Java example (aha!) - http://blog.local.ch/archive/2007/10/29/openssl-php-to-java.html. Though, this mostly deals with decrypting data - helpful, but still no luck with the 'encryptor' "openssl_seal" implementation. Another hint - it does use Bouncy Castle libraries for Java (the .NET C# version can be found at http://www.bouncycastle.org/csharp/) so I go ahead in that direction.

As expected, Bouncy Castle does not provide straightforward "openssl_seal"/"openssl_open" implementation. But it seems to have everything I'd need (or at least, I think I need) - oh, and by the way there's no easy documentation there neither. You'll need to search through trunks on Google and such to figure out finer details (for example, http://code.google.com/p/ecc-micropayment/source/browse/trunk/csharp/crypto/src/security/PbeUtilities.cs?r=2).

After whole bunch of trial and error, I got very close. I was able to generate a RC4 random 128-bit key, encrypt data using RC4 cipher and bring it back (decrypt it). However, there was one last step still missing - encrypting the above-mentioned 128-bit key using RSA cipher. It looked like I was doing it as prescribed, what could it be?!

It wasn't until I found PHP security library interop documentation for OpenSSL (http://phpseclib.sourceforge.net/interop.html#  and click on PHP Bindings: openssl_seal() / openssl_open() -> phpseclib) and one detail caught my eye - PHP line "$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); " line.

The key must be encrypted with RSA (I knew that) AND padded using PKCS1!!! Well, of course, isn't that obvious to everyone (*sarcastic argh!). 

So here's my take on all this - go ahead and grab a Visual Studio NuGet Package for "Bouncy Castle". And use following code:

For encryption, the "OPENSSL_SEAL" equivalent:

.NET "open_seal"
  1.  
  2. var randomPassword = Guid.NewGuid().ToString("n").ToCharArray();
  3.  
  4. var salt = new byte[16];
  5. for (int i = 0; i != salt.Length; i++)
  6.     salt[i] = (byte)i;
  7.  
  8. var pGen = new OpenSslPbeParametersGenerator();
  9. pGen.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(randomPassword), salt);
  10. var rc4Parameters = (KeyParameter)pGen.GenerateDerivedParameters("RC4", 128); /* generate random 128 bit key */
  11.  
  12. var rc4Cipher = CipherUtilities.GetCipher("RC4");
  13. rc4Cipher.Init(true, rc4Parameters);
  14. var rc4DataBytes = rc4Cipher.DoFinal(Encoding.ASCII.GetBytes("YOUR_DATA_STRING_TO_ENCYPT")); /* encrypt data symmetrically with RC4 using the random key */
  15.  
  16. var rsaCipher = CipherUtilities.GetCipher("RSA//PKCS1PADDING"); /* that pesky padding flag */
  17. var remReader = new PemReader(new StringReader("YOUR_STRING_OF_PUBLIC_KEY_CONTENTS_FROM_PEM_FILE"));
  18. var rsaParameters = (RsaKeyParameters)remReader.ReadObject();
  19. rsaCipher.Init(true, rsaParameters);
  20. var rsaDataBytes = rsaCipher.DoFinal(rc4Parameters.GetKey()); /* encrypt the random key itself using RSA */
  21.         
  22. .

 

As for decrypting: first, you'll need a way to parse the private key from the PEM file and generate RsaPrivateCrtKeyParameters:

PEM Private Key Reader
  1. public static RsaPrivateCrtKeyParameters GetPrivateKey(String pemFile)
  2. {
  3.     if (string.IsNullOrEmpty(pemFile))
  4.         throw new ArgumentNullException("pemFile");
  5.  
  6.     string privateKey = File.Exists(pemFile) ? File.ReadAllText(pemFile) : pemFile;
  7.  
  8.     var reader = new PemReader(new StringReader(privateKey));
  9.     RsaPrivateCrtKeyParameters privkey = null;
  10.     Object obj = reader.ReadObject();
  11.     if (obj is AsymmetricCipherKeyPair)
  12.     {
  13.         privkey = (RsaPrivateCrtKeyParameters)((AsymmetricCipherKeyPair)obj).Private;
  14.     }
  15.     return privkey;
  16. }

 

..and basically reverse the process but use the private key:

.NET "openssl_open"
  1.  
  2. var privateKeyParameters = GetPrivateKey("YOUR_STRING_OF_PRIVATE_KEY_CONTENTS_FROM_PEM_FILE");
  3.  
  4. var rsaCipher = CipherUtilities.GetCipher("RSA//PKCS1PADDING");
  5. rsaCipher.Init(false, privateKeyParameters);
  6. var keyBytes = rsaCipher.DoFinal(Convert.FromBase64String("ENCRYPTED_KEY_STRING"));                    /* decrypt key using RSA */
  7.  
  8. var rc4CipherDecrypt = CipherUtilities.GetCipher("RC4");
  9. var decryptParameter = new KeyParameter(keyBytes);
  10. rc4CipherDecrypt.Init(false, decryptParameter);
  11. var rc4DataBytesDecrypt = rc4CipherDecrypt.DoFinal(Convert.FromBase64String("ENCRYPTED_DATA_STRING")); /* decrypt data using RC4 */
  12. var dataString = Encoding.ASCII.GetString(rc4DataBytesDecrypt);
  13.  
  14.             .

 

I hope you find this useful and save yourself much headache. Cheers!

Posted on Sunday, January 27, 2013 6:19 PM | Back to top

Copyright © Strenium | Powered by: GeeksWithBlogs.net