A: 

I believe you've answered the question yourself. The problem most certainly lies within the endianness.

This is a possible way of writing two-way conversion methods:

short convert_short(short in)
{
 short out;
 char *p_in = (char *) ∈
 char *p_out = (char *) &out;
 p_out[0] = p_in[1];
 p_out[1] = p_in[0];  
 return out;
}

long convert_long(long in)
{
 long out;
 char *p_in = (char *) ∈
 char *p_out = (char *) &out;
 p_out[0] = p_in[3];
 p_out[1] = p_in[2];
 p_out[2] = p_in[1];
 p_out[3] = p_in[0];  
 return out;
}

This might be a good resource for you (other than wikipedia): http://betterexplained.com/articles/understanding-big-and-little-endian-byte-order/

nicktmro
That's great to hear and thanks for the response! Forgive my ignorance, but how exactly would I make use of these conversion methods? I assume they're for on the iPhone so where would I plug it into my method?
bbrown
Turns out that the endianness between the platforms is a relic of the PowerPC days. Mac OS X, being based on the x86 architecture, is little-endian--same as Windows. The iPhone, on the ARM architecture, is also little-endian as near as I can determine from searches.
bbrown
Endianness wouldn't be a problem here, anyway. All of your operations involve raw bytes, not multi-byte numbers.
Jens Alfke
A: 

Because you control both sides, my recommendation (if you cannot get the library encryption algorithms to work together on the two platforms) would be to write the encryption yourself on both sides, using the same algorithm.

That way you have the control, and would be able to debug the encryption internals to see what is going wrong.

It is a last resort (of course) but would probably have taken less time than the three days that you have spent already, and have a high chance of success

HTH

Joon
With all due respect, I think I should use the respective frameworks' security features instead of rolling my own. First, their implementation is battle tested and has passed security reviews that mine will not have. Second, I'm a C# and Objective-C programmer with admittedly-limited cryptographic skills. Third, I think I'm really close on this one and that the problem is soluble. Finally, the number of questions on SO (and elsewhere) about this situation suggests that there are many that have grappled unsuccessfully and so it would be nice to have a resolution available.
bbrown
Um, no. Nobody in their right mind should be implementing the RSA algorithm unless (a) there's not one available for the platform already, (b) they are a professional, experienced cryptographer, and (c) they're willing to budget a lot of time for code review and testing.Cryptographic implementations are complicated, prone to subtle problems, and very sensitive since people will be actively attacking them looking for exploits.
Jens Alfke
A: 

Will this help you ?

Asymmetric Key Encryption w/ .NET & C#

  • Sorry for the short post, time constraints and all. Anyway, saw your Twitter request for help.. this shows how I did this with PHP and decrypted on .NET, simliar. I notice your decrypt class is slightly diff than mine, so this article might help.
Matthew M.
Sadly, OpenSSL is not available on the iPhone so it doesn't help me. But thanks for trying!
bbrown
The point I was making was to try the decryption routine to match mine. I found the 509Certificate2 was better.
Matthew M.
That was my bad. In the code sample above, there's a private instance variable called "clientCert" that is of type X509Certificate2. I failed to include it in the listing. Sorry about that...
bbrown
+3  A: 

Well... the first step (as you say you have done) is to encrypt the same messages with the same initialization vectors using both the iPhone and the C# implementation. You should get the same output. You said you didn't, so there is a problem.

This means either:

  • The iPhone implementation of RSA is incorrect.
  • The .NET implementation of RSA is incorrect.
  • The key files are different (or being interpreted differently).

I would suggest the first two are unlikely, however they are remotely possible.

You state: "Installed .cer file in different cert store and server-encrypted string roundtripped just fine"... this doesn't prove anything: all this proves is that given a particular random set of numbers you can encrypt/decrypt successfully on one platform. You are not guaranteeing that both platforms are seeing the same set of random numbers.

So I suggest you take it down to the lowest level possible here. Inspect the direct (byte array) inputs and outputs of the encryption on both platforms. If with the exact same (binary) inputs you don't get the same output, then you have a platform problem. I think this is unlikely, so I'm guessing you will find that the IVs are being interpreted differently.

