在使用RSA的iPhone上加密的C#语言中无法解密 [英] Having trouble decrypting in C# something encrypted on iPhone using RSA

查看:164
本文介绍了在使用RSA的iPhone上加密的C#语言中无法解密的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我到目前为止已经花了两天的时间,梳理了我的所有资源,所以这是最后的手段。



我有一个X509证书,公钥我已经存储在iPhone的钥匙串(模拟器此时)。在ASP.NET方面,我在证书存储中拥有一个私钥的证书。当我加密iPhone上的一个字符串并在服务器上解密时,我得到一个 CryptographicException 坏数据。我试过在 Array.Reverse


$ b $我比较了两边的64个字符串,相当于。我比较了解码后的原始字节数组,并且它们也是相等的。如果我使用公共密钥对服务器进行加密,则字节数组与iPhone的版本不同,并且可以使用私钥进行解密。原始明文字符串是115个字符,所以它在我的2048位密钥的256字节限制内。



这是iPhone加密方法(几乎从 CryptoExercise示例应用程序 wrapSymmetricKey 方法):

  +(NSData *)encrypt :( NSString *)plainText usingKey:(SecKeyRef)错误:(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]自动释放];
if(cipherBuffer)
{
free(cipherBuffer);
}
NSLog(@加密文本(%d字节):%@,
[encryptedBytes length],[encryptedBytes description]);
返回encryptedBytes;
}
else
{
* err = [NSError errorWithDomain:@errorDomaincode:status userInfo:nil];
NSLog(@encrypt:usingKey:Error:%d,status);
return nil;
}
}

这里是服务器端C#解密方法: / p>

 私有字符串解密(string cipherText)
{
if(clientCert == null)
{
//获取证书
var store = new X509Store(StoreName.My,StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach(store.Certificates中的var证书)
{
if(certificate.GetNameInfo(X509NameType.SimpleName,false)== CERT)
{
clientCert = certificate ;
break;
}
}
}

使用(var rsa =(RSACryptoServiceProvider)clientCert.PrivateKey)
{
try
{
var encryptedBytes = Convert.FromBase64String(cipherText);
var decryptptedBytes = rsa.Decrypt(encryptedBytes,false);
var plaintext = Encoding.UTF8.GetString(decryptptedBytes);
返回明文;
}
catch(CryptographicException e)
{
throw(new ApplicationException(Unable to decrypt payload,e));
}
}
}

我的怀疑是有平台之间的一些编码问题。 <罢工>我知道一个是big-endian,另一个是little-endian,但是我不知道哪个是哪个或怎么克服差异。 Mac OS X,Windows和iPhone都是小端点的,所以这不是问题。



新的理论:如果将OAEP填充布尔值设置为false,则默认为PKCS#1 1.5填充。 SecKey 只有 SecPadding 定义 PKCS1 PKCS1MD2 PKCS1MD5 PKCS1SHA1 。也许微软的PKCS#1 1.5!=苹果的PKCS1,所以填充影响加密的二进制输出。我尝试使用 kSecPaddingPKCS1 fOAEP 设置为 false 和它仍然不起作用。显然, kSecPaddingPKCS1 等同于PKCS#1 1.5。



其他新尝试的理论:


  1. iPhone上的证书(.cer文件)与服务器上的PKCS#12捆绑软件(.pfx文件)不完全相同,因此无法正常工作。安装的.cer文件在不同的cert存储和服务器加密的字符串roundtripped只是罚款;

  2. 转换到base-64并且对服务器进行POST操作导致了异类,这在同一班的往返中并不存在,所以我首先尝试了一些URLEncoding / Decoding,然后发贴来自iPhone的二进制文件,验证它是相等的,并且有相同的坏数据;

  3. 我的原始字符串为125个字节,所以我以为可能会以UTF-8(长镜头)截断,所以我将其裁剪成一个44字节的字符串,没有结果;

  4. 查看System.Cryptography库,以确保我正在使用一个合适的类,并发现RSAPKCS1KeyExchangeDeformatter,在新的潜在客户中变得高兴,并且当它的行为正确时一样。

成功!



〜/ Library / Application Support / iPhone Simulator / User / Library / Keychains / keychain-2-debug.db 中的Keychain DB,以使其重新创建它工作正常感谢您的帮助。数字本来就是简单但不明显的。 (我学到的两件事情:1)从模拟器中卸载该应用程序不清除其钥匙串条目,2)定期启动新鲜度。)



