Client Certificate and NSURLSession - ios

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] <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 Unknown[791] <Error>:
Oct 27 15:38:43 Philip-Banks-ipod MPEtestApplication[793] <Error>: SecTrustEvaluate [leaf AnchorTrusted]
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

Okay, after some back and forth with a helpful Apple employee and banging a few heads locally to do certs in a sensible way this is the solution I ended up with :-
#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://your.server.url";
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
{
NSLog(#"Appending data: %lu bytes", (unsigned long)[data length]);
[mDataReceived appendData:data];
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable))completionHandler
{
// We got the data.
NSLog(#"Download finished: %lu bytes", (unsigned long)[mDataReceived length]);
completionHandler(NULL);
}
-(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)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(#"Download finished: %lu bytes", (unsigned long)[mDataReceived length]);
if (error) {
NSLog(#"Error: %#", [error userInfo]);
}
}
#pragma NSURLConnection delegate
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Error: %#", [error userInfo]);
}
- (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 stringByAppendingString:#"/client.p12"];
NSData *p12data = [NSData dataWithContentsOfFile:cacertPath];
CFDataRef inP12data = (__bridge CFDataRef)p12data;
SecIdentityRef myIdentity = nil;
extractIdentity(inP12data, &myIdentity);
assert(myIdentity != nil);
NSURLCredential* credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:nil persistence:NSURLCredentialPersistenceNone];
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 extractIdentity(CFDataRef inP12data, SecIdentityRef *identity)
{
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 == errSecSuccess) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
CFIndex count = CFArrayGetCount(items);
NSLog(#"Certificates found: %ld",count);
}
if (options) {
CFRelease(options);
}
return securityError;
}
#end
We use a locally downloaded pkcs12 format file which has the private key and the cert to establish an indentity. The cert is now being signed by a public CA which means iOS is all happy and things now work nicely. Hopefully this is useful for someone else banging their head a bit.

Related

xcode ios simulator The request timed out

