我想知道是否可以在iOS中生成CSR(证书签名请求),以及是否有库。我想生成一个请求,使用扩展中的私钥对其进行签名,然后将CSR请求发送回服务器。
这可能,并且有一个很好的库吗?
由于
答案 0 :(得分:8)
是的,这是可能的,但并不简单,因为iOS不能像你想象的那样使用标准的密钥格式
我已成功使用此库以PCKS#10格式生成CSR,其密钥在KeyChain中生成并以DER格式(二进制)编码。
https://github.com/ateska/ios-csr
func createCertificationRequest(keyId: String, C: String?, CN: String?, O:String?, SERIALNAME:String? ) -> String {
//Replace this with your mechanism to get the private and public key
let publicKey = loadKeyStringFromKeyChainAsNSData(PUBLIC_KEY + keyId)
let privateKey = loadKeySecKeyFromKeyChain(PRIVATE_KEY + keyId)
//SCCSR from ios-csr library
let sccsr : SCCSR = SCCSR()
sccsr.commonName = CN;
sccsr.organizationName = O;
sccsr.countryName = C;
// // aditional data you can set
// sccsr.countryName = @"";
// sccsr.organizationalUnitName = @"";
// sccsr.subjectDER = nil;
// //
//
let certificateRequest = sccsr.build(publicKey, privateKey: privateKey)
let certificateRequestB64 = certificateRequest.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
let certificateRequestPEM =
"-----BEGIN CERTIFICATE REQUEST-----\\n" + certificateRequestB64 + "\\n-----END CERTIFICATE REQUEST-----\\n"
return certificateRequestPEM
}
此后,您可以使用DER(格式)将CSR发送到服务器,或者根据服务的功能以PEM格式(base64)进行编码
我猜你错过了最后一步,将X509从服务器返回到设备进行存储
我还包含了帮助使用iOS-KeyChain生成密钥的代码
let KEY_SIZE = 2048
let PUBLIC_KEY = "mysystem.publickey."
let PRIVATE_KEY = "mysystem.privatekey."
// Generates a key pair in KeyChain using keyId as identifier and returns the public key
// publicKey -->PUBLIC_KEY + keyId
// privateKey --> PRIVATE_KEY + keyId
// KEY_SIZE is stablished globally
func generateKeyPairInKeyChain(keyId: String) -> String {
let privateAttributes = [String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): PRIVATE_KEY + keyId,
String(kSecAttrIsPermanent): kCFBooleanTrue]
let publicAttributes = [String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): PUBLIC_KEY + keyId,
String(kSecAttrIsPermanent): kCFBooleanTrue]
let pairAttributes = [String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): KEY_SIZE,
String(kSecPublicKeyAttrs): publicAttributes,
String(kSecPrivateKeyAttrs): privateAttributes]
//Ensure that keychain has no key with keyId identifier
deleteKeyPairFromKeyChain(keyId)
var publicRef: SecKeyRef?
var privateRef: SecKeyRef?
//Generate the keys and recover the references in publicRef and privateRf
switch SecKeyGeneratePair(pairAttributes, &publicRef, &privateRef) {
case noErr:
// Get the public key as a String
let publicKeyStr = loadKeyStringFromKeyChain(PUBLIC_KEY + keyId)
return publicKeyStr
default:
return ""
}
}
以下内容包括用于生成CSR或密钥的实用程序功能。您将看到更改结果类型的基本相同(需要一些额外的工作来简化...)
//Delete an existing keypair from keychain (public + private)
func deleteKeyPairFromKeyChain(keyId: String) {
deleteRSAKeyFromKeychain(PRIVATE_KEY + keyId)
deleteRSAKeyFromKeychain(PUBLIC_KEY + keyId)
}
// Delete existing RSA key from keychain
private func deleteRSAKeyFromKeychain(tagName: String) {
let queryFilter: [String: AnyObject] = [
String(kSecClass) : kSecClassKey,
String(kSecAttrKeyType) : kSecAttrKeyTypeRSA,
String(kSecAttrApplicationTag): tagName
]
let status: OSStatus = SecItemDelete(queryFilter)
NSLog("private or public deletion result is: " + status.description)
}
// Finds the SecKeyRef corresponding to the parameter key and returns it as a String
private func loadKeyStringFromKeyChain(key: String) -> String {
let query: Dictionary<String, AnyObject> = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): KEY_SIZE,
String(kSecClass): kSecClassKey,
String(kSecAttrApplicationTag): key,
kSecReturnData as String : kCFBooleanTrue ]
var dataTypeRef: AnyObject? = nil
var resultData: NSData? = nil
let status: OSStatus = withUnsafeMutablePointer(&dataTypeRef) { SecItemCopyMatching(query as NSDictionary, UnsafeMutablePointer($0)) }
NSLog("SecItemCopyMatching: " + status.description)
if status == errSecSuccess {
NSLog("private or public debug description is: " + dataTypeRef.debugDescription)
resultData = dataTypeRef as? NSData
let resultStr = resultData?.base64EncodedStringWithOptions([])
NSLog("private or public String is: " + resultStr!)
return resultStr!
} else {
NSLog("no key found!!!!")
return ""
}
}
// Finds the SecKeyRef corresponding to the parameter key and returns it
func loadKeySecKeyFromKeyChain(key: String) -> SecKeyRef {
let query: Dictionary<String, AnyObject> = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): KEY_SIZE,
String(kSecClass): kSecClassKey,
String(kSecAttrApplicationTag): key,
kSecReturnRef as String : kCFBooleanTrue ]
var dataTypeRef: Unmanaged<AnyObject>? = nil
var resultData: SecKeyRef? = nil
let status: OSStatus = withUnsafeMutablePointer(&dataTypeRef) { SecItemCopyMatching(query as NSDictionary, UnsafeMutablePointer($0)) }
NSLog("SecItemCopyMatching: " + status.description)
if status == errSecSuccess {
NSLog("private or public debug description is: " + dataTypeRef.debugDescription)
resultData = (dataTypeRef!.takeRetainedValue() as! SecKeyRef)
NSLog("SecItemCopyMatching returns SecKey: " + resultData.debugDescription)
return resultData!
} else {
return resultData!
}
}
// Finds the SecKeyRef corresponding to the parameter key and returns it as a NSData
private func loadKeyStringFromKeyChainAsNSData(key: String) -> NSData {
let query: Dictionary<String, AnyObject> = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): KEY_SIZE,
String(kSecClass): kSecClassKey,
String(kSecAttrApplicationTag): key,
kSecReturnData as String : kCFBooleanTrue ]
var dataTypeRef: AnyObject? = nil
var resultData: NSData? = nil
let status: OSStatus = withUnsafeMutablePointer(&dataTypeRef) { SecItemCopyMatching(query as NSDictionary, UnsafeMutablePointer($0)) }
NSLog("SecItemCopyMatching: " + status.description)
if status == errSecSuccess {
NSLog("private or public debug description is: " + dataTypeRef.debugDescription)
resultData = dataTypeRef as? NSData
return resultData!
} else {
NSLog("no key found!!!!")
return resultData!
}
}
请注意,此代码不会以DER
等可读格式提供publickeylet publicKey = loadKeyStringFromKeyChainAsNSData(PUBLIC_KEY + keyId)
如果您需要在iOS之外使用公钥(或导入证书并获取有效密钥),则需要执行额外操作。使用以下项目的CryptoExportImportManager.swift
支持转换密钥
https://github.com/DigitalLeaves/CryptoExportImportManager
例如
func exportPublicKeyToDER(keyId:String) -> NSData?{
let publicKey = loadKeyStringFromKeyChainAsNSData(PUBLIC_KEY + keyId)
let keyType = kSecAttrKeyTypeRSA
let keySize = 2048
let exportImportManager = CryptoExportImportManager()
if let exportableDERKey = exportImportManager.exportPublicKeyToDER(publicKey, keyType: keyType as String, keySize: keySize) {
return exportableDERKey
} else {
return nil
}
}