到目前为止,我花了两天的时间来处理这个问题,并且根据我所掌握的每一个来源进行梳理,所以这是最后的选择。
我有一个X509证书,其公钥存储在iPhone的钥匙串中(此时仅为模拟器)。在ASP.NET方面,我在证书库中获得了带有私钥的证书。当我在iPhone上加密字符串并在服务器上解密时,我收到CryptographicException
“错误数据”。我尝试了RSACryptoServiceProvider
页面上建议的Array.Reverse
,但没有帮助。
我比较了两侧的64字符串,它们是相同的。我在解码后比较了原始字节数组,它们也相等。如果我使用公钥在服务器上加密,则字节数组与iPhone的版本不同,并且可以使用私钥轻松解密。原始明文字符串是115个字符,因此它在我的2048位密钥的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));
}
}
}
我怀疑平台之间存在一些编码问题。 我知道一个是big-endian而另一个是little-endian但是我不知道哪个是哪个或如何克服差异。 Mac OS X,Windows和iPhone都是小端的,所以这不是问题。
新理论:如果将OAEP padding布尔值设置为false,则默认为PKCS#1 1.5填充。 显然,SecKey
仅对SecPadding
,PKCS1
,PKCS1MD2
和PKCS1MD5
有PKCS1SHA1
个定义。也许微软的PKCS#1 1.5!= Apple的PKCS1,因此填充正在影响加密的二进制输出。我尝试使用kSecPaddingPKCS1
将fOAEP
设置为false
,但仍然无法正常工作。kSecPaddingPKCS1
是equivalent到PKCS# 1 1.5。回到理论上的绘图板......
其他新尝试的理论:
成功!
事实证明,我在iPhone模拟器上的钥匙扣上有一些混乱,可以说是混淆了水域。我删除了~/Library/Application Support/iPhone Simulator/User/Library/Keychains/keychain-2-debug.db
处的钥匙串数据库,以便重新创建它并且工作正常。谢谢你的帮助。数字本来是简单但不明显的。 (我学到的两件事:1)从模拟器卸载应用程序不会清除其钥匙串条目,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
答案 0 :(得分:3)
嗯......第一步(正如你所说的那样)是使用iPhone和C#实现使用相同的初始化向量加密相同的消息。你应该得到相同的输出。你说你没有,所以有一个问题。
这意味着:
我建议前两个不太可能,但它们是远程可能的。
您声明:“已安装的.cer文件在不同的证书存储区和服务器加密的字符串中进行了详细检查”......这并不能证明任何事情:所有这一切都证明,给定一组特定的随机数字可以加密/在一个平台上成功解密。您无法保证两个平台都能看到相同的随机数。
所以我建议你把它降到最低水平。在两个平台上检查加密的直接(字节数组)输入和输出。如果使用完全相同的(二进制)输入,则无法获得相同的输出,那么您就遇到了平台问题。我认为这不太可能,所以我猜你会发现IV的解释方式不同。
答案 1 :(得分:1)
这是我在stackoverflow上的第一个答案,所以如果我做错了,请原谅我!
我无法给你一个完整的答案,但是当我尝试与PHP集成时,我遇到了类似的问题 - 似乎Apple的证书文件格式与其他软件预期的格式略有不同(包括openssl)
以下是我在PHP中解密加密签名的方法 - 实际上我手动从传输的公钥中提取模数和PK,并将其用于RSA内容,而不是尝试导入密钥:
// 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.
生成此公钥的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)
我希望这有帮助吗?
答案 2 :(得分:0)
这会对你有帮助吗?
Asymmetric Key Encryption w/ .NET & C#
答案 3 :(得分:-2)
我相信你自己已经回答了这个问题。问题当然在于字节序。
这是编写双向转换方法的可能方法:
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/
答案 4 :(得分:-3)
因为你控制双方,我的建议(如果你不能让图书加密算法在两个平台上一起工作)就是使用相同的算法在双方自己编写加密。
这样你就拥有了控制权,并且能够调试加密内部,看看出了什么问题。
这是最后的手段(当然),但可能花费的时间少于你已经花费的三天,并且很有可能获得成功
HTH