ClientCertificate SSL with NSURLSession

Okay, I am new to the wide wonders of SSL certificates and authentication so I am probably doing something very very obviously wrong here. But I am trying to setup an NSURLSession to download a file from server proxied through an SSL Gateway. For reasons involved with the solution we are building we want to use a non-standard CA for signing the cert. As a result I have a CACert, server certificate and a server private key. These have been worked into a pkcs12 file which I load to get the identity from the file and I then try to do a security trust evaluation with that cert.


What I get is that if I connect to a server that uses a publicly signed cert then the server trust authentication works just fine and I get the callbacks I am expecting. So I know I've done the delegate hookup correctly.


However with the client certificate challenge on the test URL indicated in the code I get a -9802 error. Which suggests that either the cert is being evaluated properly or I have got something else wrong. Indeed the server tries to move on to ServerTrust and then curls everything up. (But curiously the didBecomeInvalidWithError callback doesn't get called, which I was expecting when everything goes wrong.)


I have turned CFNetworking diagnostics on and the device log ends up looking like this :-


Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Warning>: <0x274edc Logs.m:(97)> 02:38:38 App: MPEAPI instance created
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork diagnostics log file created at: /private/var/mobile/Containers/Data/Application/9ED65B9A-30AD-44FE-BAE9-BEC396D2F10F/Library/Logs/CrashReporter/CFNetwork_com.verifone.MPEtestApplication_793.nwlrb.log
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:1] 15:38:38.504 {
  LoaderWhatToDo
     Request: <CFURL 0x14f2f150 [0x38002170]>{string = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, encoding = 134217984, base = (null)}
  CachePolicy: 0
    WhatToDo: originload
  CreateToNow: 0.00319s
  } [3:1]