Jon Grant
Thanks for your response! I can find nowhere in the .NET or Cocoa libraries where you can set an IV for RSA encryption or decryption. I thought IV applied only to symmetric algorithms.I too would not presume to think that either 1 or 2 are possible, but I also cannot fathom that an X509 certificate's public key would be interpreted differently on the two platforms. But I took the cert from the phone and plopped it into the server and it was successful there so I don't know what to think.I will look into the public key details within each platform to make sure that they're the same, <cont>
bbrown
but I'm pretty sure that I shouldn't be surprised that the same public key might generate different output for the same input on different passes. That is the purpose of the PKCS#1 padding, something an Apple engineer confirmed: http://lists.apple.com/archives/apple-cdsa/2009/Jul/msg00032.html
bbrown
Sorry - when I said the IV I was perhaps misusing the terminology - it's late here :) I meant basically the encryption key: the data you get from the ExportParameters method in .NET or the contents of your 'key' parameter on the iPhone. I think it would be worth ensuring they are the same (excluding the private part of course).
Jon Grant
I'm giving you the answer credit because you urged a fresh start.
bbrown
My two cents here: IVs are used in block ciphers, not necessarily symmetric algorithms.
yodaj007
+1  A: 

Hi, this is my first answer on stackoverflow, so please forgive me if I do it wrong!

I can't give you a complete answer, however I had very similar issues when I tried to integrate with PHP - it seems that the format of Apple's certificate files is a little different from that which other software expects (including openssl).

Here's how I decrypt an encrypted signature in PHP - I actually extract the modulus and PK from the transmitted public key manually and use that for the RSA stuff, rather than trying to import the key:

// Public key format in hex (2 hex chars = 1 byte):
//30480241009b63495644db055437602b983f9a9e63d9af2540653ee91828483c7e302348760994e88097d223b048e42f561046c602405683524f00b4cd3eec7e67259c47e90203010001
//<IGNORE><--------------------------------------------- MODULUS --------------------------------------------------------------------------><??>< PK > 
// We're interested in the modulus and the public key.
// PK = Public key, probably 65537

// First, generate the sha1 of the hash string:
$sha1 = sha1($hashString,true);

// Unencode the user's public Key:
$pkstr = base64_decode($publicKey);
// Skip the <IGNORE> section:
$a = 4;
// Find the very last occurrence of \x02\x03 which seperates the modulus from the PK:
$d = strrpos($pkstr,"\x02\x03");
// If something went wrong, give up:
if ($a == false || $d == false) return false;
// Extract the modulus and public key:
$modulus = substr($pkstr,$a,($d-$a));
$pk = substr($pkstr,$d+2);

// 1) Take the $signature from the user
// 2) Decode it from base64 to binary
// 3) Convert the binary $pk and $modulus into (very large!) integers (stored in strings in PHP)
// 4) Run rsa_verify, from http://www.edsko.net/misc/rsa.php
$unencoded_signature = rsa_verify(base64_decode($signature), binary_to_number($pk), binary_to_number($modulus), "512");

//Finally, does the $sha1 we calculated match the $unencoded_signature (less any padding bytes on the end)?
return ($sha1 == substr($unencoded_signature,-20)); // SHA1 is only 20 bytes, whilst signature is longer than this.

The objective-c that generates this public key is:

NSData * data = [[SecKeyWrapper sharedWrapper] getPublicKeyBits];
[req addValue:[data base64Encoding] forHTTPHeaderField: @"X-Public-Key"];
data = [[SecKeyWrapper sharedWrapper] getSignatureBytes:[signatureData dataUsingEncoding:NSUTF8StringEncoding]];
[req addValue:[data base64Encoding] forHTTPHeaderField: @"X-Signature"];

Using SecKeyWrapper from Apple's example project CryptoExercise (you can view the file here: https://developer.apple.com/iphone/library/samplecode/CryptoExercise/listing15.html)

I hope this helps?

Benjie Gillam
As Jon Grant suggested, I will inspect the public key bits themselves but the fact is that the iPhone is reading a X509 certificate bundled with the app (not generated on the phone itself) and I find it incredible that the phone would read the public key in differently. I'll settle this by checking the bytes, but I'm going to be awfully surprised if that's the case.
bbrown