自己署名証明書を取り込む(Android Studio Project編)

投稿日:  更新日:

通常、UIコンポーネントのWebViewは自己署名証明書を「信頼できない証明書」と判断します。その結果、httpsによる接続は拒否されます。

しかし、自己署名証明書を「信頼できる証明書」として、Android StudioのProjectへ意図的に取り込むことで、httpsによる接続が可能になります。

ただし、自己署名証明書が信頼できない点は変わりません。

ですので、この方法を用いるのはDebug時に止め、Release時は正式なCA証明書を持つWebサーバーへ、運用を移行するようにして下さい。

※環境:Android Studio Otter | 2025.2.1 Patch 1

スポンサーリンク

想定する開発環境

この記事で想定する開発環境は図のようなものです。

「自己署名証明書を取り込む」で想定する開発環境

ローカルWebサーバの自己署名証明書を取り込んで、httpsによる接続を行います。

※参考記事
Android Emulatorの仮想ネットワーク
ローカルWebサーバーの構築(XAMPP編)
ローカルWebサーバーのhttps対応(XAMPP編)
Android Emulatorの/etc/hosts書き換え(API≦28)
Android Emulatorの/etc/hosts書き換え(API>28)

スポンサーリンク

WebViewの動作

自己署名証明書を取り込んでいない状態で、httpsによる接続を行った場合の動作を見てみましょう。

接続を拒否

自己署名証明書を持つmysite.example.comに対して、httpsによる接続を試みる例です。

                        val _uriString = "https://mysite.example.com"
                        AndroidView(
                            factory = ::WebView,
                            update = { webView ->
                                WebView.setWebContentsDebuggingEnabled(true)
                                webView.settings.javaScriptEnabled = true
                                webView.webViewClient = WebViewClient()
                                webView.loadUrl(_uriString)
                            },
                            modifier = Modifier
                                .background(Color.Gray)
                                .padding(10.dp).fillMaxWidth().height(300.dp)
                        )

※Compose UIにWebViewに相当するコンポーネントがないため、AndroidViewによりラップする形でWebViewを組み込んでいる。

WebViewは自己署名証明書を「信頼できない証明書」と判断します。

エラーとなり、接続は拒否されます。

[ERROR:ssl_client_socket_impl.cc(992)] handshake failed; returned -1, SSL error code 1, net_error -202

エラーの場合、WebViewの表示を行う前に接続がキャンセルされるので、何も表示されません。

WebViewデフォルトでhttpsの接続を拒否

スポンサーリンク

拒否の理由

接続のキャンセルはWebViewClient内で行われています。

WebViewClient#onReceivedSslErrorは、SSLのエラー時に呼ばれるコールバックです。

public class WebViewClient {
    ...
    public void onReceivedSslError(WebView view, SslErrorHandler handler,
            SslError error) {
        handler.cancel();
    }
	...
}

引数errorに「拒否の理由」が格納されています。以下のように継承して取り出します。

                            update = { webView ->
                                WebView.setWebContentsDebuggingEnabled(true)
                                webView.settings.javaScriptEnabled = true
                                webView.webViewClient = object : WebViewClient() {
                                    override fun onReceivedSslError(
                                        view: WebView?,
                                        handler: SslErrorHandler?,
                                        error: SslError?
                                    ) {
                                        Log.i(TAG, "SSL Error:${error?.description()}")
                                        Log.i(TAG, error.toString())
                                        handler?.cancel()   // サーバーとの通信を終了
                                        // handler?.proceed()  // サーバーとの通信を続行(エラーを無視)
                                    }
                                }
                                webView.loadUrl(_uriString)
                            },
fun SslError.description(): String {
    val _e = primaryError
    return when(_e) {
        SSL_NOTYETVALID -> "(${_e}) The certificate is not yet valid"
        SSL_EXPIRED -> "(${_e}) The certificate has expired"
        SSL_IDMISMATCH -> "(${_e}) Hostname mismatch"
        SSL_UNTRUSTED -> "(${_e}) The certificate authority is not trusted"
        SSL_DATE_INVALID -> "(${_e}) The date of the certificate is invalid"
        SSL_INVALID -> "(${_e}) A generic error occurred"
        else -> "Unknown !"
    }
}

