@@ -538,6 +538,7 @@ private class InnerWebSocket: Hashable {
538
538
var _event = WebSocketEvents ( )
539
539
var _eventDelegate : WebSocketDelegate ?
540
540
var _binaryType = WebSocketBinaryType . uInt8Array
541
+ var _pinnedPublicKeys : [ SecKey ] ? = nil
541
542
var _readyState = WebSocketReadyState . connecting
542
543
var _networkTimeout = TimeInterval ( - 1 )
543
544
@@ -587,6 +588,10 @@ private class InnerWebSocket: Hashable {
587
588
get { lock ( ) ; defer { unlock ( ) } ; return _readyState }
588
589
set { lock ( ) ; defer { unlock ( ) } ; _readyState = newValue }
589
590
}
591
+ var pinnedPublicKeys : [ SecKey ] ? {
592
+ get { lock ( ) ; defer { unlock ( ) } ; return _pinnedPublicKeys }
593
+ set { lock ( ) ; defer { unlock ( ) } ; _pinnedPublicKeys = newValue }
594
+ }
590
595
591
596
func copyOpen( _ request: URLRequest , subProtocols : [ String ] = [ ] ) -> InnerWebSocket {
592
597
let ws = InnerWebSocket ( request: request, subProtocols: subProtocols, stub: false )
@@ -597,6 +602,7 @@ private class InnerWebSocket: Hashable {
597
602
ws. event = event
598
603
ws. eventQueue = eventQueue
599
604
ws. binaryType = binaryType
605
+ ws. pinnedPublicKeys = pinnedPublicKeys
600
606
return ws
601
607
}
602
608
@@ -699,6 +705,7 @@ private class InnerWebSocket: Hashable {
699
705
stage = . readResponse
700
706
case . readResponse:
701
707
try readResponse ( )
708
+ try verifySSLPinning ( )
702
709
privateReadyState = . open
703
710
fire {
704
711
self . event. open ( )
@@ -1192,6 +1199,42 @@ private class InnerWebSocket: Hashable {
1192
1199
inputBytesStart += bufferCount+ 4
1193
1200
}
1194
1201
}
1202
+
1203
+ private func verifySSLPinning( ) throws {
1204
+ let keys = self . _pinnedPublicKeys ?? [ ]
1205
+ if keys. isEmpty { return }
1206
+
1207
+ let peerTrust = wr. property ( forKey: kCFStreamPropertySSLPeerTrust as Stream . PropertyKey ) as! SecTrust
1208
+
1209
+ let serverPubKeys = publicKeys ( from: peerTrust)
1210
+ for key in keys as [ AnyObject ] {
1211
+ for serverKey in serverPubKeys as [ AnyObject ] {
1212
+ if key. isEqual ( serverKey) {
1213
+ return
1214
+ }
1215
+ }
1216
+ }
1217
+
1218
+ throw WebSocketError . invalidResponse ( " Failed SSL public key pinning verification " )
1219
+ }
1220
+
1221
+ private func publicKeys( from trust: SecTrust ) -> [ SecKey ] {
1222
+ let policy = SecPolicyCreateBasicX509 ( )
1223
+ let keys = ( 0 ..< SecTrustGetCertificateCount ( trust) ) . flatMap { ( index: Int ) -> SecKey ? in
1224
+ let cert = SecTrustGetCertificateAtIndex ( trust, index)
1225
+ return extractPublicKey ( cert!, policy: policy)
1226
+ }
1227
+
1228
+ return keys
1229
+ }
1230
+
1231
+ private func extractPublicKey( _ cert: SecCertificate , policy: SecPolicy ) -> SecKey ? {
1232
+ var possibleTrust : SecTrust ? = nil
1233
+ SecTrustCreateWithCertificates ( cert, policy, & possibleTrust)
1234
+
1235
+ guard let trust = possibleTrust else { return nil }
1236
+ return SecTrustCopyPublicKey ( trust)
1237
+ }
1195
1238
1196
1239
class ByteReader {
1197
1240
var start : UnsafePointer < UInt8 >
@@ -1722,6 +1765,13 @@ open class WebSocket: NSObject {
1722
1765
get { return ws. binaryType }
1723
1766
set { ws. binaryType = newValue }
1724
1767
}
1768
+
1769
+ /// A collection of public keys to pin the SSL connection against. If no keys are provided, pinning is not enforced.
1770
+ @nonobjc open var pinnedPublicKeys : [ SecKey ] ? {
1771
+ get { return ws. pinnedPublicKeys }
1772
+ set { ws. pinnedPublicKeys = newValue }
1773
+ }
1774
+
1725
1775
/// The current state of the connection; this is one of the WebSocketReadyState constants. Read only.
1726
1776
open var readyState : WebSocketReadyState {
1727
1777
return ws. readyState
0 commit comments