在 iOS 库中生成 CSR?

Generate a CSR in iOS Library?

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

这可能吗,是否有适合它的库?

谢谢

是的,这是可能的,但一点也不简单,因为 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

    }

在此之后,您可以将 CSR 以 DER(格式)发送到服务器或以 PEM 格式(base64)编码,具体取决于您的服务能力

我猜你错过了最后一步,将 X509 从服务器返回到设备进行存储

已编辑

使用 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([=12=])) }
    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([=12=])) }
    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([=12=])) }
    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!
    }
}

已编辑

将 public 密钥导出为 DER

请注意,此代码不会以 DER

等可读格式提供 public 密钥
let publicKey = loadKeyStringFromKeyChainAsNSData(PUBLIC_KEY + keyId)

如果您需要在 iOS 之外使用 public 密钥(或导入证书并获取有效密钥),则需要额外的操作。使用以下项目

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
    }
}