Why SecCertificateCreateWithData is always return nil?

My code is here.


    class func sslCertificate() -> SecCertificateRef? {
      
        if let path = NSBundle.mainBundle().pathForResource('CERTIFICATE_NAME', ofType: "der") {
            if let data = NSData(contentsOfFile: path) {
                let cfData = CFDataCreate(kCFAllocatorDefault, UnsafePointer<UInt8>(data.bytes), data.length)
              
                let cert:SecCertificateRef? = SecCertificateCreateWithData(kCFAllocatorDefault, cfData)
                return cert
            }
        }
      
        return nil
    }


SecCertificateCreateWithData is always return nil.

What kind of problems can be considered?

Replies

Three things…

What version of Swift are you using here? Judging by your use of NSBundle (rather than

Bundle
) I presume you’re using Swift 2 of some form. Is that right?

You don’t need to create your own CFData here; Swift will automatically go from NSData to CFData like this:

let cert = SecCertificateCreateWithData(nil, data)

If you’re using

Data
(in Swift 3) you’ll need to bounce to NSData.
let cert = SecCertificateCreateWithData(nil, data as NSData)

The most common reason for

SecCertificateCreateWithData
to return nil is that the data isn’t a valid certificate. A common problem is that folks try to pass in a PEM format certificate, whereas
SecCertificateCreateWithData
requires DER. If you open the certificate with a text editor, do you see Base64? Or do you see binary goo? If you see Base64, you’ll need to convert the certificate to binary (DER) form before passing to to
SecCertificateCreateWithData
. For a single certificate that you include in your bundle, you can just pre-convert it using Keychain Access on the Mac.

OTOH, if it’s already in binary form, there’s something broken within the certificate itself. If you post a hex dump of that binary data I can take a look.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I using Swift2.


I checked my certificate.That's seem Base64 encoded.But extension is DER.

00000000  42 61 67 20 41 74 74 72  69 62 75 74 65 73 0a 20  |Bag Attributes. |
00000010  20 20 20 6c 6f 63 61 6c  4b 65 79 49 44 3a 20 30  |   localKeyID: 0|
00000020  31 20 30 30 20 30 30 20  30 30 20 0a 73 75 62 6a  |1 00 00 00 .subj|
00000030  65 63 74 3d 2f 43 3d 5c  78 45 36 5c 78 39 37 5c  |ect=/C=\xE6\x97\|
00000040  78 41 35 5c 78 45 36 5c  78 39 43 5c 78 41 43 2f  |xA5\xE6\x9C\xAC/|
00000050  53 54 3d 5c 78 45 36 5c  78 39 44 5c 78 42 31 5c  |ST=\xE6\x9D\xB1\|
00000060  78 45 34 5c 78 42 41 5c  78 41 43 5c 78 45 39 5c  |xE4\xBA\xAC\xE9\|
00000070  78 38 33 5c 78 42 44 2f  4f 3d 5c 78 45 36 5c 78  |x83\xBD/O=\xE6\x|
00000080

At first I converted p12 format to DER with openssl. Is this not okay?

I checked my certificate.That's seem Base64 encoded.But extension is DER.

Yeah, that’s a problem. This sort of textual representation is indicative of PEM.

SecCertificateCreateWithData
can’t deal with PEM. You have two choices:
  • If you’re working with a certificate hard-wired into your app, you can just use some tool (like Keychain Utility or the

    x509
    subcommand of
    openssl
    ) to convert the certificate to DER and add that to your app.
  • If you have to handle arbitrary certificates at runtime, you’ll need to undo the Base64 encoding.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks a lot.I solved problem.

But I got new problem.


When I checked SecTrustResultType.hashValue,that's value was kSecTrustResultRecoverableTrustFailure.

When installing a certificate in General, it was possible to communicate normally.

So I think that there is probably no problem with the certificate.


What kind of problems can be considered?

First up, I moved your thread to Core OS > Security because there’s nothing particularly Swift-specific here.