ログは「証明機関が信頼されていない」と告げています。自己署名(自身が証明機関)だからです。

SSL Error:(3) The certificate authority is not trusted
primary error: 3 certificate: \
    Issued to: \
        1.2.840.113549.1.9.1=#160a53616d706c6541646472,
        CN=mysite.example.com,OU=SampleSect,O=SampleCorp,L=SampleShi,ST=SampleKen,C=JP; \
    Issued by: \
        1.2.840.113549.1.9.1=#160a53616d706c6541646472, \
        CN=mysite.example.com,OU=SampleSect,O=SampleCorp,L=SampleShi,ST=SampleKen,C=JP; \
    on URL: https://mysite.example.com/
スポンサーリンク

証明書の取り込み

自己署名証明書を「信頼できる証明書」として、Android StudioのProjectへ意図的に取り込みます。

以下の3つの作業が必要です。

  • (1)res/rawへ証明書を配置
  • (2)res/xmlへnetwork_security_configファイルを配置
  • (3)AndroidManifestへエントリーを追加

※Androidのドキュメントが用意されています。必要であれば参照してください。
ネットワーク セキュリティ構成

(1)res/rawへ証明書を配置

res/rawフォルダを作成し、証明書ファイルを配置します。

res/rawの作成

res/rawへ証明書を配置

証明書ファイルのフォーマットは、depおよびpemが利用できます。

opensslのデフォルトはpemを出力するので、ここではpemを用いました。

【server.crt】
-----BEGIN CERTIFICATE-----
MIIEDDCCAvSgAwIBAgIUdo0DYPrXnA7wy/nfoP2/up20GTQwDQYJKoZIhvcNAQEL
BQAwgZcxCzAJBgNVBAYTAkpQMRIwEAYDVQQIDAlTYW1wbGVLZW4xEjAQBgNVBAcM
...
nT1hfM3OsdRmTHXWp7XFIbpCU3uCRaGdQ16uQ7Ge5PFQGv7vCtdAKJC+iM9LFplP
SvL1QEaS3Z5h1fu6o7eCnU8XxR+r5qJNodEcLrNzk6g=
-----END CERTIFICATE-----

※dep:証明書をITU-Tで定められたデータ構造で表し、シリアライズしたバイナリ
※pem:depのバイナリをBase64でテキスト化

スポンサーリンク

(2)res/xmlへnetwork_security_configファイルを配置

res/xmlフォルダを作成し、network_security_configファイルを配置します。

res/xmlの作成

res/xmlへConfigを配置

「<domain>」に証明対象のドメイン(ホスト名)を指定します。

includeSubdomains=”true”にすると、証明対象をサブドメイの範囲まで拡大できます。ここでは”false”にしています。

また、「<certificates>」に証明書ファイル名(拡張子を省いた名前)を指定します。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="false">mysite.example.com</domain>
<!--        <domain includeSubdomains="true">example.com</domain>-->
        <trust-anchors>
            <certificates src="@raw/server"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

(3)AndroidManifestへエントリーを追加

AndroidManifestへエントリーを追加します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        ...
        android:supportsRtl="true"
        android:networkSecurityConfig="@xml/network_security_config"
        ...
        <activity
            android:name=".MainActivity"
            ...
        </activity>
    </application>

</manifest>
スポンサーリンク

httpsアクセス

自己署名証明書の取り込み後、httpsによる接続を行いました。SSLのエラーは無くなり、接続が確立できています。

自己署名証明書の取り込み後のhttpsアクセス

スポンサーリンク

Debug/Release対応

自己署名証明書の取り込みはDebug時のみに止め、Release時は正式なCA証明書を持つWebサーバーへ、運用を移行するようにします。

