Swift和C#之间的RSA加密

我需要将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从公钥中提取模数[1, 0, 1]像往常一样,指数为[1, 0, 1] rsaProvider.ExportParameters(false).Modulus [1, 0, 1]

如何在OS X / iOS客户端的Swift应用程序中使用Modulus数字(或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 Cryptographic 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", "") 

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

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