Oct 27 15:38:38 Philip-Banks-ipod iap2d[228] <Warning>: /BuildRoot/Library/Caches/com.apple.xbs/Sources/iapd/iapd-1746.60.1/iap2d/iAP2Server.m:___xpc_iap2d_handle_incoming_request_block_invoke-878 IAPAppRegisterClient
Oct 27 15:38:38 Philip-Banks-ipod iap2d[228] <Warning>: INIT - /BuildRoot/Library/Caches/com.apple.xbs/Sources/iapd/iapd-1746.60.1/iap2d/iAP2EAClient.m:-[iAP2EAClient initWithCapabilities:auditToken:xpcConnection:andBundleId:] - 85 capability=0x30
Oct 27 15:38:38 Philip-Banks-ipod iap2d[228] <Warning>: com.verifone.MPEtestApplication supports EA while suspended = YES
Oct 27 15:38:38 Philip-Banks-ipod iap2d[228] <Warning>: com.verifone.MPEtestApplication supports EA while backgrounded = YES
Oct 27 15:38:38 Philip-Banks-ipod iap2d[228] <Warning>: /BuildRoot/Library/Caches/com.apple.xbs/Sources/iapd/iapd-1746.60.1/iap2d/iAP2Server.m:___xpc_iap2d_handle_incoming_request_block_invoke-1035 IAPAppConnectedAccessories
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:2] 15:38:38.524 {
  AddCookies Continue: request GET https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001 HTTP/1.1
        HTTPProtocol: Task: 14dcc7c0
  } [3:2]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:3] 15:38:38.526 {
  Protocol Enqueue: request GET https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001 HTTP/1.1
          Request: <CFURLRequest 0x14f48f60 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
          Message: GET https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001 HTTP/1.1
          Sending: dict [4] {
                       Accept-Encoding: gzip, deflate
                       Accept-Language: en-us
                                Accept: */
                   }
  } [3:3]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:4] 15:38:38.533 {
  SocketStream IO Logging
  } [3:4]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:5] 15:38:38.544 {
  TCP Connection Created
  conn: 0x14f53d10 for name 103.20.137.69, port 444
  } [3:5]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:6] 15:38:38.548 {
  TCP Connection Start
  conn: 0x14f53d10
  } [3:6]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:7] 15:38:38.610 {
  SocketStream TCP Connection Complete
  conn: 0x14f53d10
    fd: 7
  error: 0
  } [3:7]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:8] 15:38:38.613 {
  { fd: 7, local 10.47.29.209:53929 => peer 103.20.137.69:444 } RAW-SENT 201
  RAW-SENT (7) | > data [ 201 ] bytes {
  RAW-SENT (7) | > 00000000: 1603 0300 c401 0000 c003 0358 1168 ae99    ...........X.h..
  RAW-SENT (7) | > 00000010: 94f9 5ed2 d848 bb05 c846 5654 71c9 e3c9    ..^..H...FVTq...
  RAW-SENT (7) | > 00000020: cd65 210b a324 dacc 75e2 a900 0018 00ff    .e!..$..u.......
  RAW-SENT (7) | > 00000030: c02c c02b c024 c00a c023 c009 c030 c02f    .,.+.$.-.#...0./
  RAW-SENT (7) | > 00000040: c028 c027 c013 0100 007f 0000 0012 0010    .(.'............
  RAW-SENT (7) | > 00000050: 0000 0d31 3033 2e32 302e 3133 372e 3639    ..-103.20.137.69
  RAW-SENT (7) | > 00000060: 000a 0008 0006 0017 0018 0019 000b 0002    .-..............
  RAW-SENT (7) | > 00000070: 0100 000d 000e 000c 0501 0401 0201 0503    ...-............
  RAW-SENT (7) | > 00000080: 0403 0203 3374 0000 0010 0030 002e 0268    ....3t.....0...h
  RAW-SENT (7) | > 00000090: 3205 6832 2d31 3605 6832 2d31 3505 6832    2.h2-16.h2-15.h2
  RAW-SENT (7) | > 000000a0: 2d31 3408 7370 6479 2f33 2e31 0673 7064    -14.spdy/3.1.spd
  RAW-SENT (7) | > 000000b0: 792f 3308 6874 7470 2f31 2e31 0005 0005    y/3.http/1.1....
  RAW-SENT (7) | > 000000c0: 0100 0000 0000 1200 00                     .........      
  RAW-SENT (7) | > }
  } [3:8]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:9] 15:38:38.617 {
  ioLogger
  logStruct: array [4] {
                BEGIN SSL RECORD DECODE: SENT
                decodeHandshake [0] @ 0x14f5d915, version 303, length 196 (0xc4)
                ClientHello (1, 0x1), length 192 (0xc0)
                END SSL RECORD DECODE: SENT
            }
  } [3:9]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:10] 15:38:38.718 {
  { fd: 7, local 10.47.29.209:53929 => peer 103.20.137.69:444 } RAW-READ 1368
  RAW-READ (7) | < data [ 1368 ] bytes {
  RAW-READ (7) | < 00000000: 1603 0305 a002 0000 4d03 0358 1168 b65d    ........M..X.h.]
  RAW-READ (7) | < 00000010: 4b61 2b40 e5f7 65d5 bbea a3d1 ce5d 113f    Ka+@..e......].?
  RAW-READ (7) | < 00000020: 86be 2d04 9288 fd34 2a86 d620 5811 68b6    ..-....4*.. X.h.
  RAW-READ (7) | < 00000030: 51f5 0beb 192f 0954 9556 c1f8 6d18 1f4b    Q..../.T.V..m..K
  RAW-READ (7) | < 00000040: 5304 638c a110 b2f6 09ab cff2 c030 0000    S.c..........0..
  RAW-READ (7) | < 00000050: 05ff 0100 0100 0b00 042b 0004 2800 0242    .........+..(..B
  RAW-READ (7) | < 00000060: 3082 023e 3082 01a7 a003 0201 0202 0821    0..>0..........!
  RAW-READ (7) | < 00000070: 92e4 4931 8b9b ad30 0d06 092a 8648 86f7    ..I1...0-..*.H..
  RAW-READ (7) | < 00000080: 0d01 0105 0500 3025 3123 3021 0603 5504    -.....0%1#0!..U.
  RAW-READ (7) | < 00000090: 030c 1a66 692d 706f 696e 7379 732d 7372    ...fi-poinsys-sr
  RAW-READ (7) | < 000000a0: 762d 7465 7374 2d30 3031 2d63 6130 2017    v-test-001-ca0 .
  RAW-READ (7) | < 000000b0: 0d30 3830 3332 3631 3335 3035 305a 180f    -080326135050Z..
  RAW-READ (7) | < 000000c0: 3230 3538 3033 3236 3133 3530 3530 5a30    20580326135050Z0
  RAW-READ (7) | < 000000d0: 2d31 2b30 2906 0355 0403 0c22 6669 2d70    -1+0)..U..."fi-p
  RAW-READ (7) | < 000000e0: 6f69 6e73 7973 2d73 7276 2d74 6573 7467    oinsys-srv-testg
  RAW-READ (7) | < 000000f0: 7731 2d30 3031 2d67 656e 6572 616c 3081    w1-001-general0.
  RAW-READ (7) | < 00000100: 9f30 0d06 092a 8648 86f7 0d01 0101 0500    .0-..*.H..-.....
  RAW-READ (7) | < 00000110: 0381 8d00 3081 8902 8181 00b4 1d0e 5f53    ....0........._S
  RAW-READ (7) | < 00000120: 9179 2d45 80d2 4746 2164 1cac 8613 3e67    .y-E..GF!d....>g
  RAW-READ (7) | < 00000130: 628c 2514 0036 e770 ca16 15ed 73da 5997    b.%..6.p....s.Y.
  RAW-READ (7) | < 00000140: 2c10 5c5f ce84 4225 5857 20a5 04af 2879    ,.\_..B%XW ...(y
  RAW-READ (7) | < 00000150: 661a b7c5 a9db b05c dd47 a996 63ed 58e6    f......\.G..c.X.
  RAW-READ (7) | < 00000160: 4d7a 34f4 e4b7 26fb 87c8 a08b 48e9 b504    Mz4...&.....H...
  RAW-READ (7) | < 00000170: 4e01 9aa9 aea4 fb02 93b6 0816 0a9b 1054    N...........-..T
  RAW-READ (7) | < 00000180: 6d7b 2647 dd66 ade5 e0f4 79f1 3b01 7bbf    m{&G.f....y.;.{.
  RAW-READ (7) | < 00000190: 044a 6954 6be1 408a ce75 8302 0301 0001    .JiTk.@..u......
  RAW-READ (7) | < 000001a0: a36d 306b 3009 0603 551d 1304 0230 0030    .m0k0...U....0.0
  RAW-READ (7) | < 000001b0: 5e06 0355 1d1f 0457 3055 3053 a051 a04f    ^..U...W0U0S.Q.O
  RAW-READ (7) | < 000001c0: 864d 6874 7470 733a 2f2f 706f 696e 7473    .Mhttps://points
  RAW-READ (7) | < 000001d0: 736c 7465 7374 3a38 3434 332f 6b6d 732f    sltest:8443/kms/
  RAW-READ (7) | < 000001e0: 6372 6c2f 6765 7463 726c 2e68 746d 6c3f    crl/getcrl.html?
  RAW-READ (7) | < 000001f0: 6e61 6d65 3d66 692d 706f 696e 7379 732d    name=fi-poinsys-
  RAW-READ (7) | < 00000200: 7372 762d 7465 7374 2d30 3031 2d63 6130    srv-test-001-ca0
  RAW-READ (7) | < 00000210: 0d06 092a 8648 86f7 0d01 0105 0500 0381    -..*.H..-.......
  RAW-READ (7) | < 00000220: 8100 13f7 5f61 4699 d11c 1199 87d6 964a    ...._aF........J
  RAW-READ (7) | < 00000230: 7e37 4454 94e6 3f8c 063f c560 68f3 4f89    ~7DT..?..?.`h.O.
  RAW-READ (7) | < 00000240: 9f53 1521 5cf3 aa47 f57c 007a e54b 1b47    .S.!\..G.|.z.K.G
  RAW-READ (7) | < 00000250: 8c98 eaaa 235b 3fcf 819a 3df9 5540 a67b    ....#[?...=.U@.{
  RAW-READ (7) | < 00000260: 02f1 013a c2c7 a523 a679 438f 58b3 af01    ...:...#.yC.X...
  RAW-READ (7) | < 00000270: 8a9e f3fb de96 ac7e 2d38 4216 a794 502e    .......~-8B...P.
  RAW-READ (7) | < 00000280: 1b7d 9ad5 cf3b 1ebe 745e c976 bb03 90f0    .}...;..t^.v....
  RAW-READ (7) | < 00000290: f8a7 4b81 5319 197f 221d 0d5f 504b c69a    ..K.S...".-_PK..
  RAW-READ (7) | < 000002a0: 10aa 0001 e030 8201 dc30 8201 45a0 0302    .....0...0..E...
  RAW-READ (7) | < 000002b0: 0102 0208 6c89 815a 8bf7 15f5 300d 0609    ....l..Z....0-..
  RAW-READ (7) | < 000002c0: 2a86 4886 f70d 0101 0505 0030 2531 2330    *.H..-.....0%1#0
  RAW-READ (7) | < 000002d0: 2106 0355 0403 0c1a 6669 2d70 6f69 6e73    !..U....fi-poins
  RAW-READ (7) | < 000002e0: 7973 2d73 7276 2d74 6573 742d 3030 312d    ys-srv-test-001-
  RAW-READ (7) | < 000002f0: 6361 3020 170d 3038 3033 3236 3133 3530    ca0 .-0803261350
  RAW-READ (7) | < 00000300: 3530 5a18 0f32 3035 3830 3332 3631 3335    50Z..20580326135
  RAW-READ (7) | < 00000310: 3035 305a 3025 3123 3021 0603 5504 030c    050Z0%1#0!..U...
  RAW-READ (7) | < 00000320: 1a66 692d 706f 696e 7379 732d 7372 762d    .fi-poinsys-srv-
  RAW-READ (7) | < 00000330: 7465 7374 2d30 3031 2d63 6130 819f 300d    test-001-ca0..0-
  RAW-READ (7) | < 00000340: 0609 2a86 4886 f70d 0101 0105 0003 818d    ..*.H..-........
  RAW-READ (7) | < 00000350: 0030 8189 0281 8100 859a a533 e990 210b    .0.........3..!.
  RAW-READ (7) | < 00000360: 58c1 8b58 984a fd75 337c c021 d374 02d8    X..X.J.u3|.!.t..
  RAW-READ (7) | < 00000370: f640 ff05 3efd a51a 9df7 f6eb 1023 52bc    .@..>........#R.
  RAW-READ (7) | < 00000380: ac59 a650 e4ad 9d1f 02e6 97db c914 a01b    .Y.P............
  RAW-READ (7) | < 00000390: cd30 4945 8d71 5178 44f8 b4d4 9cba 2b8a    .0IE.qQxD.....+.
  RAW-READ (7) | < 000003a0: 9077 1d85 9547 9c49 a043 7879 6899 2048    .w...G.I.Cxyh. H
  RAW-READ (7) | < 000003b0: 6fa5 d537 0010 0591 9d61 e854 5613 3d1d    o..7.....a.TV.=.
  RAW-READ (7) | < 000003c0: 4677 5f8a ddb8 8d4d a885 3984 1cd9 7550    Fw_....M..9...uP
  RAW-READ (7) | < 000003d0: 96f4 acef 2a9f 7633 0203 0100 01a3 1330    ....*.v3.......0
  RAW-READ (7) | < 000003e0: 1130 0f06 0355 1d13 0408 3006 0101 ff02    .0...U....0.....
  RAW-READ (7) | < 000003f0: 0101 300d 0609 2a86 4886 f70d 0101 0505    ..0-..*.H..-....
  RAW-READ (7) | < 00000400: 0003 8181 007b a0cd 116b a28f b536 67bf    .....{...k...6g.
  RAW-READ (7) | < 00000410: f87e 7b61 7543 411a 6047 7ca9 e54a 1a36    .~{auCA.`G|..J.6
  RAW-READ (7) | < 00000420: e688 cd15 e346 e519 3f46 f900 79a8 e027    .....F..?F..y..'
  RAW-READ (7) | < 00000430: 43f9 b963 a0f6 81d0 26c5 f66d 9d88 017d    C..c....&..m...}
  RAW-READ (7) | < 00000440: 7c99 3168 2cf4 dced 64f8 5624 81d2 6dd2    |.1h,...d.V$..m.
  RAW-READ (7) | < 00000450: aaf4 0a0f c21d e196 e557 196c 0686 d698    ..-......W.l....
  RAW-READ (7) | < 00000460: 5f6a 2d12 996c 3157 0ba7 ee35 498c db3a    _j-..l1W...5I..:
  RAW-READ (7) | < 00000470: 2835 34cb b6e5 b941 7fac bf9f cfaa 5b98    (54....A......[.
  RAW-READ (7) | < 00000480: d118 ca76 360c 0000 c903 0017 4104 bf45    ...v6.......A..E
  RAW-READ (7) | < 00000490: 344f 7916 08d2 fa31 ec81 ac4e 7baf bfe1    4Oy....1...N{...
  RAW-READ (7) | < 000004a0: e04e 459d 2043 f3f9 8208 fce6 35ef bc99    .NE. C......5...
  RAW-READ (7) | < 000004b0: b606 a4f7 19eb 3c16 7131 ade6 4952 1dc5    ......<.q1..IR..
  RAW-READ (7) | < 000004c0: 3b21 3cde ab1d c06f 870e 6580 9489 0501    ;!<....o..e.....
  RAW-READ (7) | < 000004d0: 0080 9468 d320 2901 bcb4 07b9 691c c9b2    ...h. ).....i...
  RAW-READ (7) | < 000004e0: feae 734a dbb5 a658 a03f 93cb c769 2588    ..sJ...X.?...i%.
  RAW-READ (7) | < 000004f0: 5e5d 011c 89bb dc6e 7d72 054e b173 c8f5    ^].....n}r.N.s..
  RAW-READ (7) | < 00000500: 90c1 c0db d0ee a59d c69e 8a0f 0195 3d7b    ..............={
  RAW-READ (7) | < 00000510: c4f1 b067 5cb8 131c a79d ad43 0bc9 1cbd    ...g\......C....
  RAW-READ (7) | < 00000520: c8f0 4f57 9fbb 4680 3afa 182f af23 bea9    ..OW..F.:../.#..
  RAW-READ (7) | < 00000530: 03dd c86d eb5a fae3 c449 a0b2 688e 4b0a    ...m.Z...I..h.K-
  RAW-READ (7) | < 00000540: 2188 f37b a27e 5fa2 4221 d52c a98b 7e90    !..{.~_.B!.,..~.
  RAW-READ (7) | < 00000550: 5d81 0d00 004b 0301                        ].-..K..       
  RAW-READ (7) | < }
  } [3:10]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:11] 15:38:38.730 {
  { fd: 7, local 10.47.29.209:53929 => peer 103.20.137.69:444 } RAW-READ 77
  RAW-READ (7) | < data [ 77 ] bytes {
  RAW-READ (7) | < 00000000: 0240 0016 0603 0601 0503 0501 0403 0401    .@..............
  RAW-READ (7) | < 00000010: 0303 0301 0203 0201 0202 002d 002b 3029    ...........-.+0)
  RAW-READ (7) | < 00000020: 3127 3025 0603 5504 030c 1e66 692d 706f    1'0%..U....fi-po
  RAW-READ (7) | < 00000030: 696e 7379 732d 7465 7374 636c 742d 636d    insys-testclt-cm
  RAW-READ (7) | < 00000040: 7331 2d30 3031 2d63 610e 0000 00           s1-001-ca....  
  RAW-READ (7) | < }
  } [3:11]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:12] 15:38:38.732 {
  ioLogger
  logStruct: array [12] {
                BEGIN SSL RECORD DECODE: READ
                decodeHandshake [0] @ 0x15c62025, version 303, length 1440 (0x5a0)
                ServerHello (2, 0x2), length 77 (0x4d)
                decodeHandshake [1] @ 0x15c62076, version 303, length 1440 (0x5a0)
                Certificate (11, 0xb), length 1067 (0x42b)
                decodeHandshake [2] @ 0x15c624a5, version 303, length 1440 (0x5a0)
                ServerKeyExchange (12, 0xc), length 201 (0xc9)
                decodeHandshake [3] @ 0x15c62572, version 303, length 1440 (0x5a0)
                CertificateRequest (13, 0xd), length 75 (0x4b)
                decodeHandshake [4] @ 0x15c625c1, version 303, length 1440 (0x5a0)
                ServerHelloDone (14, 0xe), length 0 (0x0)
                END SSL RECORD DECODE: READ
            }
  } [3:12]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:13] 15:38:38.739 {
  Client Cert Requested
  Distinguished Names: 1
                   0: << DATA <CFData 0x14def8c0 [0x38002170]>{length = 43, capacity = 43, bytes = 0x30293127302506035504030c1e66692d ... 312d3030312d6361} >>
  } [3:13]
