在使用RSA在iPhone上加密的C#中解密时遇到问题

到目前为止,我已经花了两天时间来处理这个问题,并且可以根据我的需要进行梳理,所以这是最后的选择。

我有一个X509证书,其公钥存储在iPhone的钥匙串中(此时仅为模拟器)。 在ASP.NET方面,我在证书库中获得了带有私钥的证书。 当我在iPhone上加密字符串并在服务器上解密它时,我收到CryptographicException “Bad data”。 我尝试了在RSACryptoServiceProvider页面上建议的Array.Reverse,但它没有帮助。

我比较了两侧的64弦,它们是相同的。 我在解码后比较了原始字节数组,它们也相等。 如果我使用公钥在服务器上加密,则字节数组与iPhone的版本不同,并且可以使用私钥轻松解密。 原始明文字符串是115个字符,因此它在我的2048位密钥的256字节限制范围内。

这是iPhone加密方法(几乎逐字逐句来自CryptoExercise示例应用程序的wrapSymmetricKey方法):

 + (NSData *)encrypt:(NSString *)plainText usingKey:(SecKeyRef)key error:(NSError **)err { size_t cipherBufferSize = SecKeyGetBlockSize(key); uint8_t *cipherBuffer = NULL; cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t)); memset((void *)cipherBuffer, 0x0, cipherBufferSize); NSData *plainTextBytes = [plainText dataUsingEncoding:NSUTF8StringEncoding]; OSStatus status = SecKeyEncrypt(key, kSecPaddingNone, (const uint8_t *)[plainTextBytes bytes], [plainTextBytes length], cipherBuffer, &cipherBufferSize); if (status == noErr) { NSData *encryptedBytes = [[[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize] autorelease]; if (cipherBuffer) { free(cipherBuffer); } NSLog(@"Encrypted text (%d bytes): %@", [encryptedBytes length], [encryptedBytes description]); return encryptedBytes; } else { *err = [NSError errorWithDomain:@"errorDomain" code:status userInfo:nil]; NSLog(@"encrypt:usingKey: Error: %d", status); return nil; } } 

这是服务器端C#解密方法:

 private string Decrypt(string cipherText) { if (clientCert == null) { // Get certificate var store = new X509Store(StoreName.My, StoreLocation.LocalMachine); store.Open(OpenFlags.ReadOnly); foreach (var certificate in store.Certificates) { if (certificate.GetNameInfo(X509NameType.SimpleName, false) == CERT) { clientCert = certificate; break; } } } using (var rsa = (RSACryptoServiceProvider)clientCert.PrivateKey) { try { var encryptedBytes = Convert.FromBase64String(cipherText); var decryptedBytes = rsa.Decrypt(encryptedBytes, false); var plaintext = Encoding.UTF8.GetString(decryptedBytes); return plaintext; } catch (CryptographicException e) { throw(new ApplicationException("Unable to decrypt payload.", e)); } } } 

我怀疑平台之间存在一些编码问题。 我知道一个是大端,另一个是小端,但我不知道哪个是哪个或如何克服差异。 Mac OS X,Windows和iPhone都是小端的,所以这不是问题所在。

新理论:如果将OAEP padding Boolean设置为false,则默认为PKCS#1 1.5 padding。 SecKey仅具有PKCS1PKCS1MD2PKCS1MD5PKCS1SHA1 SecPadding定义。 也许微软的PKCS#1 1.5!= Apple的PKCS1,因此填充正在影响加密的二进制输出。 我尝试使用kSecPaddingPKCS1并将fOAEP设置为false ,但仍然无法正常工作。 显然, kSecPaddingPKCS1 相当于 PKCS#1 1.5。 回到理论上的绘图板……

其他新尝试的理论:

  1. iPhone上的证书(.cer文件)与服务器上的PKCS#12捆绑包(.pfx文件)不完全相同,因此它永远不会起作用。 已安装的.cer文件在不同的证书存储区和服务器加密的字符串中完成了很好的处理;
  2. 转换到base-64和POST到服务器的行为导致奇怪,这在同一类往返中不存在所以我首先尝试了一些URLEncoding / Decoding,然后从iPhone发布原始二进制文件,validation它是相同的,并得到相同的坏数据;
  3. 我的原始字符串是125字节,所以我认为它可能在UTF-8(长镜头)中截断,所以我将其裁剪为44字节的字符串,没有结果;
  4. 回顾一下System.Cryptography库以确保我使用了一个合适的类并发现了`RSAPKCS1KeyExchangeDeformatter`,在新的潜在客户中变得兴高采烈,并且当它表现完全相同时会感到沮丧。

成功!

事实certificate,我在iPhone模拟器上的钥匙扣上有一些瑕疵,可以说是混淆了水域。 我删除了~/Library/Application Support/iPhone Simulator/User/Library/Keychains/keychain-2-debug.db的Keychain数据库,以便重新创建它并且工作正常。 谢谢你的帮助。 数字它本来是简单但不明显的。 (我学到的两件事:1)从模拟器卸载应用程序不会清除其钥匙串条目,2)定期开始绝对新鲜。)