Secondly, you wrote:

When installing a certificate in General, it was possible to communicate normally.

You mean in Settings > General, right?

Please explain more about what you’re trying to do here. This is the first time you’ve mentioned communication — so far this thread has been about importing a certificate — so it’s hard to understand what’s going on without more context.

Also, are you using TLS? If so, you might want to read my TLS for App Developers post, which provides a bunch of background on that subject.

ps With regards this:

When I checked

SecTrustResultType.hashValue
,that's value was
kSecTrustResultRecoverableTrustFailure
.

if you want to get the numeric representation of an enum like

SecTrustResultType
, you should use the
rawValue
property. The
hashValue
property gives you a hash, which in this case just happens to match up but is not guaranteed to do so.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Sorry.I explain what I want to do in the first place.


I connect to server from my application then needs TLS/SSL.

Server certificate is self-signed certificate.

I prepared rootCA certificate and client certificate for TLS/SSL connection.

When I want to got these certificate the first problem occured.


In addition, the following settings are made.

When connecting to the server, it is connected with a port other than 443.

ATS is disabled.

I'm using own wrapper class that was subclass of NSURLConnection.


My code is here.

    func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            print(challenge.protectionSpace.host)
            print(ConstStruct.HOST)
            if challenge.protectionSpace.host == ConstStruct.HOST.stringByReplacingOccurrencesOfString(":PortNo", withString: "") && NSString(string:ConstStruct.HOST).rangeOfString(":PortNo").location != NSNotFound {
             
                let protectionSpace = challenge.protectionSpace
                let trust = protectionSpace.serverTrust
             
                let certs: [CFTypeRef] = [HttpPostHelper.sslCertificate() as! CFTypeRef]
                let certArrayRef : CFArrayRef = CFBridgingRetain(certs as NSArray) as! CFArrayRef
             
                var status = SecTrustSetAnchorCertificates(trust!, certArrayRef)
                if status != errSecSuccess {
                    print("SecTrustSetAnchorCertificates")
                    connection.cancel()
                    return
                }
/
                let rootCa = "ROOTCA_FILENAME"
                if let rootCaPath = NSBundle.mainBundle().pathForResource(rootCa, ofType: "der") {
                    if let rootCaData = NSData(contentsOfFile: rootCaPath) {
                        if let rootCert = SecCertificateCreateWithData(nil, rootCaData) {
                            SecTrustSetAnchorCertificates(trust!, CFBridgingRetain([rootCert,HttpPostHelper.sslCertificate() as! CFTypeRef] as NSArray) as! CFArrayRef)
                            SecTrustSetAnchorCertificatesOnly(trust!, false) // also allow regular CAs.
                        }
                    }
                }
*/             
                var trustResult:SecTrustResultType = SecTrustResultType(kSecTrustResultInvalid)
                status = SecTrustEvaluate(trust!, &trustResult)
             
                if status != errSecSuccess {
                    print("SecTrustEvaluate")
                    print("trustResult")
                    connection.cancel()
                    return
                }
             
                switch trustResult.hashValue {
                case kSecTrustResultProceed,kSecTrustResultUnspecified:
                    challenge.sender?.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!), forAuthenticationChallenge: challenge)
                    return
                case kSecTrustResultRecoverableTrustFailure:
                    challenge.sender?.cancelAuthenticationChallenge(challenge)
                    connection.cancel()
                    break
                default:
                    challenge.sender?.cancelAuthenticationChallenge(challenge)
                    connection.cancel()
                    break
                }
             
                challenge.sender?.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!), forAuthenticationChallenge: challenge)
                challenge.sender?.continueWithoutCredentialForAuthenticationChallenge(challenge)
            }
        }
    }

Running comment lines 19 to 29 did not change the result.


Are there causes in this?

Hi,eskimo.I rewritten my wrapper class with NSURLSession.But that problem was not solved.

I read other articles, but I think whether specifying direct connection by IP address is not good.Is that right?

My test server does'nt have domain name.How do I test it?