Oct 27 15:38:38 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:14] 15:38:38.742 {
  Authentication Challenge
    Loader: <CFURLRequest 0x14dcb620 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
  Challenge: challenge space https://103.20.137.69:444/, ClientCertificateRequested (Hash c3626e29)
  } [3:14]
Oct 27 15:38:38 Philip-Banks-ipod debugserver[791] <Error>: assertion failed: 13G36: libsystem_trace.dylib + 15927 [E82A6F2D-873A-39AD-8014-EDEB52248157]: 0x0
Oct 27 15:38:38 Philip-Banks-ipod Unknown[791] <Error>:
Oct 27 15:38:38 Philip-Banks-ipod debugserver[791] <Error>: assertion failed: 13G36: libsystem_trace.dylib + 15927 [E82A6F2D-873A-39AD-8014-EDEB52248157]: 0x0
Oct 27 15:38:38 Philip-Banks-ipod Unknown[791] <Error>:
Oct 27 15:38:38 Philip-Banks-ipod debugserver[791] <Error>: assertion failed: 13G36: libsystem_trace.dylib + 15927 [E82A6F2D-873A-39AD-8014-EDEB52248157]: 0x0
Oct 27 15:38:38 Philip-Banks-ipod Unknown[791] <Error>:
Oct 27 15:38:38 Philip-Banks-ipod debugserver[791] <Error>: assertion failed: 13G36: libsystem_trace.dylib + 15927 [E82A6F2D-873A-39AD-8014-EDEB52248157]: 0x0
Oct 27 15:38:38 Philip-Banks-ipod Unknown[791] <Error>:
Oct 27 15:38:38 Philip-Banks-ipod debugserver[791] <Error>: assertion failed: 13G36: libsystem_trace.dylib + 15927 [E82A6F2D-873A-39AD-8014-EDEB52248157]: 0x0
Oct 27 15:38:38 Philip-Banks-ipod Unknown[791] <Error>:
Oct 27 15:38:38 Philip-Banks-ipod debugserver[791] <Error>: assertion failed: 13G36: libsystem_trace.dylib + 15927 [E82A6F2D-873A-39AD-8014-EDEB52248157]: 0x0
Oct 27 15:38:38 Philip-Banks-ipod Unknown[791] <Error>:
Oct 27 15:38:38 Philip-Banks-ipod debugserver[791] <Error>: assertion failed: 13G36: libsystem_trace.dylib + 15927 [E82A6F2D-873A-39AD-8014-EDEB52248157]: 0x0
Oct 27 15:38:38 Philip-Banks-ipod Unknown[791] <Error>:
Oct 27 15:38:43 Philip-Banks-ipod MPEtestApplication[793] <Error>:  SecTrustEvaluate  [leaf AnchorTrusted]
Oct 27 15:38:43 Philip-Banks-ipod debugserver[791] <Error>: assertion failed: 13G36: libsystem_trace.dylib + 15927 [E82A6F2D-873A-39AD-8014-EDEB52248157]: 0x0
Oct 27 15:38:43 Philip-Banks-ipod Unknown[791] <Error>:
Oct 27 15:38:44 Philip-Banks-ipod MPEtestApplication[793] <Warning>: Certificates found: 1
Oct 27 15:38:44 Philip-Banks-ipod MPEtestApplication[793] <Error>:  SecTrustEvaluate  [leaf AnchorTrusted]
Oct 27 15:38:44 Philip-Banks-ipod MPEtestApplication[793] <Warning>: User: (null), certificates (
     "<cert(0x160214f0) s: 400-133-738-MOB i: fi-poinsys-testclt-cms1-001-ca>"
  ) identity:<SecIdentityRef: 0x16020270>