注意:keychain文件的通用路径取决于iOS版本:〜/ Library / Application Support / iPhone Simulator / [version] /Library/Keychains/keychain-2-debug.db eg~ / Library / Application Support / iPhone模拟器/ 4.3 /库/钥匙串/ keychain-2-debug.db

嗯……第一步(正如你所说的那样)是使用iPhone和C#实现使用相同的初始化向量加密相同的消息。 你应该得到相同的输出。 你说你没有,所以有一个问题。

这意味着:

  • RSA的iPhone实现不正确。
  • RSA的.NET实现不正确。
  • 密钥文件不同(或以不同方式解释)。

我建议前两个不太可能,但它们是远程可能的。

您声明:“已安装的.cer文件在不同的证书存储区和服务器加密的字符串中进行了很好的解决”……这并不能certificate任何事情:所有这些都certificate,给定一组特定的数字,您可以成功加密/解密一个平台。 您无法保证两个平台都能看到同一组随机数。

所以我建议你把它降到最低水平。 在两个平台上检查加密的直接(字节数组)输入和输出。 如果使用完全相同的(二进制)输入,则无法获得相同的输出,那么您就遇到了平台问题。 我认为这不太可能,所以我猜你会发现IV的解释方式不同。

这是我在stackoverflow上的第一个答案,所以如果我做错了,请原谅我!

我无法给你一个完整的答案,但是当我尝试与PHP集成时,我遇到了非常类似的问题 – 似乎Apple的证书文件的格式与其他软件期望的格式(包括openssl)略有不同。

以下是我在PHP中解密加密签名的方法 – 实际上我手动从传输的公钥中提取模数和PK并将其用于RSA内容,而不是尝试导入密钥:

 // Public key format in hex (2 hex chars = 1 byte): //30480241009b63495644db055437602b983f9a9e63d9af2540653ee91828483c7e302348760994e88097d223b048e42f561046c602405683524f00b4cd3eec7e67259c47e90203010001 //<--------------------------------------------- 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  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. 

生成此公钥的objective-c是:

 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"]; 

使用Apple的示例项目CryptoExercise中的SecKeyWrapper(您可以在此处查看该文件: https : //developer.apple.com/iphone/library/samplecode/CryptoExercise/listing15.html )

我希望这有帮助?

这对你有帮助吗?

带.NET和C#的非对称密钥加密

  • 对不起,短信,时间限制和所有。 无论如何,看到你的Twitter请求帮助..这显示了我如何使用PHP并在.NET上解密,simliar。 我注意到你的解密类比我的稍微不同,所以这篇文章可能会有所帮助。

我相信你自己已经回答了这个问题。 问题最主要在于字节序。

这是编写双向转换方法的可能方法:

 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; } 

这可能是一个很好的资源(维基百科除外): http : //betterexplained.com/articles/understanding-big-and-little-endian-byte-order/

因为你控制双方,我的建议(如果你不能让库加密算法在两个平台上一起工作)将是使用相同的算法在两边自己编写加密。

这样你就可以控制,并且能够调试加密内部以查看出错的地方。

这是最后的手段(当然),但可能花费的时间少于你已经花费的三天,并且成功率很高

HTH