mainフォルダと並列にdebugファルダを作成し、その下にリソースファイルを配置すると、「Build Variants:Debug/Release」の切り替えに追随して、使われるリソースファイルが切り替わります。

debug/res/xmlの作成

debug/res/xmlへConfigを配置

  • Variants:Debug  ⇒ debugフォルダ以下のリソースを使用
  • Variants:Release ⇒ mainフォルダ以下のリソースを使用
  • ※Variantsに対応するフォルダが無ければmain(デフォルト)

main側のconfigは空にして、自己署名証明書の取り込みが行われない(=正式なCA証明書を使う)ようにします。

mainのconfigdebugのconfig
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
</network-security-config>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="false">mysite.example.com</domain>
<!--        <domain includeSubdomains="true">example.com</domain>-->
        <trust-anchors>
            <certificates src="@raw/server"/>
        </trust-anchors>
    </domain-config>
</network-security-config>
スポンサーリンク

関連記事:

Androidエミュレーターが実行されると、仮想ネットワークが自動生成されます。 そして、エミュレーターは、仮想ネットワークに接続された一つのコンピュータとして扱われます。 仮想ネットワークは開発マシン(Android Studioが起動しているマシン)にも接続されてるので、エミュレーターから開発マシンへ、ネットワークを介した通信が可能です。 開発マシンへWebサーバーを構築すれば、サーバーと通信を行うアプリ開発が行えます。 開発者の机の上に、バックエンド(サーバ側)とフロントエンド(クライアント側)の開発環境が得られて、とても便利です。 ※環境:Android Studio Otter | 2025.2.1     Android emulator version 36.2.12.0 ...
WindowsもLinux(Unix)と同じように「/etc/hostsファイル」を持っています。 役割も同じで「ホスト名とIPアドレスの変換テーブル」です。ホスト名からIPアドレスへ変換する際に、DNSに先立って参照されます。 ですので、hostsファイルにホスト名とIPアドレスの対を登録すれば、任意のホスト名でネットアクセスが可能になります。 ただし、Windowsの一般ユーザ向けの機能というよりも、ヘビーユーザー向けの隠し機能のような存在です。 このような機能はトラブルの火種に成りかねないので、一時的な利用に止め、不要になったら設定を削除することをお勧めします。 ※環境:Windows 11 Pro Version 25H2 ...
Androidエミュレータがブート時に用いるシステムイメージは、デフォルトで読み出し専用イメージです。 Androidシステムの起動後に、システムに深く関連するディレクトリやファイル(例えば、/etc/hosts)の書き込み(または書き換え)は出来ません。読み出しは出来ます。 書き込みを行いたい場合は、書き込み可能イメージを作成して、そのイメージをブートで用います。 書き込み可能イメージは、エミュレータの起動オプション「-writable-system」の指定で作成可能です。 Android StudioのDevice Managerからエミュレータを起動できますが、Android Studioは起動オプションを指定出来ません。昔は出来ていたと思うのですが... ですので、ここではAndroid Studioは使わずに、ターミナル等のコマンドラインからエミュレータを起動しています。 ※環境:Android Studio Otter | 2025.2.1     Android emulator version 36.2.12.0     Google APIs Inten x86 At ...
Androidエミュレータがブート時に用いるシステムイメージは、デフォルトで読み出し専用イメージです。 Androidシステムの起動後に、システムに深く関連するディレクトリやファイル(例えば、/etc/hosts)の書き込み(または書き換え)は出来ません。読み出しは出来ます。 書き込みを行いたい場合は、書き込み可能イメージを作成して、そのイメージをブートで用います。 書き込み可能イメージは、エミュレータの起動オプション「-writable-system」の指定で作成可能です。 Android StudioのDevice Managerからエミュレータを起動できますが、Android Studioは起動オプションを指定出来ません。昔は出来ていたと思うのですが... ですので、ここではAndroid Studioは使わずに、ターミナル等のコマンドラインからエミュレータを起動しています。 ※環境:Android Studio Otter | 2025.2.1     Android emulator version 36.2.12.0     Google APIs Inten x86 At ...
開発マシンへローカルWebサーバー(Apache)を構築します。 これにより、Webサーバーと通信するアプリのテストが容易になります。 外部にWebサーバーを調達する必要がなく、机上のパソコン(開発マシン)内でテスト環境が完結します。 Webサーバーの構築にXAMPPを使いました。 XAMPPは、ファストフードならぬ、ファストツールです。インストールして直ぐに使える点が、いいですね! ※環境:XAMPP 8.2.12      XAMPP Control Panel Version 3.3.0.      Apache 2.4.58      OpenSSL 1.1.1p     Chrome バージョン 142.0.7444.163 ...
2000年の初め頃、https(HTTPのSSLによる暗号化通信)は、Webサイトの一部のページやECサイトなど、機密情報を扱う部分のみで使われていました。 「ネット決済をするときは、URL横の鍵マークを確認しましょう!」と、注意喚起されていたのを覚えています。 現在は、機密情報を扱う・扱わないに関係なく、ほとんどのWebサイトがhttpsを採用しています。安全なサイトであることを証明するためです。 日本(2025年)におけるWebサイトのhttps使用率は95%に達しているそうです。ですから、httpsは必須と言えます。 ローカルWebサーバーのhttps化は不要ですが、アプリ開発環境であれば、実環境(一般ユーザにアプリを使ってもらう環境)に合わせて導入した方が良いでしょう! ※環境:XAMPP 8.2.12      XAMPP Control Panel Version 3.3.0.      Apache 2.4.58      OpenSSL 1.1.1p     Chrome バージョン 142.0.7444.163 (Windows版) ...
通常、Chromeは自己署名証明書を「信頼できない証明書」と判断します。その結果、httpsによる接続は拒否されます。 しかし、自己署名証明書を「信頼できる証明書」として、Chromeへ意図的に取り込むことで、httpsによる接続が可能になります。 ただし、自己署名証明書が信頼できない点は変わりません。 ですので、パブリックなWebサーバーで、この方法を用いる事は危険です。プライベートなローカルWebサーバーのみで用いるようにして下さい。 ※環境:XAMPP 8.2.12      XAMPP Control Panel Version 3.3.0.      Apache 2.4.58      OpenSSL 1.1.1p     Chrome バージョン 142.0.7444.163 (Windows版) ...
通常、Chromeは自己署名証明書を「信頼できない証明書」と判断します。その結果、httpsによる接続は拒否されます。 しかし、自己署名証明書を「信頼できる証明書」として、Androidへ意図的にインストールすることで、httpsによる接続が可能になります。 ただし、自己署名証明書が信頼できない点は変わりません。 ですので、パブリックなWebサーバーで、この方法を用いる事は危険です。プライベートなローカルWebサーバーのみで用いるようにして下さい。 ※環境:XAMPP 8.2.12      XAMPP Control Panel Version 3.3.0.      Apache 2.4.58      OpenSSL 1.1.1p     Chrome バージョン 74.0.3729.185 (Android版)     Emulator 10.0(Q) API 29 ...
通常、Chromeは自己署名証明書を「信頼できない証明書」と判断します。その結果、httpsによる接続は拒否されます。 しかし、自己署名証明書を「信頼できる証明書」として、Androidへ意図的にインストールすることで、httpsによる接続が可能になります。 ただし、自己署名証明書が信頼できない点は変わりません。 ですので、パブリックなWebサーバーで、この方法を用いる事は危険です。プライベートなローカルWebサーバーのみで用いるようにして下さい。 ※環境:XAMPP 8.2.12      XAMPP Control Panel Version 3.3.0.      Apache 2.4.58      OpenSSL 1.1.1p     Chrome バージョン 113.0.5672.136 (Android版)     Emulator 14.0(U) API 34 ...
スポンサーリンク