Azure DevOps Server / Team Foundation Server でリポジトリの複製で "SSL certificate problem: unable to get local issuer certificate" が発生しリポジトリにアクセスできない

DevOps Server / Team Foundation Server でリポジトリの複製で "SSL certificate problem: unable to get local issuer certificate" が発生しリポジトリにアクセスできない現象について紹介します。

現象

Team Foundation Server のGitリポジトリを複製すると、下記のエラーが表示され接続できません。
エラーメッセージ
Git failed with a fatal error. unable to access '(TFSのGitリポジトリのURL)': SSL cetificate problem: unable to get local issuer certificate


原因

Visual Studioから利用しているGitは利用しているPCのWindowsにインストールされている証明書は利用せず、Git独自の証明書を利用しています。そのため、独自の認証局で署名された証明書や自己証明書をDevOps Serverの証明書に利用し、接続するPCにCAの証明書をインストールしても、接続先のDevOps Serverのサーバーの証明書が信頼されている証明書と判断できないため、署名の確認に失敗し、接続ができません。
Visual Studioから呼び出されるGitは下記のフォルダのGitです。

Visual Studio 2022 Professionalの場合

C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\cmd\Git.exe

Visual Studio 2019 Professionalの場合

C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\cmd\Git.exe

Visual Studio 2017 Professionalの場合

C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\cmd\Git.exe

Gitが利用する証明書は下記のフォルダのバンドル証明書が利用されます。

Visual Studio 2022 Professionalの場合

C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw64\ssl\certs

Visual Studio 2019 Professionalの場合

C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs

Visual Studio 2017 Professionalの場合

C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs


以下の対策があります。

対策1:ルート認証局が信頼されている証明書を利用する

SymantecやLet's Encrypt などのメジャーな認証局によって署名されたSSL証明書を利用します。 メジャーな認証局であれば、Gitのバンドルされた証明書でも信頼されている証明書と判断でき接続ができます。
イントラネットのサーバーでは、Let's Encryptの証明書を取得しづらい問題がありましたが、DNS認証やワイルドカード証明書の取得がサポートされたため、 Let's Encrypt の証明書をDevOps Server に導入することも比較的簡単になりました。
Let's Encrypt のワイルドカード証明書の取得はこちらの記事を、 オフライン環境用にpfxファイルを取得する方法はこちらの記事を参照してください。

対策2:Gitの設定を変更しWindowsにインポートされた証明書を利用する動作にする

Gitの設定を変更してWindowsにインポートされた証明書を利用する動作に変更できます。変更するには下記のコマンドを実行します。
git.exe config --system http.sslbackend schannel
今回の例では下記のコマンドとなります。
'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions \Microsoft\TeamFoundation\Team Explorer\Git\cmd\git.exe' config --system http.sslbackend schannel
なお、元に戻す場合は下記のコマンドとなります。
git.exe config --system http.sslbackend openssl

設定変更により動作するはずですが、実際に試してみたところ、下記のエラーが発生しVisual StudioからGitのリポジトリにはアクセスできませんでした。
fatal: unable to access '(GitリポジトリのURL)': schannel: next InitializeSecurityContext failed: >Unknown error (0x80092012) - �����̊֐��͏ؖ����̎�����m�F�ł��܂���ł����B
または
fatal: unable to access '(GitリポジトリのURL)': schannel: next InitializeSecurityContext failed: Unknown error (0x80092012) - The revocation function was unable to check revocation for the certificate.

対策3:組織の認証局(CA)の証明書を指定する

内部のサーバーに費用をかけて証明書を取得したくない場合や、Let's Encrypt を利用して3か月ごとに証明書を更新する運用をしたくない場合、組織内のCAで署名された証明書を利用したいことがあります。この場合は、組織内のCAの証明書を指定することで上記の問題を解決できます。

認証局(CA)の証明書を取得し、クライアントマシンに配置します。証明書は Base 64でエンコードされた形式の必要があります。Base 64でエンコードされたCAの証明書の取得方法はこちらの記事を参照してください。
証明書を配置するパスはどこでも問題ありませんが、他の証明書と同じ位置にしておいたほうが良いため、下記のフォルダに配置します。

Visual Studio 2022 の場合

C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw64\ssl\certs

Visual Studio 2019 の場合

C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs

Visual Studio 2017 の場合

C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs


PowerShellを管理者権限で起動し、下記のコマンドを実行します。
コマンド書式
& git.exe config --global http.sslcainfo (CAの証明書ファイル)

今回の例では下記のコマンドとなります。

Visual Studio 2019 の場合

コマンド
& 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\cmd\git.exe' config --global http.sslcainfo 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs\iPentec-CA.cer'

Visual Studio 2017 の場合

コマンド
& 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\cmd\git.exe' config --global http.sslcainfo 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs\iPentec-CA.cer'


コマンドが実行できるとエラーなどは表示されず、次のコマンドの入力待ちになります。


または、サイトを指定する下記コマンドも利用可能です。(未検証)
サイトを指定するコマンド書式
git config --global http.(サイトURL).sslcainfo (CAの証明書ファイル)
サイトを指定するコマンド例
& 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions \Microsoft\TeamFoundation\Team Explorer\Git\cmd\git.exe' config --global http.https://kobako/.sslcainfo 'C:\Program Files (x86)\Microsof t Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\ce rts\iPentec-CA.cer'


Visual Studioでリポジトリの複製を実行します。エラーは発生せず、リポジトリの複製が実行できます。


対策4:ca-bundle.crt を更新する