Oct 27 15:38:46 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:15] 15:38:46.070 {
  Use Credential
     Loader: <CFURLRequest 0x14dcb620 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
  Credential: Name: 400-133-738-MOB, Persistence: permanent
  } [3:15]
Oct 27 15:38:46 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:16] 15:38:46.074 {
  touchConnection
           Loader: <CFURLRequest 0x14dcb620 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
  Timeout Interval: 60.000 seconds
  } [3:16]
Oct 27 15:38:46 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:17] 15:38:46.078 {
  Peer certificate
  Subject Sum: fi-poinsys-srv-testgw1-001-general
     Summary: fi-poinsys-srv-test-001-ca
  } [3:17]
Oct 27 15:38:46 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:18] 15:38:46.093 {
  Authentication Challenge
    Loader: <CFURLRequest 0x14dcb620 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
  Challenge: challenge space https://103.20.137.69:444/, ServerTrustEvaluationRequested (Hash c3626e29)
  } [3:18]
Oct 27 15:38:47 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:19] 15:38:47.250 {
  Use Credential
     Loader: <CFURLRequest 0x14dcb620 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
  Credential: null
  } [3:19]
Oct 27 15:38:47 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:20] 15:38:47.252 {
  touchConnection
           Loader: <CFURLRequest 0x14dcb620 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
  Timeout Interval: 60.000 seconds
  } [3:20]
