到目前为止,我已经为此花了两天时间,并梳理了我可以使用的所有资源,所以这是最后的手段。
我有一个 X509 证书,其公钥存储在 iPhone 的钥匙串(keychain)中(此时仅模拟器)。在 ASP.NET 方面,我在证书存储中使用私钥获得了证书。当我在 iPhone 上加密一个字符串并在服务器上解密它时,我得到一个 CryptographicException
“不良数据。”我试过 Array.Reverse
建议在 RSACryptoServiceProvider
远景上的一页,但它没有帮助。
我比较了两边的base-64字符串,它们是相等的。我在解码后比较了原始字节数组,它们也是相等的。如果我在服务器上使用公钥加密,字节数组与 iPhone 的版本不同,并且很容易使用私钥解密。原始明文字符串为 115 个字符,因此它在我的 2048 位 key 的 256 字节限制之内。
这是 iPhone 的加密方法(几乎是从 CryptoExercise sample app 的 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 都是 little-endian,所以这不是问题。
新理论:如果将 OAEP 填充 bool 值设置为 false,则默认为 PKCS#1 1.5 填充。
SecKey
只有 SecPadding
PKCS1
的定义, PKCS1MD2
, PKCS1MD5
, 和 PKCS1SHA1
.也许 Microsoft 的 PKCS#1 1.5 != Apple 的 PKCS1 等填充会影响加密的二进制输出。我尝试使用 kSecPaddingPKCS1
与 fOAEP
设置为 false
它仍然没有工作。显然,kSecPaddingPKCS1
是 equivalent到 PKCS#1 1.5。回到理论的绘图板......其他新尝试的理论:
成功!
事实证明,我在 iPhone 模拟器上的钥匙串(keychain)中有一些东西弄脏了水,可以这么说。我在
~/Library/Application Support/iPhone Simulator/User/Library/Keychains/keychain-2-debug.db
删除了钥匙串(keychain)数据库。使其重新创建并且工作正常。感谢您的所有帮助。数字这将是一些简单但不明显的事情。 (我学到了两件事:1)从模拟器中卸载应用程序不会清除其钥匙串(keychain)条目,2)定期重新启动。)注意:钥匙串(keychain)文件的通用路径取决于 iOS 版本:
~/Library/Application Support/iPhone Simulator/[version]/Library/Keychains/keychain-2-debug.db
例如。,
~/Library/Application Support/iPhone Simulator/4.3/Library/Keychains/keychain-2-debug.db
最佳答案
嗯...第一步(正如您所说的那样)是使用 iPhone 和 C# 实现使用相同的初始化向量加密相同的消息。你应该得到相同的输出。你说你没有,所以有问题。
这意味着:
我建议前两个不太可能,但是它们是远程可能的。
您声明:“在不同的证书存储中安装 .cer 文件和服务器加密的字符串往返就好了”......这并不能证明任何事情:这一切都证明了给定一组特定的随机数字,您可以成功加密/解密一个平台。您不能保证两个平台看到的是同一组随机数。
所以我建议你把它降到尽可能低的水平。检查两个平台上加密的直接(字节数组)输入和输出。如果使用完全相同的(二进制)输入,您没有得到相同的输出,那么您就有平台问题。我认为这不太可能,所以我猜你会发现 IV 的解释不同。
关于c# - 在 C# 中解密使用 RSA 在 iPhone 上加密的内容时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1133724/