Welcome to telecotele.com » 丁寧な暮らし

e-Gov電子申請のコツ

ガ~ブガブガブ、良いガブねぇ

tags: WORKING

はじめに

e-Gov電子申請を使いはじめました。 これは行政手続きをオンライン上で行えるサービスです。 PCに専用ソフトをインストールして使います。

これを使うと役所などに出向いたり郵送したりしなくて済むというメリットはあるものの、 これはこれでクセがあり紙での申請とはまた違う面倒さもありました。

そこで、この記事では(あとで見返すため)面倒さを緩和するための「コツ」をまとめておきます。 なお、ここではWindows上で操作することを前提にしており、 申請者は事業主本人の想定です。

GビズIDプライムを使う

e-Gov電子申請は「e-Govアカウント」や「GビズID」などからログインして使います。

ここで使うのは「GビズIDプライム」がおすすめです。 GビズIDプライムの発行には審査が必要ですが、マイナンバーカードを使ったオンライン申請なら最短即日1で発行されます。

GビズIDプライムを使うと、電子署名が省略できる手続きが増えます。 また、電子送達を利用する場合はGビズIDプライムが必要2です。

電子署名について

一部の手続きでは電子署名が必要です。 ここではマイナンバーカードを使って署名する前提でコツを紹介します。

署名の際は、 ICカードリーダを(ドライバなど設定したうえで)PCに繋いだ状態で、 マイナンバーカードを載せて読み取れる状態にしておく必要があります3。 このとき、もしマイナンバーカードやICカードリーダーを外していたとしても、 特に「準備して!😡」といったダイアログが出るわけではありません。 署名に失敗するだけです。

うまくマイナンバーカードが認識されると「ICカードのPINコード」が求められるので入力します。 PINコード?大抵4桁の数字ね、あのコンビニで住民票出すときのさ……と思ってしまいますが、これは「署名用パスワード」(6桁~16桁の英数字混在)のことを指しています。 署名するぞって場面ですからね、PINコードはミスリードなどと怒ってはいけません。

ログインの自動化

申請用ソフトを起動すると、毎回次のログイン画面が(全画面表示になって)出てきます。

e-Gov電子申請の起動画面(ログイン画面) e-Gov電子申請の起動画面(ログイン画面)

GビズIDプライムを使いたいので「GビズIDでログイン」をクリック、 するとすぐに申請トップページに飛ぶ場合(直近でログインしている)もあれば、 GビズIDの2要素認証待ちの画面に飛ぶ場合(直近ログインから少し時間が経った)もあれば、 GビズIDのユーザ名とパスワードの入力から始まる場合(しばらくログインしなかった)もあります。

最後の「しばらくログインしなかった」といっても1日ほど?で期限が切れるようで、 だいたいGビズIDのユーザ名とパスワード入力からです。 しかも、中身は何かブラウザが動いている気配はしますが、ChromeやEdgeなどに内蔵されているパスワードマネージャーは動かず4手間がかかります。

そこで、このログイン操作を自動化してみました。

Win32APIによる操作

まず、内部を確認する5と、申請用ソフトのブラウザはWebBrowser コントロールで動いているようです。 そしてこの中身はInternet Explorer(またはこれがIEモードと呼ばれるもの?)で動いているらしい6

外部のプログラムからInternet Explorer(IEモード含む)を触るには、Internet Explorer_Serverのウィンドウハンドルを取得して操作するとのこと。

【VBA】IEモードでHTMLDocument を取得したり操作したりするための関数を書いた #Microsoft - Qiita
https://qiita.com/ymd65536-ms/items/320f889e9fc35fe375f1

アプリを起動しボタンを押し「Internet Explorer_Server」クラスの子ウインドウ内のボタンも押す。
https://gist.github.com/s-note/e8c6109d4c1be237ecf98db194918cf3

見よう見まねですが、上記を参考にそれっぽくPythonから操作するものはできました。

長いので折りたたみ
import os
import time
import urllib.parse
import pythoncom
import wmi
import win32api
import win32gui
import win32con
import win32process
import win32com.client