注意:通用路径钥匙串文件取决于iOS版本:
〜/库/应用程序支持/ iPhone模拟器/ [版本] /Library/Keychains/keychain-2-debug.db
例如
〜 / Library / Application Support / iPhone Simulator / 4.3 / Library / Keychains / keychain-2-debug.db

解决方案

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



这意味着:




  • RSA的iPhone实现不正确。

  • RSA的.NET实现不正确。

  • 关键文件是不同的(或



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



你说:安装的.cer文件在不同的cert存储和服务器加密的字符串roundtripped只是罚款...这不证明任何东西:所有这证明是给定一个特定的随机集您可以在一个平台上成功加密/解密的号码。你不是保证两个平台都看到同一组随机数。



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


I've spent two days on this so far and combed through every source at my disposal, so this is the last resort.

I have an X509 certificate whose public key I have stored in the iPhone's keychain (simulator only at this point). On the ASP.NET side, I've got the certificate in the cert store with a private key. When I encrypt a string on the iPhone and decrypt it on the server, I get a CryptographicException "Bad data." I tried the Array.Reverse suggested in the RSACryptoServiceProvider page on a longshot, but it did not help.

I have compared the base-64 strings on both sides and they're equal. I've compared the raw byte arrays after decoding and they too are equal. If I encrypt on the server using the public key, the byte array is different from the iPhone's version and readily decrypts using the private key. The raw plaintext string is 115 characters so it's within the 256-byte limitation of my 2048-bit key.

Here's the iPhone encryption method (pretty much verbatim from the CryptoExercise sample app's wrapSymmetricKey method):

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

And here's the server-side C# decryption method:

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

My suspicion was that there was some encoding problems between the platforms. I know that one is big-endian and the other is little-endian but I don't know enough to say which is which or how to overcome the difference. Mac OS X, Windows, and the iPhone are all little-endian so that's not the problem.

New theory: if you set the OAEP padding Boolean to false, it defaults to PKCS#1 1.5 padding. SecKey only has SecPadding definitions of PKCS1, PKCS1MD2, PKCS1MD5, and PKCS1SHA1. Perhaps Microsoft's PKCS#1 1.5 != Apple's PKCS1 and so the padding is affecting the binary output of the encryption. I tried using kSecPaddingPKCS1 with the fOAEP set to falseand it still didn't work. Apparently, kSecPaddingPKCS1 is equivalent to PKCS#1 1.5. Back to the drawing board on theories…

Other newly-tried theories:

  1. Certificate on iPhone (.cer file) is not exactly the same as the PKCS#12 bundle on the server (.pfx file) and so it could never work. Installed .cer file in different cert store and server-encrypted string roundtripped just fine;
  2. Conversion to base-64 and act of POSTing to server resulted in oddness that wasn't present in same class roundtrip so I first tried some URLEncoding/Decoding and then posted raw binary from iPhone, verified that it was equal, and got same bad data;
  3. My original string was 125 bytes so I thought it might be truncating in UTF-8 (long shot) so I cropped it down to a 44-byte string with no result;
  4. Looked back over the System.Cryptography library to make sure I was using an appropriate class and discovered `RSAPKCS1KeyExchangeDeformatter`, became elated at new prospects, and dejected when it behaved exactly the same.

Success!

It turned out that I had some cruft in my Keychain on the iPhone Simulator that was muddying the waters, so to speak. I deleted the Keychain DB at ~/Library/Application Support/iPhone Simulator/User/Library/Keychains/keychain-2-debug.db to cause it to be re-created and it worked fine. Thank you for all of your help. Figures it would have been something simple but non-obvious. (Two things I learned: 1) uninstalling the app from the simulator does not clear its Keychain entries and 2) start absolutely fresh periodically.)

NOTE: The generic path for the keychain file is dependent on the iOS version: ~/Library/Application Support/iPhone Simulator/[version]/Library/Keychains/keychain-2-debug.db e.g., ~/Library/Application Support/iPhone Simulator/4.3/Library/Keychains/keychain-2-debug.db

解决方案

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.

这篇关于在使用RSA的iPhone上加密的C#语言中无法解密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