繁体   English   中英

Swift和C#之间的RSA加密

[英]RSA encryption between Swift and C#

我需要将Apple客户端部分构建到以.Net / C#编写的现有服务器上。 客户端应在OS X和iOS上运行。 部分通信使用加密数据完成,C#源类似于

    string privateKey = "xyz…=" // Base64 encoded
    string publicKey  = "abc…=" // Base64 encoded

    byte[] decrypt(byte[] encryptedBytes)
    {
        CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };
        RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);
        rsaProvider.ImportCspBlob(Convert.FromBase64String(privateKey));

        return rsaProvider.Decrypt(encryptedBytes, false);
    }

这看起来很简单,但是我找不到在Swift中实现encrypt副本的方法。

publicKey数据仅由模数和指数数组成。 我可以使用rsaProvider.ExportParameters(false).Modulus从公钥中提取模数, rsaProvider.ExportParameters(false).Modulus ,指数为[1, 0, 1] rsaProvider.ExportParameters(false).Modulus [1, 0, 1]

如何在OS X / iOS客户端的Swift应用程序中使用模数(或publicKey字符串常量本身),以及如何加密可以在C#服务器端解密的明文?

我知道Apple不太喜欢以这种低级的方式引入加密密钥,但是似乎有可能将这种密钥导入密钥链,然后将其用于加密。

我有构建块( SecItem…()SecKeyEncrypt()等),但是我无法启动它并运行它。

这样的事情应该可以在iOS上运行。

对于OS X,您需要稍微不同的方法( SecItemImport而不是SecItemAdd )。

import UIKit
import Security

let publicKey = "MIIBCgKCAQEAxWp6GqUOG3xuMhaE0Eeb0eOqbPHE2lRQ53qg2A1rInWdBTVtQaU82Yurv6rFoz++jswiHf3VBy3plhalF+1CTruuzSqVUjpeWTGFppoIym8andVtGLP5mN56Ks7z8VxwQ4MvmM5lGqw3YX6NWVNirWTGdJsqiplmhkAZXFAY43ivwTFSbQ4Uhx7SA0PK537V6je5MJ9edaWpKc1HoGH/bZG9/qrunv2Wam0w9qb8/TOsNvxdgBFs9WZaU0amkNb4h94y9ZrJKYsRGTngDAZ/uA+WK5ZM+Dz3GelsDUErvlUlswLyhQKYPPGn+QlVbMF4drUZ6piZWPmvpY2a/iyRcwIDAQAB"

let keyData = NSData(base64EncodedString: publicKey, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)

var dictionary: [NSString: AnyObject] = [
    kSecClass: kSecClassKey,
    kSecAttrKeyType: kSecAttrKeyTypeRSA,
    kSecAttrKeyClass: kSecAttrKeyClassPublic,
    kSecAttrApplicationTag: "mypubkeyappspecifictag",
    kSecValueData: keyData!,
    kSecReturnRef: true
];

var err = SecItemAdd(dictionary, nil);

if ((err != noErr) && (err != errSecDuplicateItem)) {
    print("error loading public key");
}

var keyRef: AnyObject?;
err = SecItemCopyMatching(dictionary, &keyRef);
if (err == noErr) {
    if let keyRef = keyRef as! SecKeyRef? {
        let plaintext = "12345";

        let plaintextLen = plaintext.lengthOfBytesUsingEncoding(NSUTF8StringEncoding);
        let plaintextBytes = [UInt8](plaintext.utf8);

        var encryptedLen: Int = SecKeyGetBlockSize(keyRef);
        var encryptedBytes = [UInt8](count: encryptedLen, repeatedValue: 0);

        err = SecKeyEncrypt(keyRef, SecPadding.PKCS1, plaintextBytes, plaintextLen, &encryptedBytes, &encryptedLen);
        if (err != noErr) {
            print(encryptedBytes);
        }
    }
}

SecItemDelete(dictionary);

请注意,iOS的公钥应按此处指定的方式去除ASN1前言。

最后,我找到了正确的组合。 Alex Skalozub的回答在此过程中提供了很大帮助。

这是我的逐步操作:

它以服务器端提供的公钥字符串开头。 这是以前以这种方式生成的:(C#.Net)

CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };

// generate key pair with given size
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(4096, cspParams);

string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));

这些键在内部存储为字符串常量,然后从它们的base64表示中提取它们之后,在服务器初始化时将其导入:

CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };
// no key generation this time:
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);
rsaProvider.ImportCspBlob(Convert.FromBase64String(publicKey));

由于这些“ CSP Blob”是专有的Microsoft加密API结构,因此我不得不使用它们并提取低级别的RSA数字(在这里实际上不需要ne表示公钥; d真正不需要私钥):(C# 。净)

RSAParameters pub = rsaProvider.ExportParameters(false);
string n = Convert.ToBase64String(pub.Modulus);
string e = Convert.ToBase64String(pub.Exponent);

RSAParameters priv = rsaProvider.ExportParameters(true);
string d = Convert.ToBase64String(priv.D);

下一步是根据数字构建一个Apple兼容(即非标准兼容)的密钥字符串。 我将Python与PyCrypto软件包一起使用:

import base64
from Crypto.Util import asn1

def b64ToNum(b64str):
  byteStr = base64.b64decode(b64str)
  num = 0L
  for digit in byteStr:
    num = num * 256 + ord(digit)
  return num

n_b64 = "..."   # copied from C# output
e_b64 = "..."   # copied from C# output

n = b64ToNum(n_b64)
e = b64ToNum(e_b64)

seq = asn1.DerSequence()
seq[:] = [ n, e ]  ## Standard would be [ 0, n, e ] !!!

print s.encode("base64").replace("\n", "")

如Alex的代码( SecItemAddSecItemCopyMatching )所示,这将生成一个base64编码的公钥字符串,可用于在iOS钥匙串中插入。

我还验证了C#代码可以成功解密在iOS端加密的数据。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM