kintone のセキュアアクセス環境で Windows アプリからのデータ取得

kintoneセキュアアクセス環境でクライアント証明書を使ったWindowsアプリからのデータ取得を試しました。自己メモとして、参考情報のリンクとコード例をこちらに残しておきます。

# kintoneのセキュアアクセスについて
セキュアアクセスは、クライアント証明書を使って接続元の端末を認証する方法で、kintoneをはじめとした cybouz 社のクラウドサービス（cybouz.com）で利用可能です。

cybouz.com でセキュアサイトは、kintoneの接続にIPアドレス制限を行った場合に外部からの接続手段として用いられます。詳細は以下に説明を参照ください。

**cybouz.com セキュアアクセスとは**
https://jp.cybozu.help/general/ja/admin/list_security/list_access/secureaccess.html
**cybouz.com IPアドレス制限とは**
https://jp.cybozu.help/general/ja/admin/list_security/list_access/ip_restrictions.html

セキュアアクセスはクライアント証明書をブラウザに登録することで、安全に外部から接続できる仕組みです。クライアント証明書のセキュリティ的なメリットを知りたい方はこちらを参照すると良いでしょう。

**クライアント証明書 総合解説サイト**
https://jp.globalsign.com/lp/epki/
**【図解】クライアント証明書(https,eap-tls)の仕組み**
https://milestone-of-se.nesuke.com/sv-advanced/digicert/client-cert/

実際にクライアント証明書を発行し、ブラウザに登録して利用するまでは以下を参照すると良いでしょう。

**セキュアアクセスを設定する**
https://jp.cybozu.help/general/ja/admin/list_security/list_secureaccess/secureaccess.html
**クライアント証明書の登録方法**
https://jp.cybozu.help/ja/settings/browser/certificate.html

# 今回の試験実装
前置きが長くなりましたが、今回はブラウザではなくWindows環境で動くアプリを開発して、外部API経由でkintoneのセキュアアクセスする方法を実際に試してみました。

## 試験環境
今回の試験環境はOS Windows 10 Professional のPCを用いて、開発ツールはVisual Studio 2017、開発言語はC#で .NET Freamework 4.8のwindows formsプロジェクトを準備しました。kintone外部APIでのデータ受け渡しはJSON形式になりますので、以下のパッケージをnugetで追加しています。

**Newtonsoft.Json.13.0.1**
https://www.nuget.org/packages/Newtonsoft.Json/

## 実装について
Windowsのフォームに配置したボタンをクリックすると、kintoneのセキュアアクセス経由で指定したアプリのレコードを読み込み、テキストフィールドに結果をJSONフォーマットで表示するテスト用のアプリを実装しました。

今回の試験実装のポイントはHttpWebRequestでクライアント証明書を使う方法でした。情報は少なかったものの、しつこく検索した結果X509Certificate2でクライアント証明書を参照し、HttpWebRequestのClientCertificates.Add()で追加するだけで良いことがわかり、以下のように以外と簡単に実装可能でした。

```KintoneApp.CS
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace kintoneSecureAccsessTest
{
    public static class KintoneApp
    {

        private static readonly string apiUrl   = "https://<サブドメイン名>.s.cybozu.com/k/v1/records.json";
        private static readonly string apiToken = "＜アプリのtoken＞";
        private static readonly string appId    = "＜アプリID＞";
        private static readonly string query    = "＜クエリ文＞"; 
        
        private static readonly string certPath   = @"＜証明書ファイルのパス＞";
        private static readonly string certPasswd = @"＜証明書パスワード＞";
        
        public static string DataGet()
        {
            // TLS1.1、TLS1.2 対応
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
            
            // token を利用して kintone のレコードを取得
            string url = apiUrl + Uri.EscapeUriString("?app=" + appId + "&query=" + query + "&totalCount=true");
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
            
            // kintone クライアント証明書を追加
            X509Certificate2 Cert = new X509Certificate2(certPath, certPasswd, X509KeyStorageFlags.PersistKeySet);
            req.ClientCertificates.Add(Cert);
            
            // ヘッダーにTokenを追加
            req.Headers.Add("X-Cybozu-API-Token:" + apiToken);
            
            // kintone からのレスポンスをテキストで返す
            using (var respons = req.GetResponse())
            {
                using (var stream = respons.GetResponseStream())
                {
                    var sr = new StreamReader(stream, Encoding.GetEncoding("UTF-8"));
                    string text = sr.ReadToEnd();
                    sr.Close();
                    stream.Close();
                    return text;
                }
            }
        }
    }
}
```

蛇足までにフォームのコードは以下です。

```Form1.CS
using Newtonsoft.Json;
using System;
using System.Windows.Forms;

namespace kintoneSecureAccsessTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // kintoneクライアント証明書を使って取得したアプリのレコードをJSONで表示
            var respons = KintoneApp.DataGet();
            this.textBox1.Text = JsonConvert.SerializeObject(
                JsonConvert.DeserializeObject(respons),
                Formatting.Indented);
        }
    }
}
```

# 参考資料
**HttpClient でクライアント証明書を設定する方法**
https://qiita.com/volpe28v/items/95efad9e8816b00b348c
**X509Certificate2 クラス**
https://docs.microsoft.com/ja-jp/dotnet/api/system.security.cryptography.x509certificates.x509certificate2?view=net-6.0