[英]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数字(在这里实际上不需要n和e表示公钥; 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的代码( SecItemAdd
, SecItemCopyMatching
)所示,这将生成一个base64编码的公钥字符串,可用于在iOS钥匙串中插入。
我还验证了C#代码可以成功解密在iOS端加密的数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.