Oct 27 15:38:47 Philip-Banks-ipod MPEtestApplication[793] <Error>:  SecTrustEvaluate  [leaf SSLHostname] [root AnchorTrusted]
Oct 27 15:38:47 Philip-Banks-ipod MPEtestApplication[793] <Warning>: NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Oct 27 15:38:47 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:21] 15:38:47.255 {
  Response Error
  Request: <CFURLRequest 0x14f48f60 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
   Error: Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamPropertySSLClientCertificateState=2, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x14f76660>, _kCFStreamErrorDomainKey=3, kCFStreamPropertySSLPeerCertificates=<CFArray 0x160274c0 [0x38002170]>{type = immutable, count = 2, values = (
          0 : <cert(0x14f70280) s: fi-poinsys-srv-testgw1-001-general i: fi-poinsys-srv-test-001-ca>
          1 : <cert(0x14f70520) s: fi-poinsys-srv-test-001-ca i: fi-poinsys-srv-test-001-ca>
          )}, _kCFStreamPropertySSLClientCertificates=<CFArray 0x14f74740 [0x38002170]>{type = mutable-small, count = 2, values = (
          0 : <SecIdentityRef: 0x16020270>
          1 : <cert(0x160214f0) s: 400-133-738-MOB i: fi-poinsys-testclt-cms1-001-ca>
          )}}
  } [3:21]