先の方法で設定した証明書のリポジトリにはアクセスできるようになりますが、参照する証明書を変更したため、GitHubなどと併用する場合や、独自のCA証明書を持った他のリポジトリへのアクセスはできなくなってしまいます。複数の証明書を利用する場合は、ca-bundle.crtファイルを更新する方法が良いです。

C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs\ ディレクトリを開きます。ca-bundle.crt ファイルが見つかります。


ca-bundle.crt を編集するため、いったんデスクトップなどの作業フォルダにファイルをコピーします。(Program File ディレクトリ配下のディレクトリでは直接編集できない場合があります。)


テキストエディタで"ca-bundle.crt"ファイルを開きます。


下にスクロールすると -----END CERTIFICATE-----で区切りがあることがわかります。複数の証明書が"ca-bundle.crt"ファイル内に記述されていることがわかります。


追加したいCA証明書をテキストファイルで開きます。CA証明書はBASE64形式で出力しておく必要があります。BASE64形式でCAの証明書を出力する手順はこちらの記事を参照してください。


CAの証明書のテキストを"ca-bundle.crt"の末尾にペーストして追記します。複数の証明書を追加する場合は、続けて末尾に証明書のテキストを追加します。


ca-bundle.crtファイルを保存し、元のディレクトリC:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs\に上書きします。

他の証明書を参照している設定になっている場合は、下記のコマンドを実行して参照する証明書をca-bundle.crtに変更します。
コマンド
& 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\cmd\git.exe' config --global http.sslcainfo 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs\ca-bundle.cer'

設定は以上です。Gitでリポジトリにアクセスできるかを確認します。

注意
Visual Studioのアップデートにより、ca-bundle.crt が更新される場合があります。Visual Studioアップデート後にリポジトリにアクセスできなくなってしまった場合はca-bundle.crt に再度、自組織のCA証明書を追記する必要があります。

対策5:ドメインを指定して組織の認証局(CA)の証明書を指定する

先のca-bundle.crtを更新する方法で問題なく動作しますが、Visual Studioのアップデートのたびにca-bundle.crtが更新されてしまうため、アップデートのたびにca-bundle.crtを更新する必要があります。
組織の証明書を別ファイルにして接続先のドメインごとにCAの証明書を切り替える方法を紹介します。(先の対策3のドメイン指定と同じものです。)

はじめに自組織のCA証明書を配置します。ca-bundle.crt ファイルのあるディレクトリに一緒に配置しておくのが良いかと思われます。
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\ssl\certs\

次に、C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\mingw32\etc\ ディレクトリの gitconfigファイルを編集します。
補足
Visual Studioのアップデートにより、C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Git\etc に移動している場合があります。
下記のhttpセクションの手前に
[http]
	sslCAinfo = /ssl/certs/ca-bundle.crt

以下の記述を追記します。
[http "接続先URL"]
	sslCAinfo = (上記の接続先URLで利用する証明書ファイル)
編集前 gitconfig
[core]
	symlinks = false
	autocrlf = true
[color]
	diff = auto
	status = auto
	branch = auto
	interactive = true
[pack]
	packSizeLimit = 2g
[help]
	format = html
[http]
	sslCAinfo = /ssl/certs/ca-bundle.crt
[diff "astextplain"]
	textconv = astextplain
[rebase]
	autosquash = true
[filter "lfs"]
	clean = git-lfs clean -- %f
	smudge = git-lfs smudge -- %f
	process = git-lfs filter-process
	required = true
編集後 gitconfig
[core]
	symlinks = false
	autocrlf = true
[color]
	diff = auto
	status = auto
	branch = auto
	interactive = true
[pack]
	packSizeLimit = 2g
[help]
	format = html
[http "(リポジトリのURL)"]
	sslCAinfo = /ssl/certs/iPentec-CA.cer
[http]
	sslCAinfo = /ssl/certs/ca-bundle.crt
[diff "astextplain"]
	textconv = astextplain
[rebase]
	autosquash = true
[filter "lfs"]
	clean = git-lfs clean -- %f
	smudge = git-lfs smudge -- %f
	process = git-lfs filter-process
	required = true

対策6:ドメインを指定して組織の認証局(CA)の証明書を指定する (localのgitのconfigファイルに記述)

対策5でも問題なく利用できますが、Visual Studioのアップデートでgitconfigが移動してしまうなどの問題があります。また、特定のリポジトリのみの証明書のためグローバル設定に入れないほうが良いといった理由もあります。対策5の記述をグローバルのgitconfigではなく、リポジトリのローカルのconfigに記述する方法です。
(リポジトリのディレクトリ)\.git\config にそのリポポジトリの設定が記載されていますので、その中に下記のセクションを記述します。
[http "接続先URL"]
	sslCAinfo = (上記の接続先URLで利用する証明書ファイル)

編集前 (リポジトリのディレクトリ)\.git\config
[core]
	repositoryformatversion = 0
	filemode = false
	bare = false
	logallrefupdates = true
	symlinks = false
	ignorecase = true
[submodule]
	active = .
[remote "origin"]
	url = (リポジトリのURL)
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master
編集後 (リポジトリのディレクトリ)\.git\config
[core]
	repositoryformatversion = 0
	filemode = false
	bare = false
	logallrefupdates = true
	symlinks = false
	ignorecase = true
[http "(リポジトリのURL)"]
	sslCAinfo = /ssl/certs/iPentec-CA-ACA.cer
[submodule]
	active = .
[remote "origin"]
	url = (リポジトリのURL)
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/master
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2018-09-23
iPentec all rights reserverd.