在iOS库中生成CSR?

时间:2016-06-09 20:24:40

标签: ios ssl csr

我想知道是否可以在iOS中生成CSR(证书签名请求),以及是否有库。我想生成一个请求,使用扩展中的私钥对其进行签名,然后将CSR请求发送回服务器。

这可能,并且有一个很好的库吗?

由于

1 个答案:

答案 0 :(得分:8)

是的,这是可能的,但并不简单,因为iOS不能像你想象的那样使用标准的密钥格式

将CSR生成为PEM

我已成功使用此库以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从服务器返回到设备进行存储

EDITED

使用KeyChain生成密钥

我还包含了帮助使用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!
    }
}

EDITED

将公钥导出为DER

请注意,此代码不会以DER

等可读格式提供publickey
let 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
    }
}