Oct 27 15:38:47 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:22] 15:38:47.258 {
            Did Fail
              Loader: <CFURLRequest 0x14dcb620 [0x38002170]> {url = https://103.20.137.69:444/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001, cs = 0x0}
               Error: Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamPropertySSLClientCertificateState=2, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x14f76660>, _kCFStreamErrorDomainKey=3, kCFStreamPropertySSLPeerCertificates=<CFArray 0x160274c0 [0x38002170]>{type = immutable, count = 2, values = (
                      0 : <cert(0x14f70280) s: fi-poinsys-srv-testgw1-001-general i: fi-poinsys-srv-test-001-ca>
                      1 : <cert(0x14f70520) s: fi-poinsys-srv-test-001-ca i: fi-poinsys-srv-test-001-ca>
                      )}, _kCFStreamPropertySSLClientCertificates=<CFArray 0x14f74740 [0x38002170]>{type = mutable-small, count = 2, values = (
                      0 : <SecIdentityRef: 0x16020270>
                      1 : <cert(0x160214f0) s: 400-133-738-MOB i: fi-poinsys-testclt-cms1-001-ca>
                      )}}
  init to origin load: 0.011323s
          total time: 8.75724s
         total bytes: 0
  } [3:22]
Oct 27 15:38:47 Philip-Banks-ipod MPEtestApplication[793] <Notice>: CFNetwork Diagnostics [3:23] 15:38:47.275 {
  ~HTTPProtocol: nullptr request
       Request: null
          sent: 0
      received: 0
     cell sent: 0
  cell received: 0
  } [3:23]


Which seems to be pretty clear that the authentication failed, but not why it failed at. I'd appreciate any useful suggestions here as I am kinda stuck at this point.


This code is being build using XCode 8 building against the 10 SDK and deploying it onto an iOS 9.3.5 device.


Here is the code in question :-