I have an iOS 13.3 application running on the XCode iOS simulator. It communicates with a simple python tcpserver on a Raspberry PI running on a local network. The iOS code is based on //https://dev.to/bsorrentino/develop-a-textfield-validator-for-swiftui-7d3 which uses URLSession. I am getting a timeout in the iOS code after about 10 seconds. I've checked the network packets with tcpdump on the mac. The proper packets appear to be sent and received. The url used is http://10.0.0.119:60708 as the PI server does not handle https at this point. The python server receives a JWT, prepends the thread name and echos it back. The python snippet is as follows:
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
while True:
data = self.request.recv(4096).decode('utf-8')
if len(data) == 0:
break
cur_thread = threading.current_thread()
response = "{}: {}".format(cur_thread.name, data)
print(response)
self.request.sendall(response.encode('utf-8'))
The error is:
2020-02-22 12:11:35.324596-0500 PIHome[64511:66647174] Task <2C1CE343-FB68-40CB-97C5-5E7967933838>.<2> finished with error [-1001] Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x600001046ca0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=http://10.0.0.119:60708/temperature, NSErrorFailingURLKey=http://10.0.0.119:60708/temperature, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.}
The tcpdump is as follows:
12:11:25.305647 IP 10.0.0.89.52915 > hastings.60708: Flags [S], seq 2969915581, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 2152442902 ecr 0,sackOK,eol], length 0
0x0000: b827 eb94 a718 8c85 9048 25eb 0800 4500 .'.......H%...E.
0x0010: 0040 0000 4000 4006 25e9 0a00 0059 0a00 .#..#.#.%....Y..
0x0020: 0077 ceb3 ed24 b105 50bd 0000 0000 b002 .w...$..P.......
0x0030: ffff 382f 0000 0204 05b4 0103 0306 0101 ..8/............
0x0040: 080a 804b ac16 0000 0000 0402 0000 ...K..........
12:11:25.312528 IP hastings.60708 > 10.0.0.89.52915: Flags [S.], seq 3390573497, ack 2969915582, win 28960, options [mss 1460,sackOK,TS val 3187848125 ecr 2152442902,nop,wscale 7], length 0
0x0000: 8c85 9048 25eb b827 ebc1 f24d 0800 4500 ...H%..'...M..E.
0x0010: 003c 0000 4000 4006 25ed 0a00 0077 0a00 .<..#.#.%....w..
0x0020: 0059 ed24 ceb3 ca18 0bb9 b105 50be a012 .Y.$........P...
0x0030: 7120 906f 0000 0204 05b4 0402 080a be02 q..o............
0x0040: b3bd 804b ac16 0103 0307 ...K......
12:11:25.312574 IP 10.0.0.89.52915 > hastings.60708: Flags [.], ack 1, win 2058, options [nop,nop,TS val 2152442909 ecr 3187848125], length 0
0x0000: b827 eb94 a718 8c85 9048 25eb 0800 4500 .'.......H%...E.
0x0010: 0034 0000 4000 4006 25f5 0a00 0059 0a00 .4..#.#.%....Y..
0x0020: 0077 ceb3 ed24 b105 50be ca18 0bba 8010 .w...$..P.......
0x0030: 080a 284b 0000 0101 080a 804b ac1d be02 ..(K.......K....
0x0040: b3bd ..
12:11:25.314030 IP 10.0.0.89.52915 > hastings.60708: Flags [P.], seq 1:269, ack 1, win 2058, options [nop,nop,TS val 2152442910 ecr 3187848125], length 268
0x0000: b827 eb94 a718 8c85 9048 25eb 0800 4500 .'.......H%...E.
0x0010: 0140 0000 4000 4006 24e9 0a00 0059 0a00 .#..#.#.$....Y..
0x0020: 0077 ceb3 ed24 b105 50be ca18 0bba 8018 .w...$..P.......
0x0030: 080a 597f 0000 0101 080a 804b ac1e be02 ..Y........K....
0x0040: b3bd 504f 5354 202f 7465 6d70 6572 6174 ..POST./temperat
0x0050: 7572 6520 4854 5450 2f31 2e31 0d0a 486f ure.HTTP/1.1..Ho
0x0060: 7374 3a20 3130 2e30 2e30 2e31 3139 3a36 st:.10.0.0.119:6
0x0070: 3037 3038 0d0a 436f 6e74 656e 742d 5479 0708..Content-Ty
0x0080: 7065 3a20 6170 706c 6963 6174 696f 6e2f pe:.application/
0x0090: 6a73 6f6e 0d0a 436f 6e6e 6563 7469 6f6e json..Connection
0x00a0: 3a20 6b65 6570 2d61 6c69 7665 0d0a 4163 :.keep-alive..Ac
0x00b0: 6365 7074 3a20 6170 706c 6963 6174 696f cept:.applicatio
0x00c0: 6e2f 6a73 6f6e 0d0a 5573 6572 2d41 6765 n/json..User-Age
0x00d0: 6e74 3a20 5049 486f 6d65 2f31 2043 464e nt:.PIHome/1.CFN
0x00e0: 6574 776f 726b 2f31 3132 312e 322e 3120 etwork/1121.2.1.
0x00f0: 4461 7277 696e 2f31 392e 332e 300d 0a43 Darwin/19.3.0..C
0x0100: 6f6e 7465 6e74 2d4c 656e 6774 683a 2032 ontent-Length:.2
0x0110: 3139 0d0a 4163 6365 7074 2d4c 616e 6775 19..Accept-Langu
0x0120: 6167 653a 2065 6e2d 7573 0d0a 4163 6365 age:.en-us..Acce
0x0130: 7074 2d45 6e63 6f64 696e 673a 2067 7a69 pt-Encoding:.gzi
0x0140: 702c 2064 6566 6c61 7465 0d0a 0d0a p,.deflate....
12:11:25.314317 IP 10.0.0.89.52915 > hastings.60708: Flags [P.], seq 269:488, ack 1, win 2058, options [nop,nop,TS val 2152442910 ecr 3187848125], length 219
0x0000: b827 eb94 a718 8c85 9048 25eb 0800 4500 .'.......H%...E.
0x0010: 010f 0000 4000 4006 251a 0a00 0059 0a00 ....#.#.%....Y..
0x0020: 0077 ceb3 ed24 b105 51ca ca18 0bba 8018 .w...$..Q.......
0x0030: 080a e989 0000 0101 080a 804b ac1e be02 ...........K....
0x0040: b3bd 224c 6f67 696e 2073 7563 6365 7373 .."Login.success
0x0050: 6675 6c3a 6579 4a30 6558 4169 4f69 4a71 ful:eyJ0eXAiOiJq
0x0060: 6433 5169 4c43 4a68 6247 6369 4f69 4a49 d3QiLCJhbGciOiJI
0x0070: 557a 4931 4e69 4a39 2e65 794a 7a64 5749 UzI1NiJ9.eyJzdWI
0x0080: 694f 694a 5153 5568 7662 5755 6762 4739 iOiJQSUhvbWUgbG9
0x0090: 6e61 5734 694c 434a 3163 3256 7949 6a6f naW4iLCJ1c2VyIjo
0x00a0: 6963 476c 6f62 3231 6c4c 6e52 6862 6d52 icGlob21lLnRhbmR
0x00b0: 6f51 4752 6862 4756 354c 6e64 7a49 6977 oQGRhbGV5LndzIiw
0x00c0: 6961 5746 3049 6a6f 784e 5467 794d 7a6b iaWF0IjoxNTgyMzk
0x00d0: 784e 4467 314c 434a 6e63 6d39 3163 484d xNDg1LCJncm91cHM
0x00e0: 694f 6c73 6964 4746 755a 4767 6958 5830 iOlsidGFuZGgiXX0
0x00f0: 2e53 6770 6875 4d6a 6347 6f37 6648 6a6a .SgphuMjcGo7fHjj
0x0100: 7878 4736 5569 7770 6c67 754d 645a 6268 xxG6UiwplguMdZbh
0x0110: 6374 2d35 7a44 6344 6e64 4c59 22 ct-5zDcDndLY"
12:11:25.318063 IP hastings.60708 > 10.0.0.89.52915: Flags [.], ack 269, win 235, options [nop,nop,TS val 3187848130 ecr 2152442910], length 0
0x0000: 8c85 9048 25eb b827 ebc1 f24d 0800 4500 ...H%..'...M..E.
0x0010: 0034 4928 4000 4006 dccc 0a00 0077 0a00 .4I(#.#......w..
0x0020: 0059 ed24 ceb3 ca18 0bba b105 51ca 8010 .Y.$........Q...
0x0030: 00eb 2e58 0000 0101 080a be02 b3c2 804b ...X...........K
0x0040: ac1e ..
12:11:25.318064 IP hastings.60708 > 10.0.0.89.52915: Flags [P.], seq 1:280, ack 269, win 235, options [nop,nop,TS val 3187848131 ecr 2152442910], length 279
0x0000: 8c85 9048 25eb b827 ebc1 f24d 0800 4500 ...H%..'...M..E.
0x0010: 014b 4929 4000 4006 dbb4 0a00 0077 0a00 .KI)#.#......w..
0x0020: 0059 ed24 ceb3 ca18 0bba b105 51ca 8018 .Y.$........Q...
0x0030: 00eb cccc 0000 0101 080a be02 b3c3 804b ...............K
0x0040: ac1e 5468 7265 6164 2d31 343a 2050 4f53 ..Thread-14:.POS
0x0050: 5420 2f74 656d 7065 7261 7475 7265 2048 T./temperature.H
0x0060: 5454 502f 312e 310d 0a48 6f73 743a 2031 TTP/1.1..Host:.1
0x0070: 302e 302e 302e 3131 393a 3630 3730 380d 0.0.0.119:60708.
0x0080: 0a43 6f6e 7465 6e74 2d54 7970 653a 2061 .Content-Type:.a
0x0090: 7070 6c69 6361 7469 6f6e 2f6a 736f 6e0d pplication/json.
0x00a0: 0a43 6f6e 6e65 6374 696f 6e3a 206b 6565 .Connection:.kee
0x00b0: 702d 616c 6976 650d 0a41 6363 6570 743a p-alive..Accept:
0x00c0: 2061 7070 6c69 6361 7469 6f6e 2f6a 736f .application/jso
0x00d0: 6e0d 0a55 7365 722d 4167 656e 743a 2050 n..User-Agent:.P
0x00e0: 4948 6f6d 652f 3120 4346 4e65 7477 6f72 IHome/1.CFNetwor
0x00f0: 6b2f 3131 3231 2e32 2e31 2044 6172 7769 k/1121.2.1.Darwi
0x0100: 6e2f 3139 2e33 2e30 0d0a 436f 6e74 656e n/19.3.0..Conten
0x0110: 742d 4c65 6e67 7468 3a20 3231 390d 0a41 t-Length:.219..A
0x0120: 6363 6570 742d 4c61 6e67 7561 6765 3a20 ccept-Language:.
0x0130: 656e 2d75 730d 0a41 6363 6570 742d 456e en-us..Accept-En
0x0140: 636f 6469 6e67 3a20 677a 6970 2c20 6465 coding:.gzip,.de
0x0150: 666c 6174 650d 0a0d 0a flate....
12:11:25.318096 IP 10.0.0.89.52915 > hastings.60708: Flags [.], ack 280, win 2054, options [nop,nop,TS val 2152442914 ecr 3187848131], length 0
0x0000: b827 eb94 a718 8c85 9048 25eb 0800 4500 .'.......H%...E.
0x0010: 0034 0000 4000 4006 25f5 0a00 0059 0a00 .4..#.#.%....Y..
0x0020: 0077 ceb3 ed24 b105 52a5 ca18 0cd1 8010 .w...$..R.......
0x0030: 0806 2546 0000 0101 080a 804b ac22 be02 ..%F.......K."..
0x0040: b3c3 ..
12:11:25.321081 IP hastings.60708 > 10.0.0.89.52915: Flags [P.], seq 280:510, ack 488, win 243, options [nop,nop,TS val 3187848134 ecr 2152442910], length 230
0x0000: 8c85 9048 25eb b827 ebc1 f24d 0800 4500 ...H%..'...M..E.
0x0010: 011a 492a 4000 4006 dbe4 0a00 0077 0a00 ..I*#.#......w..
0x0020: 0059 ed24 ceb3 ca18 0cd1 b105 52a5 8018 .Y.$........R...
0x0030: 00f3 b091 0000 0101 080a be02 b3c6 804b ...............K
0x0040: ac1e 5468 7265 6164 2d31 343a 2022 4c6f ..Thread-14:."Lo
0x0050: 6769 6e20 7375 6363 6573 7366 756c 3a65 gin.successful:e
0x0060: 794a 3065 5841 694f 694a 7164 3351 694c yJ0eXAiOiJqd3QiL
0x0070: 434a 6862 4763 694f 694a 4955 7a49 314e CJhbGciOiJIUzI1N
0x0080: 694a 392e 6579 4a7a 6457 4969 4f69 4a51 iJ9.eyJzdWIiOiJQ
0x0090: 5355 6876 6257 5567 6247 396e 6157 3469 SUhvbWUgbG9naW4i
0x00a0: 4c43 4a31 6332 5679 496a 6f69 6347 6c6f LCJ1c2VyIjoicGlo
0x00b0: 6232 316c 4c6e 5268 626d 526f 5147 5268 b21lLnRhbmRoQGRh
0x00c0: 6247 5635 4c6e 647a 4969 7769 6157 4630 bGV5LndzIiwiaWF0
0x00d0: 496a 6f78 4e54 6779 4d7a 6b78 4e44 6731 IjoxNTgyMzkxNDg1
0x00e0: 4c43 4a6e 636d 3931 6348 4d69 4f6c 7369 LCJncm91cHMiOlsi
0x00f0: 6447 4675 5a47 6769 5858 302e 5367 7068 dGFuZGgiXX0.Sgph
0x0100: 754d 6a63 476f 3766 486a 6a78 7847 3655 uMjcGo7fHjjxxG6U
0x0110: 6977 706c 6775 4d64 5a62 6863 742d 357a iwplguMdZbhct-5z
0x0120: 4463 446e 644c 5922 DcDndLY"
12:11:25.321101 IP 10.0.0.89.52915 > hastings.60708: Flags [.], ack 510, win 2050, options [nop,nop,TS val 2152442917 ecr 3187848134], length 0
0x0000: b827 eb94 a718 8c85 9048 25eb 0800 4500 .'.......H%...E.
0x0010: 0034 0000 4000 4006 25f5 0a00 0059 0a00 .4..#.#.%....Y..
0x0020: 0077 ceb3 ed24 b105 52a5 ca18 0db7 8010 .w...$..R.......
0x0030: 0802 245e 0000 0101 080a 804b ac25 be02 ..$^.......K.%..
0x0040: b3c6 ..
12:11:35.324471 IP 10.0.0.89.52915 > hastings.60708: Flags [F.], seq 488, ack 510, win 2050, options [nop,nop,TS val 2152452893 ecr 3187848134], length 0
0x0000: b827 eb94 a718 8c85 9048 25eb 0800 4500 .'.......H%...E.
0x0010: 0034 0000 4000 4006 25f5 0a00 0059 0a00 .4..#.#.%....Y..
0x0020: 0077 ceb3 ed24 b105 52a5 ca18 0db7 8011 .w...$..R.......
0x0030: 0802 fd64 0000 0101 080a 804b d31d be02 ...d.......K....
0x0040: b3c6 ..
12:11:35.331969 IP hastings.60708 > 10.0.0.89.52915: Flags [F.], seq 510, ack 489, win 243, options [nop,nop,TS val 3187858144 ecr 2152452893], length 0
0x0000: 8c85 9048 25eb b827 ebc1 f24d 0800 4500 ...H%..'...M..E.
0x0010: 0034 492b 4000 4006 dcc9 0a00 0077 0a00 .4I+#.#......w..
0x0020: 0059 ed24 ceb3 ca18 0db7 b105 52a6 8011 .Y.$........R...
0x0030: 00f3 dd58 0000 0101 080a be02 dae0 804b ...X...........K
0x0040: d31d ..
12:11:35.332037 IP 10.0.0.89.52915 > hastings.60708: Flags [.], ack 511, win 2050, options [nop,nop,TS val 2152452899 ecr 3187858144], length 0
0x0000: b827 eb94 a718 8c85 9048 25eb 0800 4500 .'.......H%...E.
0x0010: 0034 0000 4000 4006 25f5 0a00 0059 0a00 .4..#.#.%....Y..
0x0020: 0077 ceb3 ed24 b105 52a6 ca18 0db8 8010 .w...$..R.......
0x0030: 0802 d643 0000 0101 080a 804b d323 be02 ...C.......K.#..
0x0040: dae0 ..
Is this simply a problem with https or is it something deeper?
This might be due to ATS, try to set the following keys in your application Info.plist
NSAppTransportSecurity : Dictionary {
NSAllowsArbitraryLoads : Boolean -> YES
NSAllowsArbitraryLoadsForMedia : Boolean -> YES
NSAllowsArbitraryLoadsInWebContent : Boolean -> YES
NSAllowsLocalNetworking : Boolean -> YES
Find more details on official Preventing Insecure Network Connections

WinRM Negotiate get 200 instead of 401

I am working on a Ruby on Rails application which uses the WinRM library to access a remote Windows server. The transport supplied is :negotiate which will negotiate the authentication with the remote server.
The issue is the WinRM library expects a 401 HTTP status code so that it can send more data for authentication. However, a 200 HTTP status code is returned and the negotiate fails.
The backtrace is :
NoMethodError: undefined method `split' for nil:NilClass
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/http/transport.rb:226:in `init_auth'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/http/transport.rb:166:in `send_request'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/winrm_service.rb:489:in `send_message'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/winrm_service.rb:390:in `run_wql'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/command_executor.rb:186:in `os_version'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/command_executor.rb:145:in `code_page'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/command_executor.rb:72:in `block in open'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/command_executor.rb:218:in `retryable'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/winrm-1.7.3/lib/winrm/command_executor.rb:71:in `open'
from (irb):20
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/railties-4.2.1/lib/rails/commands/console.rb:110:in `start'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/railties-4.2.1/lib/rails/commands/console.rb:9:in `start'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/railties-4.2.1/lib/rails/commands/commands_tasks.rb:68:in `console'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/railties-4.2.1/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
from /home/cobalt/.rvm/gems/ruby-2.2.2/gems/railties-4.2.1/lib/rails/commands.rb:17:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'2.2.2 :021 >
The TCP Dump shows the below package exchanges
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
12:04:08.372376 IP d8b5d56cba65.53166 > pokcpeusap02.corp.absc.local.wsman: Flags [S], seq 2899844066, win 29200, options [mss 1460,sackOK,TS val 1316187676 ecr 0,nop,wscale 7], length 0
0x0000: 4500 003c 75fe 4000 4006 d486 0400 0005 E..<u.#.#.......
0x0010: 0acd e165 cfae 1761 acd8 1be2 0000 0000 ...e...a........
0x0020: a002 7210 f065 0000 0204 05b4 0402 080a ..r..e..........
0x0030: 4e73 6e1c 0000 0000 0103 0307 Nsn.........
12:04:08.421019 IP pokcpeusap02.corp.absc.local.wsman > d8b5d56cba65.53166: Flags [S.], seq 3702856093, ack 2899844067, win 8192, options [mss 1351,nop,wscale 8,sackOK,TS val 79780711 ecr 1316187676],
length 0
0x0000: 4500 003c 7f04 4000 7d06 8e80 0acd e165 E..<..#.}......e
0x0010: 0400 0005 1761 cfae dcb5 199d acd8 1be3 .....a..........
0x0020: a012 2000 754e 0000 0204 0547 0103 0308 ....uN.....G....
0x0030: 0402 080a 04c1 5b67 4e73 6e1c ......[gNsn.
12:04:08.421047 IP d8b5d56cba65.53166 > pokcpeusap02.corp.absc.local.wsman: Flags [.], ack 1, win 229, options [nop,nop,TS val 1316187725 ecr 79780711], length 0
0x0000: 4500 0034 75ff 4000 4006 d48d 0400 0005 E..4u.#.#.......
0x0010: 0acd e165 cfae 1761 acd8 1be3 dcb5 199e ...e...a........
0x0020: 8010 00e5 f05d 0000 0101 080a 4e73 6e4d .....]......NsnM
0x0030: 04c1 5b67 ..[g
12:04:08.421368 IP d8b5d56cba65.53166 > pokcpeusap02.corp.absc.local.wsman: Flags [P.], seq 1:340, ack 1, win 229, options [nop,nop,TS val 1316187725 ecr 79780711], length 339
0x0000: 4500 0187 7600 4000 4006 d339 0400 0005 E...v.#.#..9....
0x0010: 0acd e165 cfae 1761 acd8 1be3 dcb5 199e ...e...a........
0x0020: 8018 00e5 f1b0 0000 0101 080a 4e73 6e4d ............NsnM
0x0030: 04c1 5b67 504f 5354 202f 7773 6d61 6e20 ..[gPOST./wsman.
0x0040: 4854 5450 2f31 2e31 0d0a 4175 7468 6f72 HTTP/1.1..Author
0x0050: 697a 6174 696f 6e3a 204e 6567 6f74 6961 ization:.Negotia
0x0060: 7465 2054 6c52 4d54 564e 5455 4141 4241 te.TlRMTVNTUAABA
0x0070: 4141 414e 3449 4934 4151 4142 4141 6741 AAAN4II4AQABAAgA
0x0080: 4141 4141 4141 4141 4351 4141 4142 4462 AAAAAAAACQAAABDb
0x0090: 334a 775a 4468 694e 5751 314e 6d4e 6959 3JwZDhiNWQ1NmNiY
0x00a0: 5459 310d 0a43 6f6e 7465 6e74 2d54 7970 TY1..Content-Typ
0x00b0: 653a 2061 7070 6c69 6361 7469 6f6e 2f73 e:.application/s
0x00c0: 6f61 702b 786d 6c3b 6368 6172 7365 743d oap+xml;charset=
0x00d0: 5554 462d 380d 0a55 7365 722d 4167 656e UTF-8..User-Agen
0x00e0: 743a 2052 7562 7920 5769 6e52 4d20 436c t:.Ruby.WinRM.Cl
0x00f0: 6965 6e74 2028 322e 372e 312c 2072 7562 ient.(2.7.1,.rub
0x0100: 7920 322e 322e 3220 2832 3031 352d 3034 y.2.2.2.(2015-04
0x0110: 2d31 3329 290d 0a41 6363 6570 743a 202a -13))..Accept:.*
0x0120: 2f2a 0d0a 4461 7465 3a20 5475 652c 2030 /*..Date:.Tue,.0
0x0130: 3720 4d61 7220 3230 3137 2031 323a 3034 7.Mar.2017.12:04
0x0140: 3a30 3820 474d 540d 0a43 6f6e 7465 6e74 :08.GMT..Content
0x0150: 2d4c 656e 6774 683a 2030 0d0a 486f 7374 -Length:.0..Host
0x0160: 3a20 706f 6b63 7065 7573 6170 3032 2e63 :.pokcpeusap02.c
0x0170: 6f72 702e 6162 7363 2e6c 6f63 616c 3a35 orp.absc.local:5
0x0180: 3938 350d 0a0d 0a 985....
12:04:08.516497 IP pokcpeusap02.corp.absc.local.wsman > d8b5d56cba65.53166: Flags [P.], seq 1:39, ack 340, win 256, options [nop,nop,TS val 79780721 ecr 1316187725], length 38
0x0000: 4500 005a 7f05 4000 7d06 8e61 0acd e165 E..Z..#.}..a...e
0x0010: 0400 0005 1761 cfae dcb5 199e acd8 1d36 .....a.........6
0x0020: 8018 0100 11f4 0000 0101 080a 04c1 5b71 ..............[q
0x0030: 4e73 6e4d 4854 5450 2f31 2e31 2032 3030 NsnMHTTP/1.1.200
0x0040: 204f 4b0d 0a43 6f6e 7465 6e74 2d4c 656e .OK..Content-Len
0x0050: 6774 683a 2030 0d0a 0d0a gth:.0....
12:04:08.516541 IP d8b5d56cba65.53166 > pokcpeusap02.corp.absc.local.wsman: Flags [.], ack 39, win 229, options [nop,nop,TS val 1316187821 ecr 79780721], length 0
0x0000: 4500 0034 7601 4000 4006 d48b 0400 0005 E..4v.#.#.......
0x0010: 0acd e165 cfae 1761 acd8 1d36 dcb5 19c4 ...e...a...6....
0x0020: 8010 00e5 f05d 0000 0101 080a 4e73 6ead .....]......Nsn.
0x0030: 04c1 5b71 ..[q
What would be the issue? Why I don't get a 401 HTTP status code?
I havae managed to found the root cause of the issue. It turns out there is another service instead of WinRM srevice is listening to port 5985. Hence when a request is sent to that port, that service responded with a respone requiring Basic Authentication and the status code 200. The issue is fixed after starting WinRM service and make it listen at port 5985.
The detailed analysis can be found at Ruby WinRM undefined method `split' for nil:NilClass. It's really a good lesson to learn. Sometimes the issue is very simple and stupid, but to find out the issue would take much effort.

GCDWebServer for streaming

I was trying to stream a mp4 video from a remote server on a iOS device. I somehow got
[ERROR] Error while writing to socket 15: Broken pipe (32) after a few writes.
Not sure where is wrong. Can you kindly give any advice?
iOS re-routing requests w/ GCDWebServer (not redirecting)
Thanks!
Rao
#property (nonatomic, strong) GCDWebServerBodyReaderCompletionBlock cb;
[_webServer addDefaultHandlerForMethod:#"GET"
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
GCDWebServerStreamedResponse* response = [GCDWebServerStreamedResponse responseWithContentType:#"application/octet-stream" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) {
NSString * realURL = #"https://ia800309.us.archive.org/2/items/Popeye_Nearlyweds/Popeye_Nearlyweds_512kb.mp4";
self.cb = completionBlock;
NSURLSessionDataTask * dataTask = [self.session dataTaskWithURL:[[NSURL alloc] initWithString:realURL]] ;
}];
return response;
}];
(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
self.response = response;
completionHandler(NSURLSessionResponseAllow);
}
(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
NSLog(#"got data size == %lu", data.length);
self.cb(data, nil);
}
(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
self.cb([NSData data], nil);
}
[DEBUG] Did open IPv4 listening socket 6
[DEBUG] Did open IPv6 listening socket 7
[INFO] GCDWebServer started on port 8080 and reachable at http://192.168.1.27:8080/
2016-02-23 15:34:39.096 Proxy2Proxy[5315:1148991] Visit http://192.168.1.27:8080/ in your web browser
[DEBUG] Did open connection on socket 15
[DEBUG] Did connect
[DEBUG] Did start background task
[DEBUG] Connection received 370 bytes on socket 15
[DEBUG] Connection on socket 15 preflighting request "GET /https://ia800309.us.archive.org/2/items/Popeye_Nearlyweds/Popeye_Nearlyweds_512kb.mp4" with 370 bytes body
[DEBUG] Connection on socket 15 processing request "GET /https://ia800309.us.archive.org/2/items/Popeye_Nearlyweds/Popeye_Nearlyweds_512kb.mp4" with 370 bytes body
[DEBUG] Connection sent 175 bytes on socket 15
2016-02-23 15:34:47.488 Proxy2Proxy[5315:1148991] got data size == 16059
[DEBUG] Connection sent 16067 bytes on socket 15
2016-02-23 15:34:47.488 Proxy2Proxy[5315:1148991] got data size == 32768
[DEBUG] Connection sent 32776 bytes on socket 15
2016-02-23 15:34:47.489 Proxy2Proxy[5315:1148991] got data size == 32768
[DEBUG] Connection sent 32776 bytes on socket 15
2016-02-23 15:34:47.489 Proxy2Proxy[5315:1148991] got data size == 98304
2016-02-23 15:34:47.490 Proxy2Proxy[5315:1148991] got data size == 16384
[DEBUG] Connection sent 98313 bytes on socket 15
[DEBUG] Connection sent 16392 bytes on socket 15
2016-02-23 15:34:47.506 Proxy2Proxy[5315:1148991] got data size == 16384
[DEBUG] Connection sent 16392 bytes on socket 15
2016-02-23 15:34:47.517 Proxy2Proxy[5315:1148991] got data size == 16384
[DEBUG] Connection sent 16392 bytes on socket 15
2016-02-23 15:34:47.530 Proxy2Proxy[5315:1148991] got data size == 16384
[DEBUG] Connection sent 16392 bytes on socket 15
2016-02-23 15:34:47.545 Proxy2Proxy[5315:1148991] got data size == 16384
[ERROR] Error while writing to socket 15: Broken pipe (32)

How to print Tagged Pointer for NSNumber?

I read it's article and i try print Tagged Pointer for NSNumber
I try in xcode follow code:
- (NSString *)binForObjectPointer:(NSObject *)obj
{
return [self binForScalarNumber:(uintptr_t)(__bridge void *)(obj)];
}
- (NSString *)binForScalarNumber:(uintptr_t)number
{
NSString *ms = #"";
while (number)
{
if (number & 1)
ms = [ms stringByAppendingString:#"1"];
else
ms = [ms stringByAppendingString:#"0"];
number >>= 1;
}
return ms;
}
for (int i = 0; i < 10; ++i)
{
NSNumber *n = [NSNumber numberWithInt:i];
NSString *s = [self binForObjectPointer:n];
NSLog(#"%i %# %# %#",
i,
[s substringWithRange:NSMakeRange(0, s.length - 4)],
[s substringWithRange:NSMakeRange(s.length - 4, 3)],
[s substringWithRange:NSMakeRange(s.length - 1, 1)]
);
}
and get result in console
2014-09-25 16:57:46.926 AppName[3983:60b] 0 0000001111100101001010100 110 1
2014-09-25 16:57:46.930 AppName[3983:60b] 1 0000010000000101001010100 110 1
2014-09-25 16:57:46.931 AppName[3983:60b] 2 0000000001110101001010100 110 1
2014-09-25 16:57:46.932 AppName[3983:60b] 3 0000011011111101110001100 110 1
2014-09-25 16:57:46.933 AppName[3983:60b] 4 0000011011000110000110100 110 1
2014-09-25 16:57:46.934 AppName[3983:60b] 5 0000101101000110101001100 110 1
2014-09-25 16:57:46.935 AppName[3983:60b] 6 0000100110100110111010100 110 1
2014-09-25 16:57:46.936 AppName[3983:60b] 7 0000000111011010101001100 110 1
2014-09-25 16:57:46.938 AppName[3983:60b] 8 0000001100000110101010100 110 1
2014-09-25 16:57:46.939 AppName[3983:60b] 9 0000001011000110000110100 110 1
It doesn't look like in article example:
0000 0000 0000 0000 0000 0000 0011 1011
^ ^ ^ tag bit
| |
| tagged pointer class (5)
|
binary 3
When my error?

How do I URL encode a string

I have a URL string (NSString) with spaces and & characters. How do I url encode the entire string (including the & ampersand character and spaces)?
Unfortunately, stringByAddingPercentEscapesUsingEncoding doesn't always work 100%. It encodes non-URL characters but leaves the reserved characters (like slash / and ampersand &) alone. Apparently this is a bug that Apple is aware of, but since they have not fixed it yet, I have been using this category to url-encode a string:
#implementation NSString (NSString_Extended)
- (NSString *)urlencode {
NSMutableString *output = [NSMutableString string];
const unsigned char *source = (const unsigned char *)[self UTF8String];
int sourceLen = strlen((const char *)source);
for (int i = 0; i < sourceLen; ++i) {
const unsigned char thisChar = source[i];
if (thisChar == ' '){
[output appendString:#"+"];
} else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' ||
(thisChar >= 'a' && thisChar <= 'z') ||
(thisChar >= 'A' && thisChar <= 'Z') ||
(thisChar >= '0' && thisChar <= '9')) {
[output appendFormat:#"%c", thisChar];
} else {
[output appendFormat:#"%%%02X", thisChar];
}
}
return output;
}
Used like this:
NSString *urlEncodedString = [#"SOME_URL_GOES_HERE" urlencode];
// Or, with an already existing string:
NSString *someUrlString = #"someURL";
NSString *encodedUrlStr = [someUrlString urlencode];
This also works:
NSString *encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)unencodedString,
NULL,
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8 );
Some good reading about the subject:
Objective-c iPhone percent encode a string?
Objective-C and Swift URL encoding
http://cybersam.com/programming/proper-url-percent-encoding-in-ios
https://devforums.apple.com/message/15674#15674
http://simonwoodside.com/weblog/2009/4/22/how_to_really_url_encode/
This might be helpful
NSString *sampleUrl = #"http://www.google.com/search.jsp?params=Java Developer";
NSString* encodedUrl = [sampleUrl stringByAddingPercentEscapesUsingEncoding:
NSUTF8StringEncoding];
For iOS 7+, the recommended way is:
NSString* encodedUrl = [sampleUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
You can choose the allowed character set as per the requirement of the URL component.
New APIs have been added since the answer was selected; You can now use NSURLUtilities. Since different parts of URLs allow different characters, use the applicable character set. The following example encodes for inclusion in the query string:
encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];
To specifically convert '&', you'll need to remove it from the url query set or use a different set, as '&' is allowed in a URL query:
NSMutableCharacterSet *chars = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy;
[chars removeCharactersInRange:NSMakeRange('&', 1)]; // %26
encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:chars];
Swift 2.0 Example (iOS 9 Compatiable)
extension String {
func stringByURLEncoding() -> String? {
let characters = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet
characters.removeCharactersInString("&")
guard let encodedString = self.stringByAddingPercentEncodingWithAllowedCharacters(characters) else {
return nil
}
return encodedString
}
}
ios 7 update
NSString *encode = [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSString *decode = [encode stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
Here's a production-ready flexible approach in Swift 5.x:
public extension CharacterSet {
static let urlQueryParameterAllowed = CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "&?"))
static let urlQueryDenied = CharacterSet.urlQueryAllowed.inverted()
static let urlQueryKeyValueDenied = CharacterSet.urlQueryParameterAllowed.inverted()
static let urlPathDenied = CharacterSet.urlPathAllowed.inverted()
static let urlFragmentDenied = CharacterSet.urlFragmentAllowed.inverted()
static let urlHostDenied = CharacterSet.urlHostAllowed.inverted()
static let urlDenied = CharacterSet.urlQueryDenied
.union(.urlQueryKeyValueDenied)
.union(.urlPathDenied)
.union(.urlFragmentDenied)
.union(.urlHostDenied)
func inverted() -> CharacterSet {
var copy = self
copy.invert()
return copy
}
}
public extension String {
func urlEncoded(denying deniedCharacters: CharacterSet = .urlDenied) -> String? {
return addingPercentEncoding(withAllowedCharacters: deniedCharacters.inverted())
}
}
Example usage:
print("Hello, World!".urlEncoded()!)
print("You&Me?".urlEncoded()!)
print("#Blessed 100%".urlEncoded()!)
print("Pride and Prejudice".urlEncoded(denying: .uppercaseLetters)!)
Output:
Hello,%20World!
You%26Me%3F
%23Blessed%20100%25
%50ride and %50rejudice
I opted to use the CFURLCreateStringByAddingPercentEscapes call as given by accepted answer, however in newest version of XCode (and IOS), it resulted in an error, so used the following instead:
NSString *apiKeyRaw = #"79b|7Qd.jW=])(fv|M&W0O|3CENnrbNh4}2E|-)J*BCjCMrWy%dSfGs#A6N38Fo~";
NSString *apiKey = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)apiKeyRaw, NULL, (CFStringRef)#"!*'();:#&=+$,/?%#[]", kCFStringEncodingUTF8));
This code helped me for encoding special characters
NSString* encPassword = [password stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]];
Try to use stringByAddingPercentEncodingWithAllowedCharacters method with [NSCharacterSet URLUserAllowedCharacterSet] it will cover all the cases
Objective C
NSString *value = #"Test / Test";
value = [value stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLUserAllowedCharacterSet]];
swift
var value = "Test / Test"
value.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLUserAllowedCharacterSet())
Output
Test%20%2F%20Test
After reading all the answers for this topic and the (wrong) accepted one, I want to add my contribution.
IF the target is iOS7+, and in 2017 it should since XCode makes really hard to deliver compatibility under iOS8, the best way, thread safe, fast, amd will full UTF-8 support to do this is:
(Objective C code)
#implementation NSString (NSString_urlencoding)
- (NSString *)urlencode {
static NSMutableCharacterSet *chars = nil;
static dispatch_once_t pred;
if (chars)
return [self stringByAddingPercentEncodingWithAllowedCharacters:chars];
// to be thread safe
dispatch_once(&pred, ^{
chars = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy;
[chars removeCharactersInString:#"!*'();:#&=+$,/?%#[]"];
});
return [self stringByAddingPercentEncodingWithAllowedCharacters:chars];
}
#end
This will extend NSString, will exclude RFC forbidden characters, support UTF-8 characters, and let you use things like:
NSString *myusername = "I'm[evil]&want(to)break!!!$->àéìòù";
NSLog(#"Source: %# -> Dest: %#", myusername, [myusername urlencode]);
That will print on your debug console:
Source: I'm[evil]&want(to)break!!!$->àéìòù -> Dest: I%27m%5Bevil%5D%26want%28to%29break%21%21%21%24-%3E%C3%A0%C3%A9%C3%AC%C3%B2%C3%B9
... note also the use of dispatch_once to avoid multiple initializations in multithread environments.
Apple's advice, in the 10.11 release notes, is:
If you need to percent-encode an entire URL string, you can use this code to encode a NSString intended to be a URL (in urlStringToEncode):
NSString *percentEncodedURLString =
[[NSURL URLWithDataRepresentation:[urlStringToEncode dataUsingEncoding:NSUTF8StringEncoding] relativeToURL:nil] relativeString];
swift code based on chown's objc answer in this thread.
extension String {
func urlEncode() -> String {
return CFURLCreateStringByAddingPercentEscapes(
nil,
self,
nil,
"!*'();:#&=+$,/?%#[]",
CFStringBuiltInEncodings.UTF8.rawValue
)
}
}
Use NSURLComponents to encode HTTP GET parameters:
var urlComponents = NSURLComponents(string: "https://www.google.de/maps/")!
urlComponents.queryItems = [
NSURLQueryItem(name: "q", value: String(51.500833)+","+String(-0.141944)),
NSURLQueryItem(name: "z", value: String(6))
]
urlComponents.URL // returns https://www.google.de/maps/?q=51.500833,-0.141944&z=6
http://www.ralfebert.de/snippets/ios/encoding-nsurl-get-parameters/
In swift 3:
// exclude alpha and numeric == "full" encoding
stringUrl = stringUrl.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!;
// exclude hostname and symbols &,/ and etc
stringUrl = stringUrl.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!;
In Swift 3, please try out below:
let stringURL = "YOUR URL TO BE ENCODE";
let encodedURLString = stringURL.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(encodedURLString)
Since, stringByAddingPercentEscapesUsingEncoding encodes non URL characters but leaves the reserved characters (like !*'();:#&=+$,/?%#[]), You can encode the url like the following code:
let stringURL = "YOUR URL TO BE ENCODE";
let characterSetTobeAllowed = (CharacterSet(charactersIn: "!*'();:#&=+$,/?%#[] ").inverted)
if let encodedURLString = stringURL.addingPercentEncoding(withAllowedCharacters: characterSetTobeAllowed) {
print(encodedURLString)
}
In my case where the last component was Arabic letters I did the following in Swift 2.2:
extension String {
func encodeUTF8() -> String? {
//If I can create an NSURL out of the string nothing is wrong with it
if let _ = NSURL(string: self) {
return self
}
//Get the last component from the string this will return subSequence
let optionalLastComponent = self.characters.split { $0 == "/" }.last
if let lastComponent = optionalLastComponent {
//Get the string from the sub sequence by mapping the characters to [String] then reduce the array to String
let lastComponentAsString = lastComponent.map { String($0) }.reduce("", combine: +)
//Get the range of the last component
if let rangeOfLastComponent = self.rangeOfString(lastComponentAsString) {
//Get the string without its last component
let stringWithoutLastComponent = self.substringToIndex(rangeOfLastComponent.startIndex)
//Encode the last component
if let lastComponentEncoded = lastComponentAsString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.alphanumericCharacterSet()) {
//Finally append the original string (without its last component) to the encoded part (encoded last component)
let encodedString = stringWithoutLastComponent + lastComponentEncoded
//Return the string (original string/encoded string)
return encodedString
}
}
}
return nil;
}
}
usage:
let stringURL = "http://xxx.dev.com/endpoint/nonLatinCharacters"
if let encodedStringURL = stringURL.encodeUTF8() {
if let url = NSURL(string: encodedStringURL) {
...
}
}
-(NSString *)encodeUrlString:(NSString *)string {
return CFBridgingRelease(
CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge CFStringRef)string,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8)
);
}
according to the following blog
So many answers but didn't work for me, so I tried the following:
fun simpleServiceCall(for serviceUrl: String, appendToUrl: String) {
let urlString: String = serviceUrl + appendToUrl.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!
let finalUrl = URL(string: urlString)!
//continue to execute your service call...
}
Hopefully it'll help someone. thanks
For individual www form-encoded query parameters, I made a category on NSString:
- (NSString*)WWWFormEncoded{
NSMutableCharacterSet *chars = NSCharacterSet.alphanumericCharacterSet.mutableCopy;
[chars addCharactersInString:#" "];
NSString* encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:chars];
encodedString = [encodedString stringByReplacingOccurrencesOfString:#" " withString:#"+"];
return encodedString;
}
//This is without test
NSMutableCharacterSet* set = [[NSCharacterSet alphanumericCharacterSet] mutableCopy];
[set addCharactersInString:#"-_.~"];
NSString *encode = [test stringByAddingPercentEncodingWithAllowedCharacters:set];
I faced a similar problem passing complex strings as a POST parameter. My strings can contain Asian characters, spaces, quotes and all sorts of special characters. The solution I eventually found was to convert my string into the matching series of unicodes, e.g. "Hu0040Hu0020Hu03f5...." using [NSString stringWithFormat:#"Hu%04x",[string characterAtIndex:i]] to get the Unicode from each character in the original string. The same can be done in Java.
This string can be safely passed as a POST parameter.
On the server side (PHP), I change all the "H" to "\" and I pass the resulting string to json_decode. Final step is to escape single quotes before storing the string into MySQL.
This way I can store any UTF8 string on my server.
This one is working for me.
func stringByAddingPercentEncodingForFormData(plusForSpace: Bool=false) -> String? {
let unreserved = "*-._"
let allowed = NSMutableCharacterSet.alphanumericCharacterSet()
allowed.addCharactersInString(unreserved)
if plusForSpace {
allowed.addCharactersInString(" ")
}
var encoded = stringByAddingPercentEncodingWithAllowedCharacters(allowed)
if plusForSpace {
encoded = encoded?.stringByReplacingOccurrencesOfString(" ",
withString: "+")
}
return encoded
}
I found the above function from this link: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/
You can also use this function with swift extension. Please let me know if there is any issue.
For the php function urlencode encoding a NSString to a cString with UTF8Encode, like [NSString UTF8String] was not working.
Here is my custom objective c NSString+ASCIIencode Category with works with all ASCII values 0..255
Header
#import <Cocoa/Cocoa.h>
#interface NSString (ASCIIEncode)
- (const char*)ASCIIEncode;
#end
Implementation
#import "NSString+ASCIIEncode.h"
#implementation NSString (ASCIIEncode)
- (const char*)ASCIIEncode {
static char output[1024];
// https://tools.piex.at/ascii-tabelle/
// https://www.ionos.de/digitalguide/server/knowhow/ascii-american-standard-code-for-information-interchange/
NSMutableArray *ascii = [NSMutableArray new];
// Hex
// 000 Dez Hex
[ascii addObject:#"\0"]; // 000 000 NUL
[ascii addObject:#( 1)]; // 001 001 SOH
[ascii addObject:#( 2)]; // 002 002 STX
[ascii addObject:#( 3)]; // 003 003 ETX
[ascii addObject:#( 4)]; // 004 004 EOT
[ascii addObject:#( 5)]; // 005 005 ENQ
[ascii addObject:#( 6)]; // 006 006 ACK
[ascii addObject:#"\a"]; // 007 007 BEL
[ascii addObject:#"\b"]; // 008 008 BS
[ascii addObject:#( 9)]; // 009 009 TAB
[ascii addObject:#"\n"]; // 010 00A LF
[ascii addObject:#(11)]; // 011 00B VT
[ascii addObject:#(12)]; // 012 00C FF
[ascii addObject:#"\r"]; // 013 00D CR
[ascii addObject:#(14)]; // 014 00E SO
[ascii addObject:#(15)]; // 015 00F NAK
// 010
[ascii addObject:#(16)]; // 016 010 DLE
[ascii addObject:#(17)]; // 017 011 DC1
[ascii addObject:#(18)]; // 018 012 DC2
[ascii addObject:#(19)]; // 019 013 DC3
[ascii addObject:#(20)]; // 020 014 DC4
[ascii addObject:#(21)]; // 021 015 NAK
[ascii addObject:#(22)]; // 022 016 SYN
[ascii addObject:#(23)]; // 023 017 ETB
[ascii addObject:#(24)]; // 024 018 CAN
[ascii addObject:#(25)]; // 025 019 EM
[ascii addObject:#(26)]; // 026 01A SUB
[ascii addObject:#(27)]; // 027 01B ESC
[ascii addObject:#(28)]; // 028 01C FS
[ascii addObject:#(29)]; // 029 01D GS
[ascii addObject:#(30)]; // 030 01E RS
[ascii addObject:#(31)]; // 031 01F US
// 020
[ascii addObject:#" "]; // 032 020 Space
[ascii addObject:#"!"]; // 033 021
[ascii addObject:#"\""]; // 034 022
[ascii addObject:#"#"]; // 035 023
[ascii addObject:#"$"]; // 036 024
[ascii addObject:#"%"]; // 037 025
[ascii addObject:#"&"]; // 038 026
[ascii addObject:#"'"]; // 039 027
[ascii addObject:#"("]; // 040 028
[ascii addObject:#")"]; // 041 029
[ascii addObject:#"*"]; // 042 02A
[ascii addObject:#"+"]; // 043 02B
[ascii addObject:#","]; // 044 02C
[ascii addObject:#"-"]; // 045 02D
[ascii addObject:#"."]; // 046 02E
[ascii addObject:#"/"]; // 047 02F
// 030
[ascii addObject:#"0"]; // 048 030
[ascii addObject:#"1"]; // 049 031
[ascii addObject:#"2"]; // 050 032
[ascii addObject:#"3"]; // 051 033
[ascii addObject:#"4"]; // 052 034
[ascii addObject:#"5"]; // 053 035
[ascii addObject:#"6"]; // 054 036
[ascii addObject:#"7"]; // 055 037
[ascii addObject:#"8"]; // 056 038
[ascii addObject:#"9"]; // 057 039
[ascii addObject:#":"]; // 058 03A
[ascii addObject:#";"]; // 059 03B
[ascii addObject:#"<"]; // 060 03C
[ascii addObject:#"="]; // 061 03D
[ascii addObject:#">"]; // 062 03E
[ascii addObject:#"?"]; // 063 03F
// 040
[ascii addObject:#"#"]; // 064 040
[ascii addObject:#"A"]; // 065 041
[ascii addObject:#"B"]; // 066 042
[ascii addObject:#"C"]; // 067 043
[ascii addObject:#"D"]; // 068 044
[ascii addObject:#"E"]; // 069 045
[ascii addObject:#"F"]; // 070 046
[ascii addObject:#"G"]; // 071 047
[ascii addObject:#"H"]; // 072 048
[ascii addObject:#"I"]; // 073 049
[ascii addObject:#"J"]; // 074 04A
[ascii addObject:#"K"]; // 075 04B
[ascii addObject:#"L"]; // 076 04C
[ascii addObject:#"M"]; // 077 04D
[ascii addObject:#"N"]; // 078 04E
[ascii addObject:#"O"]; // 079 04F
// 050
[ascii addObject:#"P"]; // 080 050
[ascii addObject:#"Q"]; // 081 051
[ascii addObject:#"R"]; // 082 052
[ascii addObject:#"S"]; // 083 053
[ascii addObject:#"T"]; // 084 054
[ascii addObject:#"U"]; // 085 055
[ascii addObject:#"V"]; // 086 056
[ascii addObject:#"W"]; // 087 057
[ascii addObject:#"X"]; // 088 058
[ascii addObject:#"Y"]; // 089 059
[ascii addObject:#"Z"]; // 090 05A
[ascii addObject:#"["]; // 091 05B
[ascii addObject:#"\\"]; // 092 05C
[ascii addObject:#"]"]; // 093 05D
[ascii addObject:#"^"]; // 094 05E
[ascii addObject:#"_"]; // 095 05F
// 060
[ascii addObject:#"`"]; // 096 060
[ascii addObject:#"a"]; // 097 061
[ascii addObject:#"b"]; // 098 062
[ascii addObject:#"c"]; // 099 063
[ascii addObject:#"d"]; // 100 064
[ascii addObject:#"e"]; // 101 065
[ascii addObject:#"f"]; // 102 066
[ascii addObject:#"g"]; // 103 067
[ascii addObject:#"h"]; // 104 068
[ascii addObject:#"i"]; // 105 069
[ascii addObject:#"j"]; // 106 06A
[ascii addObject:#"k"]; // 107 06B
[ascii addObject:#"l"]; // 108 06C
[ascii addObject:#"m"]; // 109 06D
[ascii addObject:#"n"]; // 110 06E
[ascii addObject:#"o"]; // 111 06F
// 070
[ascii addObject:#"p"]; // 112 070
[ascii addObject:#"q"]; // 113 071
[ascii addObject:#"r"]; // 114 072
[ascii addObject:#"s"]; // 115 073
[ascii addObject:#"t"]; // 116 074
[ascii addObject:#"u"]; // 117 075
[ascii addObject:#"v"]; // 118 076
[ascii addObject:#"w"]; // 119 077
[ascii addObject:#"x"]; // 120 078
[ascii addObject:#"y"]; // 121 079
[ascii addObject:#"z"]; // 122 07A
[ascii addObject:#"{"]; // 123 07B
[ascii addObject:#"|"]; // 124 07C
[ascii addObject:#"}"]; // 125 07D
[ascii addObject:#"~"]; // 126 07E
[ascii addObject:#(127)];// 127 07F DEL
// 080
[ascii addObject:#"€"]; // 128 080
[ascii addObject:#(129)];// 129 081
[ascii addObject:#"‚"]; // 130 082
[ascii addObject:#"ƒ"]; // 131 083
[ascii addObject:#"„"]; // 132 084
[ascii addObject:#"…"]; // 133 085
[ascii addObject:#"†"]; // 134 086
[ascii addObject:#"‡"]; // 135 087
[ascii addObject:#"ˆ"]; // 136 088
[ascii addObject:#"‰"]; // 137 089
[ascii addObject:#"Š"]; // 138 08A
[ascii addObject:#"‹"]; // 139 08B
[ascii addObject:#"Œ"]; // 140 08C
[ascii addObject:#(141)];// 141 08D
[ascii addObject:#"Ž"]; // 142 08E
[ascii addObject:#(143)]; // 143 08F
// 090
[ascii addObject:#(144)];// 144 090
[ascii addObject:#"‘"]; // 145 091
[ascii addObject:#"’"]; // 146 092
[ascii addObject:#"“"]; // 147 093
[ascii addObject:#"”"]; // 148 094
[ascii addObject:#"•"]; // 149 095
[ascii addObject:#"–"]; // 150 096
[ascii addObject:#"—"]; // 151 097
[ascii addObject:#"˜"]; // 152 098
[ascii addObject:#"™"]; // 153 099
[ascii addObject:#"š"]; // 154 09A
[ascii addObject:#"›"]; // 155 09B
[ascii addObject:#"œ"]; // 156 09C
[ascii addObject:#(157)];// 157 09D
[ascii addObject:#"ž"]; // 158 09E
[ascii addObject:#"Ÿ"]; // 159 09F
// 0A0
[ascii addObject:#(160)];// 160 0A0
[ascii addObject:#"¡"]; // 161 0A1
[ascii addObject:#"¢"]; // 162 0A2
[ascii addObject:#"£"]; // 163 0A3
[ascii addObject:#"¤"]; // 164 0A4
[ascii addObject:#"¥"]; // 165 0A5
[ascii addObject:#"¦"]; // 166 0A6
[ascii addObject:#"§"]; // 167 0A7
[ascii addObject:#"¨"]; // 168 0A8
[ascii addObject:#"©"]; // 169 0A9
[ascii addObject:#"ª"]; // 170 0AA
[ascii addObject:#"«"]; // 171 0AB
[ascii addObject:#"¬"]; // 172 0AC
[ascii addObject:#(173)];// 173 0AD
[ascii addObject:#"®"]; // 174 0AE
[ascii addObject:#"¯"]; // 175 0AF
// 0B0
[ascii addObject:#"°"]; // 176 0B0
[ascii addObject:#"±"]; // 177 0B1
[ascii addObject:#"²"]; // 178 0B2
[ascii addObject:#"³"]; // 179 0B3
[ascii addObject:#"´"]; // 180 0B4
[ascii addObject:#"µ"]; // 181 0B5
[ascii addObject:#"¶"]; // 182 0B6
[ascii addObject:#"·"]; // 183 0B7
[ascii addObject:#"¸"]; // 184 0B8
[ascii addObject:#"¹"]; // 185 0B9
[ascii addObject:#"º"]; // 186 0BA
[ascii addObject:#"»"]; // 187 0BB
[ascii addObject:#"¼"]; // 188 0BC
[ascii addObject:#"½"]; // 189 0BD
[ascii addObject:#"¾"]; // 190 0BE
[ascii addObject:#"¿"]; // 191 0BF
// 0C0
[ascii addObject:#"À"]; // 192 0C0
[ascii addObject:#"Á"]; // 193 0C1
[ascii addObject:#"Â"]; // 194 0C2
[ascii addObject:#"Ã"]; // 195 0C3
[ascii addObject:#"Ä"]; // 196 0C4
[ascii addObject:#"Å"]; // 197 0C5
[ascii addObject:#"Æ"]; // 198 0C6
[ascii addObject:#"Ç"]; // 199 0C7
[ascii addObject:#"È"]; // 200 0C8
[ascii addObject:#"É"]; // 201 0C9
[ascii addObject:#"Ê"]; // 202 0CA
[ascii addObject:#"Ë"]; // 203 0CB
[ascii addObject:#"Ì"]; // 204 0CC
[ascii addObject:#"Í"]; // 205 0CD
[ascii addObject:#"Î"]; // 206 0CE
[ascii addObject:#"Ï"]; // 207 0CF
// 0D0
[ascii addObject:#"Ð"]; // 208 0D0
[ascii addObject:#"Ñ"]; // 209 0D1
[ascii addObject:#"Ò"]; // 210 0D2
[ascii addObject:#"Ó"]; // 211 0D3
[ascii addObject:#"Ô"]; // 212 0D4
[ascii addObject:#"Õ"]; // 213 0D5
[ascii addObject:#"Ö"]; // 214 0D6
[ascii addObject:#"×"]; // 215 0D7
[ascii addObject:#"Ø"]; // 216 0D8
[ascii addObject:#"Ù"]; // 217 0D9
[ascii addObject:#"Ú"]; // 218 0DA
[ascii addObject:#"Û"]; // 219 0DB
[ascii addObject:#"Ü"]; // 220 0DC
[ascii addObject:#"Ý"]; // 221 0DD
[ascii addObject:#"Þ"]; // 222 0DE
[ascii addObject:#"ß"]; // 223 0DF
// 0E0
[ascii addObject:#"à"]; // 224 0E0
[ascii addObject:#"á"]; // 225 0E1
[ascii addObject:#"â"]; // 226 0E2
[ascii addObject:#"ã"]; // 227 0E3
[ascii addObject:#"ä"]; // 228 0E4
[ascii addObject:#"å"]; // 229 0E5
[ascii addObject:#"æ"]; // 230 0E6
[ascii addObject:#"ç"]; // 231 0E7
[ascii addObject:#"è"]; // 232 0E8
[ascii addObject:#"é"]; // 233 0E9
[ascii addObject:#"ê"]; // 234 0EA
[ascii addObject:#"ë"]; // 235 0EB
[ascii addObject:#"ì"]; // 236 0EC
[ascii addObject:#"í"]; // 237 0ED
[ascii addObject:#"î"]; // 238 0EE
[ascii addObject:#"ï"]; // 239 0EF
// 0F0
[ascii addObject:#"ð"]; // 240 0F0
[ascii addObject:#"ñ"]; // 241 0F1
[ascii addObject:#"ò"]; // 242 0F2
[ascii addObject:#"ó"]; // 243 0F3
[ascii addObject:#"ô"]; // 244 0F4
[ascii addObject:#"õ"]; // 245 0F5
[ascii addObject:#"ö"]; // 246 0F6
[ascii addObject:#"÷"]; // 247 0F7
[ascii addObject:#"ø"]; // 248 0F8
[ascii addObject:#"ù"]; // 249 0F9
[ascii addObject:#"ú"]; // 250 0FA
[ascii addObject:#"û"]; // 251 0FB
[ascii addObject:#"ü"]; // 252 0FC
[ascii addObject:#"ý"]; // 253 0FD
[ascii addObject:#"þ"]; // 254 0FE
[ascii addObject:#"ÿ"]; // 255 0FF
NSInteger i;
for (i=0; i < self.length; i++) {
NSRange range;
range.location = i;
range.length = 1;
NSString *charString = [self substringWithRange:range];
for (NSInteger asciiIdx=0; asciiIdx < ascii.count; asciiIdx++) {
if ([charString isEqualToString:ascii[asciiIdx]]) {
unsigned char c = (unsigned char)asciiIdx;
output[i] = c;
break;
}
}
}
// Don't forget string termination
output[i] = 0;
return (const char*)&output[0];
}
#end
This is what I did using Swift 5:
func formatPassword() -> String {
var output = "";
for ch in self {
let char = String(ch)
switch ch {
case " ":
output.append("+")
break
case ".", "-", "_", "~", "a"..."z", "A"..."Z", "0"..."9":
output.append(char)
break
default:
print(ch)
let unicode = char.unicodeScalars.first?.value ?? 0
let unicodeValue = NSNumber(value: unicode).intValue
let hexValue = String(format: "%02X", arguments: [unicodeValue])
output = output.appendingFormat("%%%#", hexValue)
}
}
return output as String
}
Then I called this function where I defined my password.

Resources