Skip to content

Commit 0844462

Browse files
committed
テスト中のコード
1 parent 6e8e414 commit 0844462

File tree

1 file changed

+133
-3
lines changed

1 file changed

+133
-3
lines changed

Sora/URLSessionWebSocketChannel.swift

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ class URLSessionWebSocketChannel: NSObject, URLSessionDelegate, URLSessionTaskDe
229229
) {
230230
// コードを短くするために変数を定義
231231
let ps = challenge.protectionSpace
232+
let authMethod = ps.authenticationMethod
232233
let previousFailureCount = challenge.previousFailureCount
233234

234235
// 既に失敗している場合はチャレンジを中止する
@@ -250,12 +251,141 @@ class URLSessionWebSocketChannel: NSObject, URLSessionDelegate, URLSessionTaskDe
250251
"[\(host)] \(#function): challenge=\(ps.host):\(ps.port), \(ps.authenticationMethod) previousFailureCount: \(previousFailureCount)"
251252
)
252253

253-
// Basic 認証のみに対応している
254-
// それ以外の認証方法は .performDefaultHandling で処理を続ける
255-
guard ps.authenticationMethod == NSURLAuthenticationMethodHTTPBasic else {
254+
// 認証方式によって処理を分岐
255+
switch authMethod {
256+
case NSURLAuthenticationMethodServerTrust:
257+
// サーバー証明書検証
258+
handleServerTrustChallenge(challenge, completionHandler: completionHandler)
259+
case NSURLAuthenticationMethodHTTPBasic:
260+
// basic 認証
261+
handleBasicAuthenticationChallenge(challenge, completionHandler: completionHandler)
262+
263+
default:
264+
Logger.debug(
265+
type: .webSocketChannel,
266+
message:
267+
"[\(host)] \(#function): Unsupported or unhandled authentication method (\(authMethod)), performing default handling."
268+
)
269+
completionHandler(.performDefaultHandling, nil)
270+
}
271+
}
272+
273+
private func loadCustomCertificate() -> SecCertificate? {
274+
// 文字列として ca cert を定義する
275+
let caCertString = """
276+
"""
277+
print(caCertString)
278+
// PEM 文字列からヘッダー/フッターを除去し、Base64 部分を結合
279+
let base64String =
280+
caCertString
281+
.split(separator: "\n") // 行ごとに分割
282+
.filter { !$0.hasPrefix("-----") } // ヘッダー/フッター行を除外
283+
.joined() // 残りの行 (Base64) を結合
284+
285+
// Base64 文字列を Data にデコード
286+
// options: .ignoreUnknownCharacters を指定すると、改行などが残っていても無視してくれる
287+
guard let certificateData = Data(base64Encoded: base64String, options: .ignoreUnknownCharacters)
288+
else {
289+
Logger.error(
290+
type: .webSocketChannel,
291+
message: "[\(host)] Failed to decode Base64 certificate string from hardcoded PEM.")
292+
return nil
293+
}
294+
return SecCertificateCreateWithData(nil, certificateData as CFData)
295+
}
296+
297+
private func handleServerTrustChallenge(
298+
_ challenge: URLAuthenticationChallenge,
299+
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
300+
) {
301+
Logger.debug(type: .webSocketChannel, message: "handleServerTrustChallenge")
302+
guard let serverTrust = challenge.protectionSpace.serverTrust else {
303+
Logger.debug(type: .webSocketChannel, message: "handleServerTrustChallenge: no serverTrust")
304+
completionHandler(.performDefaultHandling, nil)
305+
return
306+
}
307+
guard let customCACert = loadCustomCertificate() else {
308+
Logger.debug(type: .webSocketChannel, message: "handleServerTrustChallenge: no customCACert")
256309
completionHandler(.performDefaultHandling, nil)
257310
return
258311
}
312+
// SecTrust オブジェクトにカスタム CA を信頼アンカーとして設定
313+
let policy = SecPolicyCreateBasicX509() // 基本的な X.509 ポリシーを使用
314+
var ossStatus = SecTrustSetPolicies(serverTrust, policy)
315+
guard ossStatus == errSecSuccess else {
316+
Logger.warn(type: .webSocketChannel, message: "SecTrustSetPolicies failed")
317+
completionHandler(.cancelAuthenticationChallenge, nil)
318+
return
319+
}
320+
// カスタム CA を信頼アンカーとして追加
321+
// ここで追加されたアンカー以外の証明書は無視される(システムの証明書も無視される
322+
// https://developer.apple.com/documentation/security/sectrustsetanchorcertificatesonly(_:_:)
323+
let anchorCertificates = [customCACert]
324+
ossStatus = SecTrustSetAnchorCertificates(serverTrust, anchorCertificates as CFArray)
325+
if ossStatus != errSecSuccess {
326+
Logger.warn(type: .webSocketChannel, message: "SecTrustSetAnchorCertificates failed: \(ossStatus)")
327+
completionHandler(.cancelAuthenticationChallenge, nil)
328+
return
329+
}
330+
331+
// システムの信頼ストアではなく、提供されたアンカーのみを使用するように強制
332+
// これにより、カスタムCAによって署名されていること *のみ* を検証できる
333+
let setOnlyStatus = SecTrustSetAnchorCertificatesOnly(serverTrust, true)
334+
guard setOnlyStatus == errSecSuccess else {
335+
// ログ出力はするが、致命的エラーとはしない場合もある
336+
Logger.warn(type: .webSocketChannel, message: "Warning: Could not set anchor certificates only: \(setOnlyStatus)")
337+
completionHandler(.cancelAuthenticationChallenge, nil)
338+
return
339+
}
340+
341+
// ホスト名の検証ポリシーを追加(デフォルトで含まれている場合もあるが明示的に行うのが安全)
342+
// 注意: wss:// スキームの場合、ポリシー作成時に "wss" ではなく "https" を使うことが多い
343+
// または、ホスト名検証をSecTrustEvaluateの後で別途行う方法もある
344+
//Logger.debug(type: .webSocketChannel, message: "\(host) を HTTPS サーバー名として検証")
345+
//let sslpolicy = SecPolicyCreateSSL(true, host as CFString)
346+
//let sslpolicyStatus = SecTrustSetPolicies(serverTrust, sslpolicy) // 必要に応じて
347+
//guard sslpolicyStatus == errSecSuccess else {
348+
// Logger.warn(type: .webSocketChannel, message: "SecTrustSetPolicies failed: \(sslpolicyStatus)")
349+
// completionHandler(.cancelAuthenticationChallenge, nil)
350+
// return
351+
//}
352+
353+
// サーバー証明書を評価
354+
var error: CFError?
355+
let trustResult = SecTrustEvaluateWithError(serverTrust, &error)
356+
357+
if trustResult {
358+
// 信頼できると評価された場合はサーバーの証明書を使って認証情報を作成する
359+
Logger.debug(type: .webSocketChannel, message: "Server trust evaluation succeeded")
360+
completionHandler(.useCredential, URLCredential(trust: serverTrust))
361+
} else {
362+
Logger.warn(
363+
type: .webSocketChannel,
364+
message: "Server trust evaluation failed: \(error?.localizedDescription ?? "Unknown error")"
365+
)
366+
completionHandler(.cancelAuthenticationChallenge, nil)
367+
}
368+
369+
}
370+
371+
// --- Helper: Proxy Authentication ---
372+
private func handleBasicAuthenticationChallenge(
373+
_ challenge: URLAuthenticationChallenge,
374+
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
375+
) {
376+
// 既存の Proxy Basic 認証ロジックを流用
377+
let ps = challenge.protectionSpace
378+
let previousFailureCount = challenge.previousFailureCount
379+
380+
// 既に失敗している場合はチャレンジを中止する
381+
guard previousFailureCount == 0 else {
382+
let message =
383+
"[\(host)] \(#function): Proxy authentication failed (previous failure count: \(previousFailureCount)). Proxy => \(String(describing: proxy))"
384+
Logger.info(type: .webSocketChannel, message: message)
385+
completionHandler(.cancelAuthenticationChallenge, nil)
386+
disconnect(error: SoraError.signalingChannelError(reason: message)) // disconnect は適切か要確認
387+
return
388+
}
259389

260390
// username と password をチェック
261391
guard let username = proxy?.username, let password = proxy?.password else {

0 commit comments

Comments
 (0)