#import "testSSLClass.h"
@interface testSSLClass()<NSURLConnectionDelegate, NSURLSessionDelegate, NSURLSessionDataDelegate> {
  NSString* mDownloadURL;
  NSURLSessionConfiguration* mDownloadConfiguration;
  NSURLSession* mDownloadSession;
  NSURLSessionDataTask* mDownloadTask;
  NSMutableData* mDataReceived;
}
@end
@implementation testSSLClass
-(instancetype)init
{
  if (self = [super init])
  {
    mDownloadURL = @"https://103.20.137.69:443/downloadfile.aspx?filename=MON___00DADDF5FFFF00&tspid=100581332001";
    mDownloadConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
  }
  return self;
}
-(void)doADownload
{
  mDataReceived = [NSMutableData new];
  NSURL* URLtoFetch = [NSURL URLWithString:mDownloadURL];
  mDownloadSession = [NSURLSession sessionWithConfiguration:mDownloadConfiguration delegate:self delegateQueue:nil];
  mDownloadTask = [mDownloadSession dataTaskWithURL:URLtoFetch];
  [mDownloadTask resume];
}
-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
  if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
    [self willSendRequestForAuthenticationChallenge:challenge completionHandler:completionHandler];
  } else {
    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
  }
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
  [mDataReceived appendData:data];
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable))completionHandler
{
  [mDataReceived length];
}
-(void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error
{
  NSLog(@"Error: %@", [error userInfo]);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask
{
  NSLog(@"Did Become Stream Task");
}

- (void)willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
  if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
  {
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* directoryPath = [paths objectAtIndex:0];
    NSString* cacertPath = [directoryPath stringByAppendingPathComponent:@"client.p12"];
    NSData *p12data = [NSData dataWithContentsOfFile:cacertPath];
    
    CFDataRef inP12data = (__bridge CFDataRef)p12data;
    
    SecIdentityRef myIdentity;
    SecTrustRef myTrust;
    extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);
    assert(myIdentity != nil);
    assert(myTrust != nil);
        
    long count = SecTrustGetCertificateCount(myTrust);
    NSMutableArray* myCertificates = nil;
    if(count > 0) {
      myCertificates = [NSMutableArray arrayWithCapacity:count];
      for(int i = 0; i < count; ++i) {
        [myCertificates addObject:(__bridge id)SecTrustGetCertificateAtIndex(myTrust, i)];
      }
    }
    
    SecTrustResultType trustResult;
    OSStatus evalResult = SecTrustEvaluate(myTrust, &trustResult);
    
    if (trustResult == kSecTrustResultRecoverableTrustFailure)
    {
      CFDataRef errDataRef = SecTrustCopyExceptions(myTrust);
      SecTrustSetExceptions(myTrust, errDataRef);
      
      evalResult = SecTrustEvaluate(myTrust, &trustResult);
    }
    NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:myCertificates persistence:NSURLCredentialPersistencePermanent];
    assert(credential != nil);
    
    NSLog(@"User: %@, certificates %@ identity:%@", [credential user], [credential certificates], [credential identity]);
    [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
  } else {
    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
  }
}
OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust)
{
  OSStatus securityError = errSecSuccess;
  
  CFStringRef password = CFSTR("password");
  const void *keys[] = { kSecImportExportPassphrase };
  const void *values[] = { password };
  
  CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
  
  CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
  securityError = SecPKCS12Import(inP12data, options, &items);
  
  if (securityError == 0) {
    CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
    const void *tempIdentity = NULL;
    tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
    *identity = (SecIdentityRef)tempIdentity;
    const void *tempTrust = NULL;
    tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
    *trust = (SecTrustRef)tempTrust;
    
    CFIndex count = CFArrayGetCount(items);
    NSLog(@"Certificates found: %ld",count);
  }
  
  if (options) {
    CFRelease(options);
  }
  
  return securityError;
}
@end


Any suggestions, tips or advice gratefully accepted.


Philip

Replies