def find_toplevel_windows(pid):
    def callback(hwnd, l):
        _, p = win32process.GetWindowThreadProcessId(hwnd)
        if p == pid: l.append(hwnd)
        return True
    toplevel_windows = []
    win32gui.EnumWindows(callback, toplevel_windows)
    return toplevel_windows

def find_ie_windows(toplevel_windows):
    def callback(hwnd, l):
        classname = win32gui.GetClassName(hwnd)
        if classname == 'Internet Explorer_Server': l.append(hwnd)
        return True
    ie_windows = []
    for toplevel_window in toplevel_windows:
        win32gui.EnumChildWindows(toplevel_window, callback, ie_windows)
    return ie_windows

def get_html_document(ie_window):
    msg = win32gui.RegisterWindowMessage('WM_HTML_GETOBJECT')
    _, result = win32gui.SendMessageTimeout(ie_window, msg, 0, 0, win32con.SMTO_ABORTIFHUNG, 1000)
    ob = pythoncom.ObjectFromLresult(result, pythoncom.IID_IDispatch, 0)
    return win32com.client.dynamic.Dispatch(ob)

if __name__ == '__main__':
    app_path = r'C:\Program Files (x86)\eGovClient\eGovClient.exe'
    gbiz_username = 'xxxx'
    gbiz_password = 'xxxx'

    # 起動
    win32api.WinExec(app_path)
    time.sleep(3) # 起動待ち

    # PID取得
    c = wmi.WMI()
    rows = c.query(f'SELECT ProcessId FROM Win32_Process WHERE Name = "{os.path.basename(app_path)}"')
    if len(rows) != 1:
        print('Error', 'Unexpected number of processes:', len(rows))
        exit(1)
    pid = rows[0].ProcessId

    # "Internet Explorer_Server" を探す
    toplevel_windows = find_toplevel_windows(pid)
    ie_windows = find_ie_windows(toplevel_windows)
    if len(ie_windows) != 1:
        print('Error', 'Unexpected number of ie_windows:', len(ie_windows))
        exit(1)
    ie_window = ie_windows[0]

    # ウィンドウサイズを変えておく
    for toplevel_window in toplevel_windows:
        if 'e-Govアカウントログイン' in win32gui.GetWindowText(toplevel_window):
            left, top, right, bottom = win32gui.GetWindowRect(toplevel_window)
            width = right - left
            height = bottom - top
            win32gui.MoveWindow(toplevel_window, left, top, width//2, height, True)

    # 「GビズIDでログイン」を選択
    # querySelectorもgetElementByIdも使えないので頑張る
    # https://stackoverflow.com/questions/39205598/getelementbyid-takes-exactly-1-argument-2-given
    doc = get_html_document(ie_window)
    parsedUrl = urllib.parse.urlparse(doc.URL)
    if not (parsedUrl.netloc == 'account.e-gov.go.jp' and parsedUrl.path == '/auth/auth'):
        print('Error', 'Unexpected URL (eGov):', doc.URL)
        exit(1)
    for e in doc.documentElement.getElementsByTagName('li'):
        if e.id == 'gbizid':
            e.getElementsByTagName('a')[0].click()
            time.sleep(3) # 遷移待ち
            break

    # GビズID入力
    # URLが想定外(遷移できなかった or すでに二段階認証に行った)なら何もしない
    doc = get_html_document(ie_window)
    parsedUrl = urllib.parse.urlparse(doc.URL)
    if parsedUrl.netloc == 'gbiz-id.go.jp' and parsedUrl.path == '/oauth/login':
        elements = {'username': None, 'password': None, 'submit': None}
        for e in doc.documentElement.getElementsByTagName('input'):
            if e.id == 'username': elements['username'] = e
            if e.id == 'password': elements['password'] = e
            if e.type == 'submit': elements['submit'] = e
        if all([ x is not None for x in elements.values()]):
            elements['username'].value = gbiz_username
            elements['password'].value = gbiz_password
            elements['submit'].click()

ユーザ名 (gbiz_username)とパスワード (gbiz_password)は当然ダミーです。 また、平文で書いているので使用にはリスクがあります。 共用の環境では使えないでしょう。 いくら2要素認証はあるとはいえ……。 もし暗号化や特定のパスワードマネージャーを見るようにするなどのカスタマイズをご希望で、ご予算に余裕のある、ご聡明なお方がいらっしゃればお問い合わせのページから、何とは申し上げませんが承っております。

この方法を使えば、ログイン操作以外に例えば申請書類の自動化もいけそうです。 ただ、それは一時保存ファイルを見たほうが楽では、 また自動化しなければならないほど申請が多いならAPI連携(市販ソフトウェア含む)を使ったほうが楽でしょうね。

他の案

一応他の案も考えていたのでまとめます。

VBScriptからなんとかする

当初はPythonが入っていない環境のことも考え、VBScriptでなんとかする気でいました。 しかし、Win32APIを直接実行するのは難しく没になりました。

スマホで見る案

e-Gov電子申請はスマホのブラウザからも「閲覧」可能です。 新たに申請はできませんが、申請の処理状況や通知が確認できます。 ブラウザから見るのでパスワードマネージャーもちゃんと動きます。

じゃあこれでいいじゃん。 そう何度も頻繁に新規申請しまくるってことはなかなか無いでしょう。

なお、PCではブラウザから見ることはできません。 どのようにPC or スマホを判定しているかは未調査ですが、 (どうしてもPCから見られるのを拒否したいならともかく)User-Agentを見ているだけではないか、 調整したらPCでも見えるようになったりしないですかね。

自動化ツールを使う

GUIを自動で操作するツールなんていくらでもあるでしょう。 何か使おうと思ったのですが、GビズIDのログインにおいては状況によって遷移先画面が変わるため、うまくいかないと思ってやめました。

Cookieを操作する

普段使っているブラウザからGビズIDにログインしておき、セッションIDなどCookieを覚えておく。 それを(e-Gov電子申請ツールに内蔵の)WebBrowserコントロールに読んでもらう。

これは実現できるか、またこれはこれで操作が面倒ではないかと思ってしまい没になりました。

Proxyを挟む

e-Gov電子申請のツールと、Webサーバ間に「e-Gov電子申請のツール」として振る舞うProxyを挟み、Locationヘッダで飛ばしたり飛ばさなかったりしながらログイン処理を代替します。 でも、そもそも挟めるかわからなかったので没です。

Custom URL Schemeを使う

e-Gov電子申請のツールでは、Custom URL Schemeが使われています。 パラメータも渡せるようでした。 そのパラメータは次のとおり。

ログイン自動化に使えそうなものはありませんので没です。

おわりに

e-Gov電子申請のコツについてまとめました。 またコツができたら追記します。

「ログインの自動化」は自分で書いておいてなんですが、これ使うかな……スマホから見るだけで十分なんじゃないかという気がしてきました。 うーん。

ちなみに、e-Gov電子申請そのものの話ですが、先日嘆いたこれは無事受理されました。

再提出すると、以前の記入内容は消えるので最初から入力し直しです。賽の河原。 コツとして「めげない」とか入れたほうが良いかも知れませんね。

おわり。

Footnotes

  1. 自分の場合だと、平日のサービス提供時間内に申し込みを行い、数十分で発行されました。

  2. 「GビズIDメンバー」でも良いようです。ただ、これを発行するためにはやっぱりGビズIDプライムが必要です。

  3. 最初はJPKI利用者ソフトから署名用電子証明書をファイルに保存しようとしていましたが、これは違う。

  4. ちょっと前のFusion360もこんな感じでしたが、最近は普段使っているブラウザが開いてそこでログイン情報を入力するようになりました。e-Gov電子申請もこうだと嬉しかったですね。

  5. アンチデバッグのためか、文字列は何らかの方法でエンコードされています。デコードは可能ですがgrepにしくい。

  6. 電子公文書がInternet Explorer(IEモード含む)じゃないと意図通りに見えないためと思いますが……。