Either I’m very confused or you are (-: You wrote:

But I am trying to setup an NSURLSession to download a file from server proxied through an SSL Gateway. For reasons involved with the solution we are building we want to use a non-standard CA for signing the cert. As a result I have a CACert, server certificate and a server private key.

From the above it sounds like you’re trying to override HTTPS server trust evaluation. That is, your server is using a certificate issued by a CA that’s not trusted by the system by default, and thus you need to override the client’s authentication of the certificate being presented to it by the server.

However, the code you’ve posted shows you handling the

NSURLAuthenticationMethodClientCertificate
authentication challenge, which is the exact opposite of this. That challenge allows the client to apply a digital identity (the combination of a certificate and the private key associated with the public key embedded in that certificate) to the connection, so the server can then authenticate the client based on that certificate.

So, which is it? Or perhaps both?

If you’re overriding HTTPS server trust evaluation, there’s two things you need to consider:

  • By default all TLS clients on our system do standard HTTPS server trust evaluation (per RFC 2818). In most cases you can override that using the techniques described in Technote 2232 HTTPS Server Trust Evaluation.

  • High-level HTTPS clients have additional security requirements enforced by App Transport Security (ATS). For more info on ATS, including links to the docs, see my App Transport Security pinned post.

To allow connections to a server whose certificate was issued by a CA that’s not trusted by default, you have to disable ATS for that server. This is relatively easy to do from a technical standpoint but it does have App Review implications. It is must less hassle, IMO, to give your server a proper CA-issued certificate. There’s three common cases here:

  • If you’re just testing, set up your own test CA, install its root on your device, and have it issue a certificate for your server. You can do this on the Mac using the instructions from Technote 2326 Creating Certificates for TLS Testing.

  • If you’re deploying an enterprise app, have your enterprise’s CA issue a certificate for your server and then install your enterprise’s CA’s root certificate on your device via MDM (it may already be there).

  • If you’re deploying your app to normal users via the App Store, configure your server with a certificate issued by a CA whose root certificate is built in to iOS.

Share and Enjoy

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

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

The chances are very good that I am quite confused. As I said right at the begining I am very new to the fun and games of SSL.


The setup I am working with has these features :-


SSL to a host specified by IP. (Don't ask me why they don't want to give it a public DNS name)

Client certificate issued by a CA that is, effectively, a private CA that only the company knows about.

Private key (that I assume is bound to the client cert to form an identity)

CACert for the client certificate.


The code is a bit confused because I've been thrashing around a bit with it not realy knowing what I am doing. Testing their setup using openssl works so I know the server itself is functional.


My understanding is that when we do the openssl connection by specificying the cert, cacert and private key we are doing a ClientCertificate authentication. Which is why the code is structured that way.


However (and I have been reading the technotes) the problem I have with their approach is that at best it requires the CACert to be added to the trusted authorities list (which given that we are making fundamentally a support library means we can't easily do this programmaticly). To make it more fun because they are using IP addresses and not DNS names then adding exceptions to ATS for the test environments (because they want to use cheaper self signed certs for the test environments) is impossible.


I am lobbying them to stop being silly and do the following :-


-give all the servers DNS names like sane people.

-Use a publicly signed real certificate for the production server and rely on servertrust authentication.

-Use self signed certificates for the test environments but add exceptions into our test application for those servers to keep ATS happy. (But these never get used in production code)


Which I think gets us where we need to be.


However am I right in thinking the primary issue is that ATS is rejecting the CACert we supply because a) it is self signed and b) we can't provide an exception due to the IP only connecting address? Or am I over simplfying?


Anyway, appreciate your answer it has made some things clearer.

The setup I am working with has these features :-

  • SSL to a host specified by IP. (Don't ask me why they don't want to give it a public DNS name)

  • Client certificate issued by a CA that is, effectively, a private CA that only the company knows about.

  • Private key (that I assume is bound to the client cert to form an identity)

  • CACert for the client certificate.

Just FYI, you don’t need this last item. Lemme explain how some of these parts fit together and hopefully that’ll make it clear why.

Actually, it seems like this could be generally useful so I’ve put it in a separate post; please read TLS for App Developers before continuing.

The above explains why you won’t need your CACert for the client certificate item. Specifically:

  • you don’t need it in order to do trust evaluation on the client, because you never do trust evaluation of the client certificate on the client

  • you don’t need to pass it to the server, because if the server is going to use it for trust evaluation it will have its own copy built in

I am lobbying them to stop being silly and do the following :-

give all the servers DNS names like sane people.

Yes, that’s important. Using IP addresses is setting yourself up for failure in so many ways, including ATS and IPv6 compatibility.

Use a publicly signed real certificate for the production server and rely on servertrust authentication.

Yes, that’s critical. Without this you’ll need to override ATS in order to do custom HTTPS server trust evaluation, and that will soon require that you justify your overrides to App Review. It’s much better to get a CA-issued certificate for your server.

Use self signed certificates for the test environments but add exceptions into our test application for those servers to keep ATS happy. (But these never get used in production code)

There’s a better way to do this:

  1. create your own custom CA

  2. use that CA to create a digital identity for your server

  3. install that CA’s root certificate on your client devices

This has a bunch of advantages:

  • It’s less code to write; you don’t have to override server trust evaluation on the client, disable ATS, and so on.

  • Because there’s no test code in your client, there’s no chance of you accidentally shipping an app to customers with that test code enabled.

    WARNING Don’t discount this risk. There’s at least one ‘big name’ developer who has had to ship an emergency update to their app because they accidentally shipped it with server trust evaluation disabled. Don’t be that guy!

  • Similarly, there’s no chance of any App Review hiccoughs because there are no ATS exceptions that you have to remember to remove in the production build.

  • Your testing is more realistic because it uses exactly the same code paths as your production app.

If necessary you can create a test CA using facilities that are built in macOS; see Technote 2326 Creating Certificates for TLS Testing for the details.

However am I right in thinking the primary issue is that ATS is rejecting the CACert we supply because a) it is self signed and b) we can't provide an exception due to the IP only connecting address?

It’s hard to say without being able to poke at your server. To investigate this I recommend that you disable ATS entirely (by adding an ATS dictionary that contains just the

NSAllowsArbitraryLoads
key). If the connection still fails, you know ATS is not the issue.

Share and Enjoy

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

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