TECH PLAY

株式会社メドレー

株式会社メドレー の技術ブログ

1359

こんにちは、開発本部の平木です。 去る 7/14(土)に Rails Developers Meetup 2018 Day 3 Extreme というイベントが開催されまして、 弊社の宍戸 が 電子カルテとセキュリティガイドラインと AWS と私 というタイトルで発表させていただきましたので、レポートさせていただきます。 発表のきっかけ 昨年に引き続き RubyKaigi 2018 で弊社 CTO の平山が LT スポンサーとして発表させていただいたり、ブースに出展をしていた ことがきっかけになり、Rails Developers Meetup の主催者である平野さん( @yoshi_hirano )から平山へ出演のお声がけをしていただきました。イベントからイベントへ関係がつながるのはとても嬉しいですね。 発表テーマについて せっかく、お声がけしていただいたので、発表テーマも弊社の特色が出るものが良いであろうと決まったのが今回のテーマです。 4 月に発表した電子カルテシステム CLINICS カルテ ですが、何度かこちらのブログでもエントリが上がっていますとおり、電子カルテというシステムを構築する上で、セキュリティはかなり重要なウェイトを占める要素になっています。 このセキュリティを担保するための指針として 3 省 4 ガイドライン という総務省・経産省・厚労省の各省庁から出ている 4 種類のガイドラインが出ています。 これを実際に AWS をベースとしたシステムを構築する際の一例として CLINICS カルテでの構築例を発表しようということになりました。 発表スライド 当日の発表スライドはこちらになります。 電カルでガイドラインに沿うよう cloud trail とか vpc flow logs といった細かな AWS のサービス使ってるのか。 #rdm2018A #railsdm — yuyasat (@yuyasat) July 14, 2018 会場ではスライドの色見がすっごく色褪せしていたりしましたが、発表した中での AWS のサービスは会場のみなさんの中でも、あまり馴染みがないものが多いようでスライドの写真など撮られている方もいらっしゃって、知見共有という意味で参考になった模様です。 クライアント認証の話たのしいしめっちゃ知見だな。ALB, API Gateway は TLS クライアント認証に非対応だったので、Nginx で認証させてるとのこと。 #rdm2018A #railsdm — かつひささん (@katsuhisa__) July 14, 2018 メドレーさんの発表、すげーよかった。 派手さはなくとも、こうやって一個ずつ地道に技術課題を解決している話はめちゃくちゃおもしろい。 #rdm2018A #railsdm — かつひささん (@katsuhisa__) July 14, 2018 やはりクライアント認証に関しては普段サービス開発ではそこまでは使われないので、興味を持っていただいたようで良かったです! まとめ 弊社のような医療業界ではなくても、フルマネージドサービスでセキュリティを意識するような場面があればご参考になるかもしれないテーマをエンジニアの宍戸から発表させていただきました。 社内で貯まった知見で機会があれば、ぜひ公開していきたいと思います。 メドレーについて気になった方は、こちらからどうぞ。 About 株式会社メドレー - Wantedly You can read employee interviews and the latest company's initiative of 株式会社メドレー. 急速な高齢化や医療費の高騰、医療現場の疲弊が叫ばれる中で、このままでは家計を大きく圧迫して支えきれなくなり、日本の医療は崩壊してしまいます。この状態を解消するための鍵が「医療現場におけるクラウド活用を駆使した業務効率化」です。 しかし日本では、半数以上の医療機関がいまだに紙カルテを利用しているなど、クラウド化は疎かデジタル活用も進んでいないのが現状です。私たちはテクノロジーを活用した事業やプロジェクトを通じて、医療ヘルスケア分野のデジタル活用を推進し、日本の未来を作るための取り組みを行っていきます。 www.wantedly.com
アバター
こんにちは、開発本部の平木です。 去る 7/14(土)に Rails Developers Meetup 2018 Day 3 Extreme というイベントが開催されまして、 弊社の宍戸 が 電子カルテとセキュリティガイドラインと AWS と私 というタイトルで発表させていただきましたので、レポートさせていただきます。 発表のきっかけ 昨年に引き続き RubyKaigi 2018 で弊社 CTO の平山が LT スポンサーとして発表させていただいたり、ブースに出展をしていた ことがきっかけになり、Rails Developers Meetup の主催者である平野さん( @yoshi_hirano )から平山へ出演のお声がけをしていただきました。イベントからイベントへ関係がつながるのはとても嬉しいですね。 発表テーマについて せっかく、お声がけしていただいたので、発表テーマも弊社の特色が出るものが良いであろうと決まったのが今回のテーマです。 4 月に発表した電子カルテシステム CLINICS カルテ ですが、何度かこちらのブログでもエントリが上がっていますとおり、電子カルテというシステムを構築する上で、セキュリティはかなり重要なウェイトを占める要素になっています。 このセキュリティを担保するための指針として 3 省 4 ガイドライン という総務省・経産省・厚労省の各省庁から出ている 4 種類のガイドラインが出ています。 これを実際に AWS をベースとしたシステムを構築する際の一例として CLINICS カルテでの構築例を発表しようということになりました。 発表スライド 当日の発表スライドはこちらになります。 電カルでガイドラインに沿うよう cloud trail とか vpc flow logs といった細かな AWS のサービス使ってるのか。 #rdm2018A #railsdm — yuyasat (@yuyasat) July 14, 2018 会場ではスライドの色見がすっごく色褪せしていたりしましたが、発表した中での AWS のサービスは会場のみなさんの中でも、あまり馴染みがないものが多いようでスライドの写真など撮られている方もいらっしゃって、知見共有という意味で参考になった模様です。 クライアント認証の話たのしいしめっちゃ知見だな。ALB, API Gateway は TLS クライアント認証に非対応だったので、Nginx で認証させてるとのこと。 #rdm2018A #railsdm — かつひささん (@katsuhisa__) July 14, 2018 メドレーさんの発表、すげーよかった。 派手さはなくとも、こうやって一個ずつ地道に技術課題を解決している話はめちゃくちゃおもしろい。 #rdm2018A #railsdm — かつひささん (@katsuhisa__) July 14, 2018 やはりクライアント認証に関しては普段サービス開発ではそこまでは使われないので、興味を持っていただいたようで良かったです! まとめ 弊社のような医療業界ではなくても、フルマネージドサービスでセキュリティを意識するような場面があればご参考になるかもしれないテーマをエンジニアの宍戸から発表させていただきました。 社内で貯まった知見で機会があれば、ぜひ公開していきたいと思います。 メドレーについて気になった方は、こちらからどうぞ。 About 株式会社メドレー - Wantedly You can read employee interviews and the latest company's initiative of 株式会社メドレー. 急速な高齢化や医療費の高騰、医療現場の疲弊が叫ばれる中で、このままでは家計を大きく圧迫して支えきれなくなり、日本の医療は崩壊してしまいます。この状態を解消するための鍵が「医療現場におけるクラウド活用を駆使した業務効率化」です。 しかし日本では、半数以上の医療機関がいまだに紙カルテを利用しているなど、クラウド化は疎かデジタル活用も進んでいないのが現状です。私たちはテクノロジーを活用した事業やプロジェクトを通じて、医療ヘルスケア分野のデジタル活用を推進し、日本の未来を作るための取り組みを行っていきます。 www.wantedly.com
アバター
こんにちは、開発本部の宮内です。今回、HPKI カードについて調査を行いましたので、それについて書きます。 JAHIS HPKI 対応 IC カードガイドライン Ver.3.0 を参考にして、HPKI テストカードから実際に公開鍵証明書を取得しました。 今後も HPKI について調査を続行していきたいと思います。 HPKI とは? HPKI とは厚生労働省が認める医療福祉関係資格(医師・薬剤師・看護師など 26 種類の保健医療福祉分野の国家資格と、院長・管理薬剤師など 5 種類の管理者資格)を認証することができる PKI です。 配布された HPKI カードには、ルート CA、中間 CA、証明書が格納されています。 このカードは、電子署名などに使用することができ、今後普及していけば、医療文書(処方箋や病院への紹介状など)を印刷、押印、送付するなどの非効率な業務をすることなく、すべてデジタル化することができるようになります。 また、電子認証用の証明書も含まれているため、認証・認可処理にも使用することができます。 今回、HPKI テストカードを用いて調査を行いました。 調査環境 macOS v10.13.5 ACR39-NTTCom Ruby v2.5.1 smartcard v0.5.6 HPKI テスト用カード PC/SC HPKI カードのような IC カードとやり取りを行うには、 PC/SC という API 仕様を使う必要があります。 PC/SC はもともと Windows 環境のみで利用可能でしたが、pcsc-lite という OSS 実装があり、現在では様々な UNIX like OS でも利用できます。 macOS の場合、 /System/Library/Frameworks/PCSC.framework/PCSC にライブラリが用意されており、特に準備する必要なく利用可能です。(2018 年 07 月現在) ただし、IC カードリーダーのドライバーをインストールする必要があります。 今回利用した ACR39-NTTCom は ダウンロードページ に macOS v10.13 に対応したドライバーが配布されていなかったため、IC カードリーダーのチップメーカーである ACS 社の ダウンロードページ からドライバーを入手しました。 smartcard 検証する際に使用した gem は smartcard です。 普通の rubygem と同じく gem install して利用します。 gem install smartcard IC カードリーダーを PC に接続し、 ruby -rsmartcard -e 'pp Smartcard::PCSC::Context.new.readers' を実行し、IC カードリーダー名が表示されれば接続成功です。 アプリケーション識別子の取得 実際に HPKI テストカードから情報を取得していきます。 ガイドライン の「附属書 A(参考)PKI カードアプリケーション利用のシーケンス」にある「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」を実装していきます。 引用 ガイドライン prog01.rb # prog01.rb require "smartcard" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first # SELECT コマンドで`E8 28 BD 08 0F`をパーシャル指定した DF を指定 apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response while response[ :status ] == 0x9000 # SELECT コマンドで次の DF を探す apdu = [ 0x00 , 0xA4 , 0x04 , 0x02 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response end ensure context. release end 上記のプログラムを実行すると、次のような出力が得られます。 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 status = 6A82 data = SELECT コマンドを発行すると BER-TLV で符号化された FCI(ファイル制御情報)が取得できます。 1つ目のデータから見ていきます。 1バイト目は 6F なので、このデータはファイル制御パラメタ及びファイル管理データの集合を表します。 引用 JIS X 6320-4 表 8-ファイル制御情報用の産業感共通利用テンプレート 2バイト目は 12 なので、後続するデータの長さが 18 バイトあることを表します。 3バイト目は 84 なので、データ要素が DF 名であることを表します。 引用 JIS X 6320-4 表 10-ファイル制御パラメタデータオブジェクト 4バイト目は 10 なので、後続するデータの長さが 16 バイトあることを表します。 5バイト目以降は、DF 名(= アプリケーション識別子)です。 2つ目のデータもデータ構造は同じなため省略します。 これで HPKI テストカードには、 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 という2つのアプリケーション識別子が含まれていることが分かります。 公開鍵証明書を取得する 前段にて HPKI テストカードに含まれているアプリケーション識別子が分かりましたので、次は公開鍵証明書を取得していきます。 ガイドライン の「A.3.2 証明書の読み出し」にあるコマンドの通りに APDU を発行しても、正しいデータは返ってきません。 これは、HPKI テストカードの EF 識別子が、ガイドラインに記載されている EF 識別子とは異なるためです。 HPKI カードは JIS X 6320 に準拠しているため、各種暗号情報オブジェクトへのパス情報を含んだ EF.OD が存在しています。 この EF.OD を使い公開鍵証明書へのパスを取得してから、公開鍵証明書を取得していきます。 引用 ガイドライン EF.OD を読み込む prog02.rb # prog02.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # EF.OD の読み出し apdu = [ 0x00 , 0xB0 , 0x91 , 0x00 , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) pp decode_asn1 response end ensure context. release end EF.OD を読み込むと DER 符号化されたデータが返ってきます。 これを OpenSSL::ANS1 モジュールで復号化すると、次に取得するべき EF 識別子が分かります。 EF.OD の ASN.1 定義は以下のようになっているため、タグが 4 であるデータを読み込めば良さそうです。 CIOChoice ::= CHOICE { privateKeys [0] PrivateKeys, publicKeys [1] PublicKeys, trustedPublicKeys [2] PublicKeys, secretKeys [3] SecretKeys, certificates [4] Certificates, trustedCertificates [5] Certificates, usefulCertificates [6] Certificates, dataContainerObjects [7] DataContainerObjects, authObjects [8] AuthObjects, } prog02.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8e0ef7b0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8e0ef7d8 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8e0ef800 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8d118df0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8d118e18 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8d118e40 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]>, # 中略 ] どちらのアプリケーションも 00 04 が EF.CD(証明書オブジェクト情報)の EF 識別子だということが分かります。 EF.CD を読み込む prog03.rb # prog03.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを apdu 選択する = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 04`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x04 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end pp decode_asn1 data: data end ensure context. release end prog03.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdf99aaf70 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdf99ab038 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdf99aafe8 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdf99aaf98 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05\x17" >]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdfa072308 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdfa072448 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdfa0723d0 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdfa072380 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05%" >]> ] # 中略 これで公開鍵証明書ファイルの EF 識別子が 00 16 であることが判明しました。 公開鍵証明書を読み込む prog04.rb # prog04.rb require "smartcard" require "openssl" context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 16`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x16 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end cert = OpenSSL::X509::Certificate . new (data. reverse_each . drop_while { | i | i == 0xFF }. reverse . pack ( "C*" )) puts cert. to_text end ensure context. release end HPKI テストカードから DER 符号化された公開鍵証明書データが取得できるので、 OpenSSL::X509::Certificate.new でインスタンス化できます。 上記の prog04.rb を実行すると下記のような出力が得られます。 Certificate: Data: Version: 3 (0x2) Serial Number: 13023 (0x32df) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forNonRepudiation Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit ) Modulus: 00:94:dd:09:40:f4:58:f9:0f:ec:3a:ea:e3:47:33: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:44:E9:20:05:4D:6D:C4:B7:FA:4B:F0:1B:C6:EA:C8:D6:5B:16:22:F4 DirName:/C =JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:02 X509v3 Subject Key Identifier: 9E:E5:71:59:1E:A7:FC:1E:4A:31:F8:7B:30:0B:E3:7F:05:3D:9A:40 X509v3 Key Usage: critical Non Repudiation X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-sign2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1 & 0$. "1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.1.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption 84:ae:95:45:5e:e7:64:8b:0c:6e:20:5f:9f:1f:0d:5c:ae:4a: # 中略 Certificate: Data: Version: 3 (0x2) Serial Number: 12927 (0x327f) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forAuthentication-forIndividual Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c6:f9:06:26:58:5e:11:b7:12:f2:8a:3e:97:0a: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:62:12:93:82:DE:3C:D7:FF:A8:D3:63:01:D3:01:6A:AE:6C:3B:C0:D4 DirName:/C=JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:03 X509v3 Subject Key Identifier: 45:2B:7B:B4:47:89:3D:6C:05:6D:82:4D:4C:C8:80:B8:B4:B0:89:81 X509v3 Key Usage: critical Digital Signature X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-auth2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1&0$." 1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.2.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption # 中略 それぞれのアプリケーションから正しく公開鍵証明書が取得できました。 電子認証ガイドライン によると、電子認証に使用する証明書は Issuer の CN(Common Name)が HPKI-01-*-forAuthentication-forIndividual であることが定められているため、 使用した HPKI テストカードでは、電子認証に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 であることが分かります。 また、電子署名に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 であることが分かりました。 最後に 以上で ガイドライン の「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」にある「PKI カードアプリケーションの検索」まで実装できました。 今後、次のステップである暗号計算を実装していきたいと思います。
アバター
こんにちは、開発本部の宮内です。今回、HPKI カードについて調査を行いましたので、それについて書きます。 JAHIS HPKI 対応 IC カードガイドライン Ver.3.0 を参考にして、HPKI テストカードから実際に公開鍵証明書を取得しました。 今後も HPKI について調査を続行していきたいと思います。 HPKI とは? HPKI とは厚生労働省が認める医療福祉関係資格(医師・薬剤師・看護師など 26 種類の保健医療福祉分野の国家資格と、院長・管理薬剤師など 5 種類の管理者資格)を認証することができる PKI です。 配布された HPKI カードには、ルート CA、中間 CA、証明書が格納されています。 このカードは、電子署名などに使用することができ、今後普及していけば、医療文書(処方箋や病院への紹介状など)を印刷、押印、送付するなどの非効率な業務をすることなく、すべてデジタル化することができるようになります。 また、電子認証用の証明書も含まれているため、認証・認可処理にも使用することができます。 今回、HPKI テストカードを用いて調査を行いました。 調査環境 macOS v10.13.5 ACR39-NTTCom Ruby v2.5.1 smartcard v0.5.6 HPKI テスト用カード PC/SC HPKI カードのような IC カードとやり取りを行うには、 PC/SC という API 仕様を使う必要があります。 PC/SC はもともと Windows 環境のみで利用可能でしたが、pcsc-lite という OSS 実装があり、現在では様々な UNIX like OS でも利用できます。 macOS の場合、 /System/Library/Frameworks/PCSC.framework/PCSC にライブラリが用意されており、特に準備する必要なく利用可能です。(2018 年 07 月現在) ただし、IC カードリーダーのドライバーをインストールする必要があります。 今回利用した ACR39-NTTCom は ダウンロードページ に macOS v10.13 に対応したドライバーが配布されていなかったため、IC カードリーダーのチップメーカーである ACS 社の ダウンロードページ からドライバーを入手しました。 smartcard 検証する際に使用した gem は smartcard です。 普通の rubygem と同じく gem install して利用します。 gem install smartcard IC カードリーダーを PC に接続し、 ruby -rsmartcard -e 'pp Smartcard::PCSC::Context.new.readers' を実行し、IC カードリーダー名が表示されれば接続成功です。 アプリケーション識別子の取得 実際に HPKI テストカードから情報を取得していきます。 ガイドライン の「附属書 A(参考)PKI カードアプリケーション利用のシーケンス」にある「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」を実装していきます。 引用 ガイドライン prog01.rb # prog01.rb require "smartcard" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first # SELECT コマンドで`E8 28 BD 08 0F`をパーシャル指定した DF を指定 apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response while response[ :status ] == 0x9000 # SELECT コマンドで次の DF を探す apdu = [ 0x00 , 0xA4 , 0x04 , 0x02 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response end ensure context. release end 上記のプログラムを実行すると、次のような出力が得られます。 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 status = 6A82 data = SELECT コマンドを発行すると BER-TLV で符号化された FCI(ファイル制御情報)が取得できます。 1つ目のデータから見ていきます。 1バイト目は 6F なので、このデータはファイル制御パラメタ及びファイル管理データの集合を表します。 引用 JIS X 6320-4 表 8-ファイル制御情報用の産業感共通利用テンプレート 2バイト目は 12 なので、後続するデータの長さが 18 バイトあることを表します。 3バイト目は 84 なので、データ要素が DF 名であることを表します。 引用 JIS X 6320-4 表 10-ファイル制御パラメタデータオブジェクト 4バイト目は 10 なので、後続するデータの長さが 16 バイトあることを表します。 5バイト目以降は、DF 名(= アプリケーション識別子)です。 2つ目のデータもデータ構造は同じなため省略します。 これで HPKI テストカードには、 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 という2つのアプリケーション識別子が含まれていることが分かります。 公開鍵証明書を取得する 前段にて HPKI テストカードに含まれているアプリケーション識別子が分かりましたので、次は公開鍵証明書を取得していきます。 ガイドライン の「A.3.2 証明書の読み出し」にあるコマンドの通りに APDU を発行しても、正しいデータは返ってきません。 これは、HPKI テストカードの EF 識別子が、ガイドラインに記載されている EF 識別子とは異なるためです。 HPKI カードは JIS X 6320 に準拠しているため、各種暗号情報オブジェクトへのパス情報を含んだ EF.OD が存在しています。 この EF.OD を使い公開鍵証明書へのパスを取得してから、公開鍵証明書を取得していきます。 引用 ガイドライン EF.OD を読み込む prog02.rb # prog02.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # EF.OD の読み出し apdu = [ 0x00 , 0xB0 , 0x91 , 0x00 , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) pp decode_asn1 response end ensure context. release end EF.OD を読み込むと DER 符号化されたデータが返ってきます。 これを OpenSSL::ANS1 モジュールで復号化すると、次に取得するべき EF 識別子が分かります。 EF.OD の ASN.1 定義は以下のようになっているため、タグが 4 であるデータを読み込めば良さそうです。 CIOChoice ::= CHOICE { privateKeys [0] PrivateKeys, publicKeys [1] PublicKeys, trustedPublicKeys [2] PublicKeys, secretKeys [3] SecretKeys, certificates [4] Certificates, trustedCertificates [5] Certificates, usefulCertificates [6] Certificates, dataContainerObjects [7] DataContainerObjects, authObjects [8] AuthObjects, } prog02.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8e0ef7b0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8e0ef7d8 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8e0ef800 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8d118df0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8d118e18 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8d118e40 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]>, # 中略 ] どちらのアプリケーションも 00 04 が EF.CD(証明書オブジェクト情報)の EF 識別子だということが分かります。 EF.CD を読み込む prog03.rb # prog03.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを apdu 選択する = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 04`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x04 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end pp decode_asn1 data: data end ensure context. release end prog03.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdf99aaf70 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdf99ab038 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdf99aafe8 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdf99aaf98 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05\x17" >]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdfa072308 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdfa072448 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdfa0723d0 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdfa072380 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05%" >]> ] # 中略 これで公開鍵証明書ファイルの EF 識別子が 00 16 であることが判明しました。 公開鍵証明書を読み込む prog04.rb # prog04.rb require "smartcard" require "openssl" context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 16`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x16 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end cert = OpenSSL::X509::Certificate . new (data. reverse_each . drop_while { | i | i == 0xFF }. reverse . pack ( "C*" )) puts cert. to_text end ensure context. release end HPKI テストカードから DER 符号化された公開鍵証明書データが取得できるので、 OpenSSL::X509::Certificate.new でインスタンス化できます。 上記の prog04.rb を実行すると下記のような出力が得られます。 Certificate: Data: Version: 3 (0x2) Serial Number: 13023 (0x32df) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forNonRepudiation Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit ) Modulus: 00:94:dd:09:40:f4:58:f9:0f:ec:3a:ea:e3:47:33: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:44:E9:20:05:4D:6D:C4:B7:FA:4B:F0:1B:C6:EA:C8:D6:5B:16:22:F4 DirName:/C =JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:02 X509v3 Subject Key Identifier: 9E:E5:71:59:1E:A7:FC:1E:4A:31:F8:7B:30:0B:E3:7F:05:3D:9A:40 X509v3 Key Usage: critical Non Repudiation X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-sign2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1 & 0$. "1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.1.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption 84:ae:95:45:5e:e7:64:8b:0c:6e:20:5f:9f:1f:0d:5c:ae:4a: # 中略 Certificate: Data: Version: 3 (0x2) Serial Number: 12927 (0x327f) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forAuthentication-forIndividual Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c6:f9:06:26:58:5e:11:b7:12:f2:8a:3e:97:0a: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:62:12:93:82:DE:3C:D7:FF:A8:D3:63:01:D3:01:6A:AE:6C:3B:C0:D4 DirName:/C=JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:03 X509v3 Subject Key Identifier: 45:2B:7B:B4:47:89:3D:6C:05:6D:82:4D:4C:C8:80:B8:B4:B0:89:81 X509v3 Key Usage: critical Digital Signature X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-auth2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1&0$." 1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.2.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption # 中略 それぞれのアプリケーションから正しく公開鍵証明書が取得できました。 電子認証ガイドライン によると、電子認証に使用する証明書は Issuer の CN(Common Name)が HPKI-01-*-forAuthentication-forIndividual であることが定められているため、 使用した HPKI テストカードでは、電子認証に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 であることが分かります。 また、電子署名に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 であることが分かりました。 最後に 以上で ガイドライン の「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」にある「PKI カードアプリケーションの検索」まで実装できました。 今後、次のステップである暗号計算を実装していきたいと思います。
アバター
こんにちは、開発本部の宮内です。今回、HPKI カードについて調査を行いましたので、それについて書きます。 JAHIS HPKI 対応 IC カードガイドライン Ver.3.0 を参考にして、HPKI テストカードから実際に公開鍵証明書を取得しました。 今後も HPKI について調査を続行していきたいと思います。 HPKI とは? HPKI とは厚生労働省が認める医療福祉関係資格(医師・薬剤師・看護師など 26 種類の保健医療福祉分野の国家資格と、院長・管理薬剤師など 5 種類の管理者資格)を認証することができる PKI です。 配布された HPKI カードには、ルート CA、中間 CA、証明書が格納されています。 このカードは、電子署名などに使用することができ、今後普及していけば、医療文書(処方箋や病院への紹介状など)を印刷、押印、送付するなどの非効率な業務をすることなく、すべてデジタル化することができるようになります。 また、電子認証用の証明書も含まれているため、認証・認可処理にも使用することができます。 今回、HPKI テストカードを用いて調査を行いました。 調査環境 macOS v10.13.5 ACR39-NTTCom Ruby v2.5.1 smartcard v0.5.6 HPKI テスト用カード PC/SC HPKI カードのような IC カードとやり取りを行うには、 PC/SC という API 仕様を使う必要があります。 PC/SC はもともと Windows 環境のみで利用可能でしたが、pcsc-lite という OSS 実装があり、現在では様々な UNIX like OS でも利用できます。 macOS の場合、 /System/Library/Frameworks/PCSC.framework/PCSC にライブラリが用意されており、特に準備する必要なく利用可能です。(2018 年 07 月現在) ただし、IC カードリーダーのドライバーをインストールする必要があります。 今回利用した ACR39-NTTCom は ダウンロードページ に macOS v10.13 に対応したドライバーが配布されていなかったため、IC カードリーダーのチップメーカーである ACS 社の ダウンロードページ からドライバーを入手しました。 smartcard 検証する際に使用した gem は smartcard です。 普通の rubygem と同じく gem install して利用します。 gem install smartcard IC カードリーダーを PC に接続し、 ruby -rsmartcard -e 'pp Smartcard::PCSC::Context.new.readers' を実行し、IC カードリーダー名が表示されれば接続成功です。 アプリケーション識別子の取得 実際に HPKI テストカードから情報を取得していきます。 ガイドライン の「附属書 A(参考)PKI カードアプリケーション利用のシーケンス」にある「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」を実装していきます。 引用 ガイドライン prog01.rb # prog01.rb require "smartcard" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first # SELECT コマンドで`E8 28 BD 08 0F`をパーシャル指定した DF を指定 apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response while response[ :status ] == 0x9000 # SELECT コマンドで次の DF を探す apdu = [ 0x00 , 0xA4 , 0x04 , 0x02 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response end ensure context. release end 上記のプログラムを実行すると、次のような出力が得られます。 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 status = 6A82 data = SELECT コマンドを発行すると BER-TLV で符号化された FCI(ファイル制御情報)が取得できます。 1つ目のデータから見ていきます。 1バイト目は 6F なので、このデータはファイル制御パラメタ及びファイル管理データの集合を表します。 引用 JIS X 6320-4 表 8-ファイル制御情報用の産業感共通利用テンプレート 2バイト目は 12 なので、後続するデータの長さが 18 バイトあることを表します。 3バイト目は 84 なので、データ要素が DF 名であることを表します。 引用 JIS X 6320-4 表 10-ファイル制御パラメタデータオブジェクト 4バイト目は 10 なので、後続するデータの長さが 16 バイトあることを表します。 5バイト目以降は、DF 名(= アプリケーション識別子)です。 2つ目のデータもデータ構造は同じなため省略します。 これで HPKI テストカードには、 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 という2つのアプリケーション識別子が含まれていることが分かります。 公開鍵証明書を取得する 前段にて HPKI テストカードに含まれているアプリケーション識別子が分かりましたので、次は公開鍵証明書を取得していきます。 ガイドライン の「A.3.2 証明書の読み出し」にあるコマンドの通りに APDU を発行しても、正しいデータは返ってきません。 これは、HPKI テストカードの EF 識別子が、ガイドラインに記載されている EF 識別子とは異なるためです。 HPKI カードは JIS X 6320 に準拠しているため、各種暗号情報オブジェクトへのパス情報を含んだ EF.OD が存在しています。 この EF.OD を使い公開鍵証明書へのパスを取得してから、公開鍵証明書を取得していきます。 引用 ガイドライン EF.OD を読み込む prog02.rb # prog02.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # EF.OD の読み出し apdu = [ 0x00 , 0xB0 , 0x91 , 0x00 , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) pp decode_asn1 response end ensure context. release end EF.OD を読み込むと DER 符号化されたデータが返ってきます。 これを OpenSSL::ANS1 モジュールで復号化すると、次に取得するべき EF 識別子が分かります。 EF.OD の ASN.1 定義は以下のようになっているため、タグが 4 であるデータを読み込めば良さそうです。 CIOChoice ::= CHOICE { privateKeys [0] PrivateKeys, publicKeys [1] PublicKeys, trustedPublicKeys [2] PublicKeys, secretKeys [3] SecretKeys, certificates [4] Certificates, trustedCertificates [5] Certificates, usefulCertificates [6] Certificates, dataContainerObjects [7] DataContainerObjects, authObjects [8] AuthObjects, } prog02.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8e0ef7b0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8e0ef7d8 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8e0ef800 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8d118df0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8d118e18 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8d118e40 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]>, # 中略 ] どちらのアプリケーションも 00 04 が EF.CD(証明書オブジェクト情報)の EF 識別子だということが分かります。 EF.CD を読み込む prog03.rb # prog03.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを apdu 選択する = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 04`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x04 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end pp decode_asn1 data: data end ensure context. release end prog03.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdf99aaf70 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdf99ab038 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdf99aafe8 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdf99aaf98 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05\x17" >]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdfa072308 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdfa072448 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdfa0723d0 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdfa072380 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05%" >]> ] # 中略 これで公開鍵証明書ファイルの EF 識別子が 00 16 であることが判明しました。 公開鍵証明書を読み込む prog04.rb # prog04.rb require "smartcard" require "openssl" context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 16`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x16 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end cert = OpenSSL::X509::Certificate . new (data. reverse_each . drop_while { | i | i == 0xFF }. reverse . pack ( "C*" )) puts cert. to_text end ensure context. release end HPKI テストカードから DER 符号化された公開鍵証明書データが取得できるので、 OpenSSL::X509::Certificate.new でインスタンス化できます。 上記の prog04.rb を実行すると下記のような出力が得られます。 Certificate: Data: Version: 3 (0x2) Serial Number: 13023 (0x32df) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forNonRepudiation Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit ) Modulus: 00:94:dd:09:40:f4:58:f9:0f:ec:3a:ea:e3:47:33: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:44:E9:20:05:4D:6D:C4:B7:FA:4B:F0:1B:C6:EA:C8:D6:5B:16:22:F4 DirName:/C =JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:02 X509v3 Subject Key Identifier: 9E:E5:71:59:1E:A7:FC:1E:4A:31:F8:7B:30:0B:E3:7F:05:3D:9A:40 X509v3 Key Usage: critical Non Repudiation X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-sign2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1 & 0$. "1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.1.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption 84:ae:95:45:5e:e7:64:8b:0c:6e:20:5f:9f:1f:0d:5c:ae:4a: # 中略 Certificate: Data: Version: 3 (0x2) Serial Number: 12927 (0x327f) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forAuthentication-forIndividual Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c6:f9:06:26:58:5e:11:b7:12:f2:8a:3e:97:0a: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:62:12:93:82:DE:3C:D7:FF:A8:D3:63:01:D3:01:6A:AE:6C:3B:C0:D4 DirName:/C=JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:03 X509v3 Subject Key Identifier: 45:2B:7B:B4:47:89:3D:6C:05:6D:82:4D:4C:C8:80:B8:B4:B0:89:81 X509v3 Key Usage: critical Digital Signature X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-auth2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1&0$." 1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.2.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption # 中略 それぞれのアプリケーションから正しく公開鍵証明書が取得できました。 電子認証ガイドライン によると、電子認証に使用する証明書は Issuer の CN(Common Name)が HPKI-01-*-forAuthentication-forIndividual であることが定められているため、 使用した HPKI テストカードでは、電子認証に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 であることが分かります。 また、電子署名に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 であることが分かりました。 最後に 以上で ガイドライン の「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」にある「PKI カードアプリケーションの検索」まで実装できました。 今後、次のステップである暗号計算を実装していきたいと思います。
アバター
こんにちは、開発本部の宮内です。今回、HPKI カードについて調査を行いましたので、それについて書きます。 JAHIS HPKI 対応 IC カードガイドライン Ver.3.0 を参考にして、HPKI テストカードから実際に公開鍵証明書を取得しました。 今後も HPKI について調査を続行していきたいと思います。 HPKI とは? HPKI とは厚生労働省が認める医療福祉関係資格(医師・薬剤師・看護師など 26 種類の保健医療福祉分野の国家資格と、院長・管理薬剤師など 5 種類の管理者資格)を認証することができる PKI です。 配布された HPKI カードには、ルート CA、中間 CA、証明書が格納されています。 このカードは、電子署名などに使用することができ、今後普及していけば、医療文書(処方箋や病院への紹介状など)を印刷、押印、送付するなどの非効率な業務をすることなく、すべてデジタル化することができるようになります。 また、電子認証用の証明書も含まれているため、認証・認可処理にも使用することができます。 今回、HPKI テストカードを用いて調査を行いました。 調査環境 macOS v10.13.5 ACR39-NTTCom Ruby v2.5.1 smartcard v0.5.6 HPKI テスト用カード PC/SC HPKI カードのような IC カードとやり取りを行うには、 PC/SC という API 仕様を使う必要があります。 PC/SC はもともと Windows 環境のみで利用可能でしたが、pcsc-lite という OSS 実装があり、現在では様々な UNIX like OS でも利用できます。 macOS の場合、 /System/Library/Frameworks/PCSC.framework/PCSC にライブラリが用意されており、特に準備する必要なく利用可能です。(2018 年 07 月現在) ただし、IC カードリーダーのドライバーをインストールする必要があります。 今回利用した ACR39-NTTCom は ダウンロードページ に macOS v10.13 に対応したドライバーが配布されていなかったため、IC カードリーダーのチップメーカーである ACS 社の ダウンロードページ からドライバーを入手しました。 smartcard 検証する際に使用した gem は smartcard です。 普通の rubygem と同じく gem install して利用します。 gem install smartcard IC カードリーダーを PC に接続し、 ruby -rsmartcard -e 'pp Smartcard::PCSC::Context.new.readers' を実行し、IC カードリーダー名が表示されれば接続成功です。 アプリケーション識別子の取得 実際に HPKI テストカードから情報を取得していきます。 ガイドライン の「附属書 A(参考)PKI カードアプリケーション利用のシーケンス」にある「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」を実装していきます。 引用 ガイドライン prog01.rb # prog01.rb require "smartcard" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first # SELECT コマンドで`E8 28 BD 08 0F`をパーシャル指定した DF を指定 apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response while response[ :status ] == 0x9000 # SELECT コマンドで次の DF を探す apdu = [ 0x00 , 0xA4 , 0x04 , 0x02 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response end ensure context. release end 上記のプログラムを実行すると、次のような出力が得られます。 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 status = 6A82 data = SELECT コマンドを発行すると BER-TLV で符号化された FCI(ファイル制御情報)が取得できます。 1つ目のデータから見ていきます。 1バイト目は 6F なので、このデータはファイル制御パラメタ及びファイル管理データの集合を表します。 引用 JIS X 6320-4 表 8-ファイル制御情報用の産業感共通利用テンプレート 2バイト目は 12 なので、後続するデータの長さが 18 バイトあることを表します。 3バイト目は 84 なので、データ要素が DF 名であることを表します。 引用 JIS X 6320-4 表 10-ファイル制御パラメタデータオブジェクト 4バイト目は 10 なので、後続するデータの長さが 16 バイトあることを表します。 5バイト目以降は、DF 名(= アプリケーション識別子)です。 2つ目のデータもデータ構造は同じなため省略します。 これで HPKI テストカードには、 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 という2つのアプリケーション識別子が含まれていることが分かります。 公開鍵証明書を取得する 前段にて HPKI テストカードに含まれているアプリケーション識別子が分かりましたので、次は公開鍵証明書を取得していきます。 ガイドライン の「A.3.2 証明書の読み出し」にあるコマンドの通りに APDU を発行しても、正しいデータは返ってきません。 これは、HPKI テストカードの EF 識別子が、ガイドラインに記載されている EF 識別子とは異なるためです。 HPKI カードは JIS X 6320 に準拠しているため、各種暗号情報オブジェクトへのパス情報を含んだ EF.OD が存在しています。 この EF.OD を使い公開鍵証明書へのパスを取得してから、公開鍵証明書を取得していきます。 引用 ガイドライン EF.OD を読み込む prog02.rb # prog02.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # EF.OD の読み出し apdu = [ 0x00 , 0xB0 , 0x91 , 0x00 , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) pp decode_asn1 response end ensure context. release end EF.OD を読み込むと DER 符号化されたデータが返ってきます。 これを OpenSSL::ANS1 モジュールで復号化すると、次に取得するべき EF 識別子が分かります。 EF.OD の ASN.1 定義は以下のようになっているため、タグが 4 であるデータを読み込めば良さそうです。 CIOChoice ::= CHOICE { privateKeys [0] PrivateKeys, publicKeys [1] PublicKeys, trustedPublicKeys [2] PublicKeys, secretKeys [3] SecretKeys, certificates [4] Certificates, trustedCertificates [5] Certificates, usefulCertificates [6] Certificates, dataContainerObjects [7] DataContainerObjects, authObjects [8] AuthObjects, } prog02.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8e0ef7b0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8e0ef7d8 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8e0ef800 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8d118df0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8d118e18 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8d118e40 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]>, # 中略 ] どちらのアプリケーションも 00 04 が EF.CD(証明書オブジェクト情報)の EF 識別子だということが分かります。 EF.CD を読み込む prog03.rb # prog03.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを apdu 選択する = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 04`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x04 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end pp decode_asn1 data: data end ensure context. release end prog03.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdf99aaf70 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdf99ab038 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdf99aafe8 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdf99aaf98 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05\x17" >]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdfa072308 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdfa072448 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdfa0723d0 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdfa072380 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05%" >]> ] # 中略 これで公開鍵証明書ファイルの EF 識別子が 00 16 であることが判明しました。 公開鍵証明書を読み込む prog04.rb # prog04.rb require "smartcard" require "openssl" context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 16`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x16 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end cert = OpenSSL::X509::Certificate . new (data. reverse_each . drop_while { | i | i == 0xFF }. reverse . pack ( "C*" )) puts cert. to_text end ensure context. release end HPKI テストカードから DER 符号化された公開鍵証明書データが取得できるので、 OpenSSL::X509::Certificate.new でインスタンス化できます。 上記の prog04.rb を実行すると下記のような出力が得られます。 Certificate: Data: Version: 3 (0x2) Serial Number: 13023 (0x32df) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forNonRepudiation Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit ) Modulus: 00:94:dd:09:40:f4:58:f9:0f:ec:3a:ea:e3:47:33: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:44:E9:20:05:4D:6D:C4:B7:FA:4B:F0:1B:C6:EA:C8:D6:5B:16:22:F4 DirName:/C =JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:02 X509v3 Subject Key Identifier: 9E:E5:71:59:1E:A7:FC:1E:4A:31:F8:7B:30:0B:E3:7F:05:3D:9A:40 X509v3 Key Usage: critical Non Repudiation X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-sign2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1 & 0$. "1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.1.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption 84:ae:95:45:5e:e7:64:8b:0c:6e:20:5f:9f:1f:0d:5c:ae:4a: # 中略 Certificate: Data: Version: 3 (0x2) Serial Number: 12927 (0x327f) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forAuthentication-forIndividual Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c6:f9:06:26:58:5e:11:b7:12:f2:8a:3e:97:0a: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:62:12:93:82:DE:3C:D7:FF:A8:D3:63:01:D3:01:6A:AE:6C:3B:C0:D4 DirName:/C=JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:03 X509v3 Subject Key Identifier: 45:2B:7B:B4:47:89:3D:6C:05:6D:82:4D:4C:C8:80:B8:B4:B0:89:81 X509v3 Key Usage: critical Digital Signature X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-auth2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1&0$." 1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.2.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption # 中略 それぞれのアプリケーションから正しく公開鍵証明書が取得できました。 電子認証ガイドライン によると、電子認証に使用する証明書は Issuer の CN(Common Name)が HPKI-01-*-forAuthentication-forIndividual であることが定められているため、 使用した HPKI テストカードでは、電子認証に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 であることが分かります。 また、電子署名に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 であることが分かりました。 最後に 以上で ガイドライン の「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」にある「PKI カードアプリケーションの検索」まで実装できました。 今後、次のステップである暗号計算を実装していきたいと思います。
アバター
こんにちは、開発本部の宮内です。今回、HPKI カードについて調査を行いましたので、それについて書きます。 JAHIS HPKI 対応 IC カードガイドライン Ver.3.0 を参考にして、HPKI テストカードから実際に公開鍵証明書を取得しました。 今後も HPKI について調査を続行していきたいと思います。 HPKI とは? HPKI とは厚生労働省が認める医療福祉関係資格(医師・薬剤師・看護師など 26 種類の保健医療福祉分野の国家資格と、院長・管理薬剤師など 5 種類の管理者資格)を認証することができる PKI です。 配布された HPKI カードには、ルート CA、中間 CA、証明書が格納されています。 このカードは、電子署名などに使用することができ、今後普及していけば、医療文書(処方箋や病院への紹介状など)を印刷、押印、送付するなどの非効率な業務をすることなく、すべてデジタル化することができるようになります。 また、電子認証用の証明書も含まれているため、認証・認可処理にも使用することができます。 今回、HPKI テストカードを用いて調査を行いました。 調査環境 macOS v10.13.5 ACR39-NTTCom Ruby v2.5.1 smartcard v0.5.6 HPKI テスト用カード PC/SC HPKI カードのような IC カードとやり取りを行うには、 PC/SC という API 仕様を使う必要があります。 PC/SC はもともと Windows 環境のみで利用可能でしたが、pcsc-lite という OSS 実装があり、現在では様々な UNIX like OS でも利用できます。 macOS の場合、 /System/Library/Frameworks/PCSC.framework/PCSC にライブラリが用意されており、特に準備する必要なく利用可能です。(2018 年 07 月現在) ただし、IC カードリーダーのドライバーをインストールする必要があります。 今回利用した ACR39-NTTCom は ダウンロードページ に macOS v10.13 に対応したドライバーが配布されていなかったため、IC カードリーダーのチップメーカーである ACS 社の ダウンロードページ からドライバーを入手しました。 smartcard 検証する際に使用した gem は smartcard です。 普通の rubygem と同じく gem install して利用します。 gem install smartcard IC カードリーダーを PC に接続し、 ruby -rsmartcard -e 'pp Smartcard::PCSC::Context.new.readers' を実行し、IC カードリーダー名が表示されれば接続成功です。 アプリケーション識別子の取得 実際に HPKI テストカードから情報を取得していきます。 ガイドライン の「附属書 A(参考)PKI カードアプリケーション利用のシーケンス」にある「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」を実装していきます。 引用 ガイドライン prog01.rb # prog01.rb require "smartcard" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first # SELECT コマンドで`E8 28 BD 08 0F`をパーシャル指定した DF を指定 apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response while response[ :status ] == 0x9000 # SELECT コマンドで次の DF を探す apdu = [ 0x00 , 0xA4 , 0x04 , 0x02 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response end ensure context. release end 上記のプログラムを実行すると、次のような出力が得られます。 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 status = 6A82 data = SELECT コマンドを発行すると BER-TLV で符号化された FCI(ファイル制御情報)が取得できます。 1つ目のデータから見ていきます。 1バイト目は 6F なので、このデータはファイル制御パラメタ及びファイル管理データの集合を表します。 引用 JIS X 6320-4 表 8-ファイル制御情報用の産業感共通利用テンプレート 2バイト目は 12 なので、後続するデータの長さが 18 バイトあることを表します。 3バイト目は 84 なので、データ要素が DF 名であることを表します。 引用 JIS X 6320-4 表 10-ファイル制御パラメタデータオブジェクト 4バイト目は 10 なので、後続するデータの長さが 16 バイトあることを表します。 5バイト目以降は、DF 名(= アプリケーション識別子)です。 2つ目のデータもデータ構造は同じなため省略します。 これで HPKI テストカードには、 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 という2つのアプリケーション識別子が含まれていることが分かります。 公開鍵証明書を取得する 前段にて HPKI テストカードに含まれているアプリケーション識別子が分かりましたので、次は公開鍵証明書を取得していきます。 ガイドライン の「A.3.2 証明書の読み出し」にあるコマンドの通りに APDU を発行しても、正しいデータは返ってきません。 これは、HPKI テストカードの EF 識別子が、ガイドラインに記載されている EF 識別子とは異なるためです。 HPKI カードは JIS X 6320 に準拠しているため、各種暗号情報オブジェクトへのパス情報を含んだ EF.OD が存在しています。 この EF.OD を使い公開鍵証明書へのパスを取得してから、公開鍵証明書を取得していきます。 引用 ガイドライン EF.OD を読み込む prog02.rb # prog02.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # EF.OD の読み出し apdu = [ 0x00 , 0xB0 , 0x91 , 0x00 , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) pp decode_asn1 response end ensure context. release end EF.OD を読み込むと DER 符号化されたデータが返ってきます。 これを OpenSSL::ANS1 モジュールで復号化すると、次に取得するべき EF 識別子が分かります。 EF.OD の ASN.1 定義は以下のようになっているため、タグが 4 であるデータを読み込めば良さそうです。 CIOChoice ::= CHOICE { privateKeys [0] PrivateKeys, publicKeys [1] PublicKeys, trustedPublicKeys [2] PublicKeys, secretKeys [3] SecretKeys, certificates [4] Certificates, trustedCertificates [5] Certificates, usefulCertificates [6] Certificates, dataContainerObjects [7] DataContainerObjects, authObjects [8] AuthObjects, } prog02.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8e0ef7b0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8e0ef7d8 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8e0ef800 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8d118df0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8d118e18 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8d118e40 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]>, # 中略 ] どちらのアプリケーションも 00 04 が EF.CD(証明書オブジェクト情報)の EF 識別子だということが分かります。 EF.CD を読み込む prog03.rb # prog03.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを apdu 選択する = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 04`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x04 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end pp decode_asn1 data: data end ensure context. release end prog03.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdf99aaf70 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdf99ab038 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdf99aafe8 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdf99aaf98 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05\x17" >]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdfa072308 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdfa072448 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdfa0723d0 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdfa072380 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05%" >]> ] # 中略 これで公開鍵証明書ファイルの EF 識別子が 00 16 であることが判明しました。 公開鍵証明書を読み込む prog04.rb # prog04.rb require "smartcard" require "openssl" context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 16`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x16 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end cert = OpenSSL::X509::Certificate . new (data. reverse_each . drop_while { | i | i == 0xFF }. reverse . pack ( "C*" )) puts cert. to_text end ensure context. release end HPKI テストカードから DER 符号化された公開鍵証明書データが取得できるので、 OpenSSL::X509::Certificate.new でインスタンス化できます。 上記の prog04.rb を実行すると下記のような出力が得られます。 Certificate: Data: Version: 3 (0x2) Serial Number: 13023 (0x32df) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forNonRepudiation Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit ) Modulus: 00:94:dd:09:40:f4:58:f9:0f:ec:3a:ea:e3:47:33: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:44:E9:20:05:4D:6D:C4:B7:FA:4B:F0:1B:C6:EA:C8:D6:5B:16:22:F4 DirName:/C =JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:02 X509v3 Subject Key Identifier: 9E:E5:71:59:1E:A7:FC:1E:4A:31:F8:7B:30:0B:E3:7F:05:3D:9A:40 X509v3 Key Usage: critical Non Repudiation X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-sign2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1 & 0$. "1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.1.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption 84:ae:95:45:5e:e7:64:8b:0c:6e:20:5f:9f:1f:0d:5c:ae:4a: # 中略 Certificate: Data: Version: 3 (0x2) Serial Number: 12927 (0x327f) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forAuthentication-forIndividual Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c6:f9:06:26:58:5e:11:b7:12:f2:8a:3e:97:0a: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:62:12:93:82:DE:3C:D7:FF:A8:D3:63:01:D3:01:6A:AE:6C:3B:C0:D4 DirName:/C=JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:03 X509v3 Subject Key Identifier: 45:2B:7B:B4:47:89:3D:6C:05:6D:82:4D:4C:C8:80:B8:B4:B0:89:81 X509v3 Key Usage: critical Digital Signature X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-auth2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1&0$." 1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.2.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption # 中略 それぞれのアプリケーションから正しく公開鍵証明書が取得できました。 電子認証ガイドライン によると、電子認証に使用する証明書は Issuer の CN(Common Name)が HPKI-01-*-forAuthentication-forIndividual であることが定められているため、 使用した HPKI テストカードでは、電子認証に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 であることが分かります。 また、電子署名に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 であることが分かりました。 最後に 以上で ガイドライン の「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」にある「PKI カードアプリケーションの検索」まで実装できました。 今後、次のステップである暗号計算を実装していきたいと思います。
アバター
こんにちは、開発本部の宮内です。今回、HPKI カードについて調査を行いましたので、それについて書きます。 JAHIS HPKI 対応 IC カードガイドライン Ver.3.0 を参考にして、HPKI テストカードから実際に公開鍵証明書を取得しました。 今後も HPKI について調査を続行していきたいと思います。 HPKI とは? HPKI とは厚生労働省が認める医療福祉関係資格(医師・薬剤師・看護師など 26 種類の保健医療福祉分野の国家資格と、院長・管理薬剤師など 5 種類の管理者資格)を認証することができる PKI です。 配布された HPKI カードには、ルート CA、中間 CA、証明書が格納されています。 このカードは、電子署名などに使用することができ、今後普及していけば、医療文書(処方箋や病院への紹介状など)を印刷、押印、送付するなどの非効率な業務をすることなく、すべてデジタル化することができるようになります。 また、電子認証用の証明書も含まれているため、認証・認可処理にも使用することができます。 今回、HPKI テストカードを用いて調査を行いました。 調査環境 macOS v10.13.5 ACR39-NTTCom Ruby v2.5.1 smartcard v0.5.6 HPKI テスト用カード PC/SC HPKI カードのような IC カードとやり取りを行うには、 PC/SC という API 仕様を使う必要があります。 PC/SC はもともと Windows 環境のみで利用可能でしたが、pcsc-lite という OSS 実装があり、現在では様々な UNIX like OS でも利用できます。 macOS の場合、 /System/Library/Frameworks/PCSC.framework/PCSC にライブラリが用意されており、特に準備する必要なく利用可能です。(2018 年 07 月現在) ただし、IC カードリーダーのドライバーをインストールする必要があります。 今回利用した ACR39-NTTCom は ダウンロードページ に macOS v10.13 に対応したドライバーが配布されていなかったため、IC カードリーダーのチップメーカーである ACS 社の ダウンロードページ からドライバーを入手しました。 smartcard 検証する際に使用した gem は smartcard です。 普通の rubygem と同じく gem install して利用します。 gem install smartcard IC カードリーダーを PC に接続し、 ruby -rsmartcard -e 'pp Smartcard::PCSC::Context.new.readers' を実行し、IC カードリーダー名が表示されれば接続成功です。 アプリケーション識別子の取得 実際に HPKI テストカードから情報を取得していきます。 ガイドライン の「附属書 A(参考)PKI カードアプリケーション利用のシーケンス」にある「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」を実装していきます。 引用 ガイドライン prog01.rb # prog01.rb require "smartcard" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first # SELECT コマンドで`E8 28 BD 08 0F`をパーシャル指定した DF を指定 apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response while response[ :status ] == 0x9000 # SELECT コマンドで次の DF を探す apdu = [ 0x00 , 0xA4 , 0x04 , 0x02 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response end ensure context. release end 上記のプログラムを実行すると、次のような出力が得られます。 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 status = 6A82 data = SELECT コマンドを発行すると BER-TLV で符号化された FCI(ファイル制御情報)が取得できます。 1つ目のデータから見ていきます。 1バイト目は 6F なので、このデータはファイル制御パラメタ及びファイル管理データの集合を表します。 引用 JIS X 6320-4 表 8-ファイル制御情報用の産業感共通利用テンプレート 2バイト目は 12 なので、後続するデータの長さが 18 バイトあることを表します。 3バイト目は 84 なので、データ要素が DF 名であることを表します。 引用 JIS X 6320-4 表 10-ファイル制御パラメタデータオブジェクト 4バイト目は 10 なので、後続するデータの長さが 16 バイトあることを表します。 5バイト目以降は、DF 名(= アプリケーション識別子)です。 2つ目のデータもデータ構造は同じなため省略します。 これで HPKI テストカードには、 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 という2つのアプリケーション識別子が含まれていることが分かります。 公開鍵証明書を取得する 前段にて HPKI テストカードに含まれているアプリケーション識別子が分かりましたので、次は公開鍵証明書を取得していきます。 ガイドライン の「A.3.2 証明書の読み出し」にあるコマンドの通りに APDU を発行しても、正しいデータは返ってきません。 これは、HPKI テストカードの EF 識別子が、ガイドラインに記載されている EF 識別子とは異なるためです。 HPKI カードは JIS X 6320 に準拠しているため、各種暗号情報オブジェクトへのパス情報を含んだ EF.OD が存在しています。 この EF.OD を使い公開鍵証明書へのパスを取得してから、公開鍵証明書を取得していきます。 引用 ガイドライン EF.OD を読み込む prog02.rb # prog02.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # EF.OD の読み出し apdu = [ 0x00 , 0xB0 , 0x91 , 0x00 , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) pp decode_asn1 response end ensure context. release end EF.OD を読み込むと DER 符号化されたデータが返ってきます。 これを OpenSSL::ANS1 モジュールで復号化すると、次に取得するべき EF 識別子が分かります。 EF.OD の ASN.1 定義は以下のようになっているため、タグが 4 であるデータを読み込めば良さそうです。 CIOChoice ::= CHOICE { privateKeys [0] PrivateKeys, publicKeys [1] PublicKeys, trustedPublicKeys [2] PublicKeys, secretKeys [3] SecretKeys, certificates [4] Certificates, trustedCertificates [5] Certificates, usefulCertificates [6] Certificates, dataContainerObjects [7] DataContainerObjects, authObjects [8] AuthObjects, } prog02.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8e0ef7b0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8e0ef7d8 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8e0ef800 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8d118df0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8d118e18 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8d118e40 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]>, # 中略 ] どちらのアプリケーションも 00 04 が EF.CD(証明書オブジェクト情報)の EF 識別子だということが分かります。 EF.CD を読み込む prog03.rb # prog03.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを apdu 選択する = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 04`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x04 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end pp decode_asn1 data: data end ensure context. release end prog03.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdf99aaf70 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdf99ab038 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdf99aafe8 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdf99aaf98 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05\x17" >]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdfa072308 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdfa072448 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdfa0723d0 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdfa072380 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05%" >]> ] # 中略 これで公開鍵証明書ファイルの EF 識別子が 00 16 であることが判明しました。 公開鍵証明書を読み込む prog04.rb # prog04.rb require "smartcard" require "openssl" context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 16`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x16 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end cert = OpenSSL::X509::Certificate . new (data. reverse_each . drop_while { | i | i == 0xFF }. reverse . pack ( "C*" )) puts cert. to_text end ensure context. release end HPKI テストカードから DER 符号化された公開鍵証明書データが取得できるので、 OpenSSL::X509::Certificate.new でインスタンス化できます。 上記の prog04.rb を実行すると下記のような出力が得られます。 Certificate: Data: Version: 3 (0x2) Serial Number: 13023 (0x32df) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forNonRepudiation Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit ) Modulus: 00:94:dd:09:40:f4:58:f9:0f:ec:3a:ea:e3:47:33: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:44:E9:20:05:4D:6D:C4:B7:FA:4B:F0:1B:C6:EA:C8:D6:5B:16:22:F4 DirName:/C =JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:02 X509v3 Subject Key Identifier: 9E:E5:71:59:1E:A7:FC:1E:4A:31:F8:7B:30:0B:E3:7F:05:3D:9A:40 X509v3 Key Usage: critical Non Repudiation X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-sign2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1 & 0$. "1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.1.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption 84:ae:95:45:5e:e7:64:8b:0c:6e:20:5f:9f:1f:0d:5c:ae:4a: # 中略 Certificate: Data: Version: 3 (0x2) Serial Number: 12927 (0x327f) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forAuthentication-forIndividual Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c6:f9:06:26:58:5e:11:b7:12:f2:8a:3e:97:0a: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:62:12:93:82:DE:3C:D7:FF:A8:D3:63:01:D3:01:6A:AE:6C:3B:C0:D4 DirName:/C=JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:03 X509v3 Subject Key Identifier: 45:2B:7B:B4:47:89:3D:6C:05:6D:82:4D:4C:C8:80:B8:B4:B0:89:81 X509v3 Key Usage: critical Digital Signature X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-auth2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1&0$." 1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.2.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption # 中略 それぞれのアプリケーションから正しく公開鍵証明書が取得できました。 電子認証ガイドライン によると、電子認証に使用する証明書は Issuer の CN(Common Name)が HPKI-01-*-forAuthentication-forIndividual であることが定められているため、 使用した HPKI テストカードでは、電子認証に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 であることが分かります。 また、電子署名に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 であることが分かりました。 最後に 以上で ガイドライン の「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」にある「PKI カードアプリケーションの検索」まで実装できました。 今後、次のステップである暗号計算を実装していきたいと思います。
アバター
こんにちは、開発本部の宮内です。今回、HPKI カードについて調査を行いましたので、それについて書きます。 JAHIS HPKI 対応 IC カードガイドライン Ver.3.0 を参考にして、HPKI テストカードから実際に公開鍵証明書を取得しました。 今後も HPKI について調査を続行していきたいと思います。 HPKI とは? HPKI とは厚生労働省が認める医療福祉関係資格(医師・薬剤師・看護師など 26 種類の保健医療福祉分野の国家資格と、院長・管理薬剤師など 5 種類の管理者資格)を認証することができる PKI です。 配布された HPKI カードには、ルート CA、中間 CA、証明書が格納されています。 このカードは、電子署名などに使用することができ、今後普及していけば、医療文書(処方箋や病院への紹介状など)を印刷、押印、送付するなどの非効率な業務をすることなく、すべてデジタル化することができるようになります。 また、電子認証用の証明書も含まれているため、認証・認可処理にも使用することができます。 今回、HPKI テストカードを用いて調査を行いました。 調査環境 macOS v10.13.5 ACR39-NTTCom Ruby v2.5.1 smartcard v0.5.6 HPKI テスト用カード PC/SC HPKI カードのような IC カードとやり取りを行うには、 PC/SC という API 仕様を使う必要があります。 PC/SC はもともと Windows 環境のみで利用可能でしたが、pcsc-lite という OSS 実装があり、現在では様々な UNIX like OS でも利用できます。 macOS の場合、 /System/Library/Frameworks/PCSC.framework/PCSC にライブラリが用意されており、特に準備する必要なく利用可能です。(2018 年 07 月現在) ただし、IC カードリーダーのドライバーをインストールする必要があります。 今回利用した ACR39-NTTCom は ダウンロードページ に macOS v10.13 に対応したドライバーが配布されていなかったため、IC カードリーダーのチップメーカーである ACS 社の ダウンロードページ からドライバーを入手しました。 smartcard 検証する際に使用した gem は smartcard です。 普通の rubygem と同じく gem install して利用します。 gem install smartcard IC カードリーダーを PC に接続し、 ruby -rsmartcard -e 'pp Smartcard::PCSC::Context.new.readers' を実行し、IC カードリーダー名が表示されれば接続成功です。 アプリケーション識別子の取得 実際に HPKI テストカードから情報を取得していきます。 ガイドライン の「附属書 A(参考)PKI カードアプリケーション利用のシーケンス」にある「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」を実装していきます。 引用 ガイドライン prog01.rb # prog01.rb require "smartcard" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first # SELECT コマンドで`E8 28 BD 08 0F`をパーシャル指定した DF を指定 apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response while response[ :status ] == 0x9000 # SELECT コマンドで次の DF を探す apdu = [ 0x00 , 0xA4 , 0x04 , 0x02 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response end ensure context. release end 上記のプログラムを実行すると、次のような出力が得られます。 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 status = 6A82 data = SELECT コマンドを発行すると BER-TLV で符号化された FCI(ファイル制御情報)が取得できます。 1つ目のデータから見ていきます。 1バイト目は 6F なので、このデータはファイル制御パラメタ及びファイル管理データの集合を表します。 引用 JIS X 6320-4 表 8-ファイル制御情報用の産業感共通利用テンプレート 2バイト目は 12 なので、後続するデータの長さが 18 バイトあることを表します。 3バイト目は 84 なので、データ要素が DF 名であることを表します。 引用 JIS X 6320-4 表 10-ファイル制御パラメタデータオブジェクト 4バイト目は 10 なので、後続するデータの長さが 16 バイトあることを表します。 5バイト目以降は、DF 名(= アプリケーション識別子)です。 2つ目のデータもデータ構造は同じなため省略します。 これで HPKI テストカードには、 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 という2つのアプリケーション識別子が含まれていることが分かります。 公開鍵証明書を取得する 前段にて HPKI テストカードに含まれているアプリケーション識別子が分かりましたので、次は公開鍵証明書を取得していきます。 ガイドライン の「A.3.2 証明書の読み出し」にあるコマンドの通りに APDU を発行しても、正しいデータは返ってきません。 これは、HPKI テストカードの EF 識別子が、ガイドラインに記載されている EF 識別子とは異なるためです。 HPKI カードは JIS X 6320 に準拠しているため、各種暗号情報オブジェクトへのパス情報を含んだ EF.OD が存在しています。 この EF.OD を使い公開鍵証明書へのパスを取得してから、公開鍵証明書を取得していきます。 引用 ガイドライン EF.OD を読み込む prog02.rb # prog02.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # EF.OD の読み出し apdu = [ 0x00 , 0xB0 , 0x91 , 0x00 , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) pp decode_asn1 response end ensure context. release end EF.OD を読み込むと DER 符号化されたデータが返ってきます。 これを OpenSSL::ANS1 モジュールで復号化すると、次に取得するべき EF 識別子が分かります。 EF.OD の ASN.1 定義は以下のようになっているため、タグが 4 であるデータを読み込めば良さそうです。 CIOChoice ::= CHOICE { privateKeys [0] PrivateKeys, publicKeys [1] PublicKeys, trustedPublicKeys [2] PublicKeys, secretKeys [3] SecretKeys, certificates [4] Certificates, trustedCertificates [5] Certificates, usefulCertificates [6] Certificates, dataContainerObjects [7] DataContainerObjects, authObjects [8] AuthObjects, } prog02.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8e0ef7b0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8e0ef7d8 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8e0ef800 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8d118df0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8d118e18 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8d118e40 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]>, # 中略 ] どちらのアプリケーションも 00 04 が EF.CD(証明書オブジェクト情報)の EF 識別子だということが分かります。 EF.CD を読み込む prog03.rb # prog03.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを apdu 選択する = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 04`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x04 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end pp decode_asn1 data: data end ensure context. release end prog03.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdf99aaf70 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdf99ab038 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdf99aafe8 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdf99aaf98 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05\x17" >]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdfa072308 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdfa072448 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdfa0723d0 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdfa072380 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05%" >]> ] # 中略 これで公開鍵証明書ファイルの EF 識別子が 00 16 であることが判明しました。 公開鍵証明書を読み込む prog04.rb # prog04.rb require "smartcard" require "openssl" context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 16`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x16 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end cert = OpenSSL::X509::Certificate . new (data. reverse_each . drop_while { | i | i == 0xFF }. reverse . pack ( "C*" )) puts cert. to_text end ensure context. release end HPKI テストカードから DER 符号化された公開鍵証明書データが取得できるので、 OpenSSL::X509::Certificate.new でインスタンス化できます。 上記の prog04.rb を実行すると下記のような出力が得られます。 Certificate: Data: Version: 3 (0x2) Serial Number: 13023 (0x32df) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forNonRepudiation Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit ) Modulus: 00:94:dd:09:40:f4:58:f9:0f:ec:3a:ea:e3:47:33: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:44:E9:20:05:4D:6D:C4:B7:FA:4B:F0:1B:C6:EA:C8:D6:5B:16:22:F4 DirName:/C =JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:02 X509v3 Subject Key Identifier: 9E:E5:71:59:1E:A7:FC:1E:4A:31:F8:7B:30:0B:E3:7F:05:3D:9A:40 X509v3 Key Usage: critical Non Repudiation X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-sign2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1 & 0$. "1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.1.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption 84:ae:95:45:5e:e7:64:8b:0c:6e:20:5f:9f:1f:0d:5c:ae:4a: # 中略 Certificate: Data: Version: 3 (0x2) Serial Number: 12927 (0x327f) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forAuthentication-forIndividual Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c6:f9:06:26:58:5e:11:b7:12:f2:8a:3e:97:0a: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:62:12:93:82:DE:3C:D7:FF:A8:D3:63:01:D3:01:6A:AE:6C:3B:C0:D4 DirName:/C=JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:03 X509v3 Subject Key Identifier: 45:2B:7B:B4:47:89:3D:6C:05:6D:82:4D:4C:C8:80:B8:B4:B0:89:81 X509v3 Key Usage: critical Digital Signature X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-auth2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1&0$." 1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.2.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption # 中略 それぞれのアプリケーションから正しく公開鍵証明書が取得できました。 電子認証ガイドライン によると、電子認証に使用する証明書は Issuer の CN(Common Name)が HPKI-01-*-forAuthentication-forIndividual であることが定められているため、 使用した HPKI テストカードでは、電子認証に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 であることが分かります。 また、電子署名に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 であることが分かりました。 最後に 以上で ガイドライン の「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」にある「PKI カードアプリケーションの検索」まで実装できました。 今後、次のステップである暗号計算を実装していきたいと思います。
アバター
こんにちは、開発本部の宮内です。今回、HPKI カードについて調査を行いましたので、それについて書きます。 JAHIS HPKI 対応 IC カードガイドライン Ver.3.0 を参考にして、HPKI テストカードから実際に公開鍵証明書を取得しました。 今後も HPKI について調査を続行していきたいと思います。 HPKI とは? HPKI とは厚生労働省が認める医療福祉関係資格(医師・薬剤師・看護師など 26 種類の保健医療福祉分野の国家資格と、院長・管理薬剤師など 5 種類の管理者資格)を認証することができる PKI です。 配布された HPKI カードには、ルート CA、中間 CA、証明書が格納されています。 このカードは、電子署名などに使用することができ、今後普及していけば、医療文書(処方箋や病院への紹介状など)を印刷、押印、送付するなどの非効率な業務をすることなく、すべてデジタル化することができるようになります。 また、電子認証用の証明書も含まれているため、認証・認可処理にも使用することができます。 今回、HPKI テストカードを用いて調査を行いました。 調査環境 macOS v10.13.5 ACR39-NTTCom Ruby v2.5.1 smartcard v0.5.6 HPKI テスト用カード PC/SC HPKI カードのような IC カードとやり取りを行うには、 PC/SC という API 仕様を使う必要があります。 PC/SC はもともと Windows 環境のみで利用可能でしたが、pcsc-lite という OSS 実装があり、現在では様々な UNIX like OS でも利用できます。 macOS の場合、 /System/Library/Frameworks/PCSC.framework/PCSC にライブラリが用意されており、特に準備する必要なく利用可能です。(2018 年 07 月現在) ただし、IC カードリーダーのドライバーをインストールする必要があります。 今回利用した ACR39-NTTCom は ダウンロードページ に macOS v10.13 に対応したドライバーが配布されていなかったため、IC カードリーダーのチップメーカーである ACS 社の ダウンロードページ からドライバーを入手しました。 smartcard 検証する際に使用した gem は smartcard です。 普通の rubygem と同じく gem install して利用します。 gem install smartcard IC カードリーダーを PC に接続し、 ruby -rsmartcard -e 'pp Smartcard::PCSC::Context.new.readers' を実行し、IC カードリーダー名が表示されれば接続成功です。 アプリケーション識別子の取得 実際に HPKI テストカードから情報を取得していきます。 ガイドライン の「附属書 A(参考)PKI カードアプリケーション利用のシーケンス」にある「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」を実装していきます。 引用 ガイドライン prog01.rb # prog01.rb require "smartcard" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first # SELECT コマンドで`E8 28 BD 08 0F`をパーシャル指定した DF を指定 apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response while response[ :status ] == 0x9000 # SELECT コマンドで次の DF を探す apdu = [ 0x00 , 0xA4 , 0x04 , 0x02 , 0x05 , 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) puts_response response end ensure context. release end 上記のプログラムを実行すると、次のような出力が得られます。 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 status = 9000 data = 6F 12 84 10 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 status = 6A82 data = SELECT コマンドを発行すると BER-TLV で符号化された FCI(ファイル制御情報)が取得できます。 1つ目のデータから見ていきます。 1バイト目は 6F なので、このデータはファイル制御パラメタ及びファイル管理データの集合を表します。 引用 JIS X 6320-4 表 8-ファイル制御情報用の産業感共通利用テンプレート 2バイト目は 12 なので、後続するデータの長さが 18 バイトあることを表します。 3バイト目は 84 なので、データ要素が DF 名であることを表します。 引用 JIS X 6320-4 表 10-ファイル制御パラメタデータオブジェクト 4バイト目は 10 なので、後続するデータの長さが 16 バイトあることを表します。 5バイト目以降は、DF 名(= アプリケーション識別子)です。 2つ目のデータもデータ構造は同じなため省略します。 これで HPKI テストカードには、 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 という2つのアプリケーション識別子が含まれていることが分かります。 公開鍵証明書を取得する 前段にて HPKI テストカードに含まれているアプリケーション識別子が分かりましたので、次は公開鍵証明書を取得していきます。 ガイドライン の「A.3.2 証明書の読み出し」にあるコマンドの通りに APDU を発行しても、正しいデータは返ってきません。 これは、HPKI テストカードの EF 識別子が、ガイドラインに記載されている EF 識別子とは異なるためです。 HPKI カードは JIS X 6320 に準拠しているため、各種暗号情報オブジェクトへのパス情報を含んだ EF.OD が存在しています。 この EF.OD を使い公開鍵証明書へのパスを取得してから、公開鍵証明書を取得していきます。 引用 ガイドライン EF.OD を読み込む prog02.rb # prog02.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # EF.OD の読み出し apdu = [ 0x00 , 0xB0 , 0x91 , 0x00 , 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) pp decode_asn1 response end ensure context. release end EF.OD を読み込むと DER 符号化されたデータが返ってきます。 これを OpenSSL::ANS1 モジュールで復号化すると、次に取得するべき EF 識別子が分かります。 EF.OD の ASN.1 定義は以下のようになっているため、タグが 4 であるデータを読み込めば良さそうです。 CIOChoice ::= CHOICE { privateKeys [0] PrivateKeys, publicKeys [1] PublicKeys, trustedPublicKeys [2] PublicKeys, secretKeys [3] SecretKeys, certificates [4] Certificates, trustedCertificates [5] Certificates, usefulCertificates [6] Certificates, dataContainerObjects [7] DataContainerObjects, authObjects [8] AuthObjects, } prog02.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8e0ef7b0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8e0ef7d8 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8e0ef800 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::ASN1Data:0x00007f8b8d118df0 @indefinite_length= false , @tag=4, @tag_class=:CONTEXT_SPECIFIC, @value= [#<OpenSSL::ASN1::Sequence:0x00007f8b8d118e18 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007f8b8d118e40 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x04" >]>]>, # 中略 ] どちらのアプリケーションも 00 04 が EF.CD(証明書オブジェクト情報)の EF 識別子だということが分かります。 EF.CD を読み込む prog03.rb # prog03.rb require "smartcard" require "openssl" def puts_response ( response ) puts "status = %04X" % response[ :status ] puts "data = %s" % response[ :data ]. map { | i | "%02X" % i }. join ( " " ) end def decode_asn1 ( response ) data = response[ :data ]. reverse_each . drop_while { | i | i == 0xFF }. reverse return if data. empty? OpenSSL::ASN1 . decode_all data. pack ( "C*" ) end context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを apdu 選択する = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 04`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x04 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end pp decode_asn1 data: data end ensure context. release end prog03.rb を実行して実際に得られたデータ [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdf99aaf70 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdf99ab038 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdf99aafe8 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdf99aaf98 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05\x17" >]> # 中略 ] [ # 中略 #<OpenSSL::ASN1::Sequence:0x00007ffdfa072308 @indefinite_length= false , @tag=16, @tag_class=:UNIVERSAL, @tagging=nil, @value= [#<OpenSSL::ASN1::OctetString:0x00007ffdfa072448 @indefinite_length= false , @tag=4, @tag_class=:UNIVERSAL, @tagging=nil, @value= "\x00\x16" >, #<OpenSSL::ASN1::Integer:0x00007ffdfa0723d0 @indefinite_length= false , @tag=2, @tag_class=:UNIVERSAL, @tagging=nil, @value=#<OpenSSL::BN 0>>, #<OpenSSL::ASN1::ASN1Data:0x00007ffdfa072380 @indefinite_length= false , @tag=0, @tag_class=:CONTEXT_SPECIFIC, @value= "\x05%" >]> ] # 中略 これで公開鍵証明書ファイルの EF 識別子が 00 16 であることが判明しました。 公開鍵証明書を読み込む prog04.rb # prog04.rb require "smartcard" require "openssl" context = Smartcard::PCSC::Context . new begin card = context. card context. readers . first [ [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ], [ 0xE8 , 0x28 , 0xBD , 0x08 , 0x0F , 0xA0 , 0x00 , 0x00 , 0x03 , 0x91 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ] ]. each do | aid | # SELECT コマンドでアプリケーションを選択する apdu = [ 0x00 , 0xA4 , 0x04 , 0x00 , 0x10 , *aid, 0x00 ] card. transmit apdu. pack ( "C*" ) # SELECT コマンドで EF 識別子`00 16`を選択する apdu = [ 0x00 , 0xA4 , 0x02 , 0x0C , 0x02 , 0x00 , 0x16 ] card. transmit apdu. pack ( "C*" ) # READ BINARY コマンドでファイルを読み込む data = [] offset = 0 loop do apdu = [ 0x00 , 0xB0 , (offset & 0x7FFF ) >> 8 , (offset & 0x00FF ), 0x00 ] response = card. transmit apdu. pack ( "C*" ) response = Smartcard::Iso::IsoCardMixin . deserialize_response response. unpack ( "C*" ) data. concat response[ :data ] break if response[ :data ]. all? { | e | e == 0xFF } break unless response[ :status ] == 0x9000 offset += response[ :data ]. size end cert = OpenSSL::X509::Certificate . new (data. reverse_each . drop_while { | i | i == 0xFF }. reverse . pack ( "C*" )) puts cert. to_text end ensure context. release end HPKI テストカードから DER 符号化された公開鍵証明書データが取得できるので、 OpenSSL::X509::Certificate.new でインスタンス化できます。 上記の prog04.rb を実行すると下記のような出力が得られます。 Certificate: Data: Version: 3 (0x2) Serial Number: 13023 (0x32df) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forNonRepudiation Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit ) Modulus: 00:94:dd:09:40:f4:58:f9:0f:ec:3a:ea:e3:47:33: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:44:E9:20:05:4D:6D:C4:B7:FA:4B:F0:1B:C6:EA:C8:D6:5B:16:22:F4 DirName:/C =JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:02 X509v3 Subject Key Identifier: 9E:E5:71:59:1E:A7:FC:1E:4A:31:F8:7B:30:0B:E3:7F:05:3D:9A:40 X509v3 Key Usage: critical Non Repudiation X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-sign2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1 & 0$. "1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.1.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption 84:ae:95:45:5e:e7:64:8b:0c:6e:20:5f:9f:1f:0d:5c:ae:4a: # 中略 Certificate: Data: Version: 3 (0x2) Serial Number: 12927 (0x327f) Signature Algorithm: sha256WithRSAEncryption Issuer: C=JP, O=Japan Medical Association, OU=Digital Certificate Center, CN=HPKI-01-HPKI_JV2-forAuthentication-forIndividual Validity Not Before: Aug 15 15:00:00 2017 GMT Not After : Aug 15 14:59:59 2018 GMT Subject: C=JP, CN=JMACombi20413/serialNumber=TESTC20413 Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:c6:f9:06:26:58:5e:11:b7:12:f2:8a:3e:97:0a: # 中略 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:62:12:93:82:DE:3C:D7:FF:A8:D3:63:01:D3:01:6A:AE:6C:3B:C0:D4 DirName:/C=JP/O=Ministry of Health, Labour and Welfare/OU=Director-General for Policy Planning and Evaluation/OU=MHLW HPKI Root CA V2 serial:03 X509v3 Subject Key Identifier: 45:2B:7B:B4:47:89:3D:6C:05:6D:82:4D:4C:C8:80:B8:B4:B0:89:81 X509v3 Key Usage: critical Digital Signature X509v3 CRL Distribution Points: Full Name: URI:https://crl.pki.med.or.jp/repository/crl/crl-auth2.crl X509v3 Subject Directory Attributes: 0402..(..B..1(1&0$." 1 ... *.............Medical Doctor X509v3 Certificate Policies: critical Policy: 1.2.392.100495.1.5.1.2.0.1 CPS: https://www.pki.med.or.jp/certpolicy/ Signature Algorithm: sha256WithRSAEncryption # 中略 それぞれのアプリケーションから正しく公開鍵証明書が取得できました。 電子認証ガイドライン によると、電子認証に使用する証明書は Issuer の CN(Common Name)が HPKI-01-*-forAuthentication-forIndividual であることが定められているため、 使用した HPKI テストカードでは、電子認証に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 02 であることが分かります。 また、電子署名に使用するアプリケーション識別子は E8 28 BD 08 0F A0 00 00 03 91 00 00 00 00 00 01 であることが分かりました。 最後に 以上で ガイドライン の「A.2.2 JIS X 6320-15 に従った PKI カードアプリケーションの検索と利用」にある「PKI カードアプリケーションの検索」まで実装できました。 今後、次のステップである暗号計算を実装していきたいと思います。
アバター
こんにちは、開発本部の竹内です。最近子どものプリンセスへの強い憧れに若干引いております。 さて先日、TechLunch という社内勉強会で「電子レセプト」について話しましたので、こちらでも簡単に紹介させていただきます。 レセプトとは ところで、みなさまは「レセプト」についてご存知でしょうか?私はメドレーに入社するまで知りませんでした。 レセプトとは医療機関が支払基金へ診療報酬を請求するための明細書情報のことです。 と言っても、初めて聞かれる方もいらっしゃると思いますので、医療機関におけるお金の流れとともに簡単に説明します。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) 医療機関は「診療」の対価として、被保険者等(≒ 患者)からお金を受け取るわけですが、被保険者の加入する保険や公費によってその額は変わります。負担割合が 3 割の場合、残りの 7 割を被保険者が加入する保険組合などへ請求する必要があります。 この保険組合などへの請求を取りまとめ、内容を審査しているのが支払基金と呼ばれる組織で、医療機関は月に一度、前月の患者ごとの診療点数を計算し「レセプト」として支払基金に提出することになります。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) レセプトには、請求する診療点数のほか、医療機関の情報、被保険者の情報(氏名などの基本情報、加入している保険者情報)、診療行為や傷病名に関する情報などが含まれています。 「レセプト」には紙と電子データとありますが、現在は原則として電子レセプトを提出することが求められているそうです( 電子レセプト請求に係る猶予措置及び免除措置について|社会保険診療報酬支払基金 )。 電子レセプトとレセ電ビューア 電子レセプトについての仕様は支払基金によって公開されています。今回は「 電子レセプト作成の手引き 」という資料を元に「医科」のレセプトについて調べて発表しました。 発表資料はこちら。 電子レセプトの実体は CSV 形式のシンプルなテキストファイルです(拡張子は UKE なので、UKE ファイルと呼ぶこともあるようです)。ただ、電子レセプトの仕様を把握したとしても、やはり CSV ファイルを見て内容を把握するのは至難の業です。ファイル上では診療行為や医薬品、傷病名はコードとして表現されているため、 マスタデータ を参照しなければその内容まで理解することはできないからです。 そこで登場するのが、レセ電ビューアというツールで、ORCA Project によって公開されているフリーの電子レセプトビューアです。「レセ電=電子レセプト」ですね。 ORCA Project: レセ電ビューワ 日本医師会開発・日医標準レセプトソフトウェアのサイトです www.orca.med.or.jp (※上記ページでは「レセ電ビューア」と「レセ電ビューワ」が混在していますが、本ブログでは「レセ電ビューア」で統一しています) レセ電ビューアは Windows と Ubuntu で動作し、上述した UKE ファイルを読み込み、見やすく表示してくれる便利ツールです。ここからはレセ電ビューアをインストールし、電子レセプトを読み込んで表示するところまでを紹介したいと思います。 レセ電ビューアのインストール レセ電ビューアは Windows と Ubuntu 上で動作しますので、まずは Ubuntu 環境を準備します。私は VirtualBox 上に Ubuntu 環境を用意しました。 公式のインストールマニュアル( ubuntu 環境へのレセ電ビューアインストール )に基づいて作業していきます。 # Keyring と apt-line の追加 $ sudo su $ wget -q https://ftp.orca.med.or.jp/pub/ubuntu/archive.key $ apt-key add archive.key $ wget -q -O \ /etc/apt/sources.list.d/jma-receipt-xenial50.list \ https://ftp.orca.med.or.jp/pub/ubuntu/jma-receipt-xenial50.list $ apt-get update $ apt-get dist-upgrade # レセ電ビューアパッケージインストール $ apt-get install jma-receview $ apt-get install jma-receview-server # レセ電ビューア起動 $ jma-receview レセ電ビューアの設定 電子レセプトに含まれる診療行為などのコードに対応するマスタデータを参照するため、日レセ(jma-receipt)の DB を利用することができます。今回は DBFile 形式で DB に接続します。レセ電ビューアに付属するスクリプトを実行し、jma-receipt の DB から必要なテーブルをダンプすることができます。このファイルをレセ電ビューアに設定することで、電子レセプトの表示がよりわかりやすくなります。 # レセ電ビューアで使う DBFile を作る # https://ftp.orca.med.or.jp/pub/receview/manual/jma-receview.pdf # 「2.7.4 DBFile の作成方法」 $ sudo su orca $ ls -la /usr/share/jma-receview/db/ $ mkdir /var/tmp/dbfile $ cp /usr/share/jma-receview/db/make_dbfile.sh /var/tmp/dbfile/ $ cd /var/tmp/dbfile/ $ sh ./make_dbfile.sh 20170101 $ ls -lh 合計 6.9M -rwxr-xr-x 1 orca orca 2.9K 5 月 18 14:55 make_dbfile.sh -rw-r--r-- 1 orca orca 1.2M 5 月 18 14:57 tbl_byomei.rdb -rw-r--r-- 1 orca orca 89 5 月 18 14:57 tbl_dbkanri.rdb -rw-r--r-- 1 orca orca 330K 5 月 18 14:57 tbl_hknjainf.rdb -rw-r--r-- 1 orca orca 9.6K 5 月 18 14:57 tbl_labor_sio.rdb -rw-r--r-- 1 orca orca 343K 5 月 18 14:57 tbl_syskanri.rdb -rw-r--r-- 1 orca orca 5.1M 5 月 18 14:57 tbl_tensu.rdb 接続設定で「DBFile」を選択し、先ほど作成した DBFile を選択します。 レセ電ビューア近影 「ファイル」から UKE ファイルを開くと、レセプトに基づいて患者基本情報、保険・公費情報、診療行為情報のほか、紙レセプトのプレビューや患者単位での電子レセプトを表示することができます。また、「編集モード」に切り替えることで患者情報や病名の編集が可能で、編集した内容でレセプトを再出力することもできるようです。 おまけ ここまで紹介してきたレセ電ビューアですが、調べてみるとどうやら Ruby で実装されているようです。これらのコードを読んでいくことで新たな地平を開くことができるかもしれません。 $ dpkg -L jma-receview | grep ruby /usr/lib/ruby /usr/lib/ruby/2.3.0 /usr/lib/ruby/2.3.0/jma /usr/lib/ruby/2.3.0/jma/receview /usr/lib/ruby/2.3.0/jma/receview/menu.rb /usr/lib/ruby/2.3.0/jma/receview/intconv.rb /usr/lib/ruby/2.3.0/jma/receview/base.rb /usr/lib/ruby/2.3.0/jma/receview/dbfile_lib.rb /usr/lib/ruby/2.3.0/jma/receview/gui.rb /usr/lib/ruby/2.3.0/jma/receview/yearconv.rb /usr/lib/ruby/2.3.0/jma/receview/exception.rb /usr/lib/ruby/2.3.0/jma/receview/api.rb /usr/lib/ruby/2.3.0/jma/receview/config.rb /usr/lib/ruby/2.3.0/jma/receview/image.rb /usr/lib/ruby/2.3.0/jma/receview/dialog.rb /usr/lib/ruby/2.3.0/jma/receview/upstart.rb /usr/lib/ruby/2.3.0/jma/receview/dbslib.rb /usr/lib/ruby/2.3.0/jma/receview/thread.rb /usr/lib/ruby/2.3.0/jma/receview/receview.rb /usr/lib/ruby/2.3.0/jma/receview/sickname_edit.rb /usr/lib/ruby/2.3.0/jma/receview/dayconv.rb /usr/lib/ruby/2.3.0/jma/receview/isoimage.rb /usr/lib/ruby/2.3.0/jma/receview/help.rb /usr/lib/ruby/2.3.0/jma/receview/other_csv.rb /usr/lib/ruby/2.3.0/jma/receview/print.rb /usr/lib/ruby/2.3.0/jma/receview/env.rb /usr/lib/ruby/2.3.0/jma/receview/generation.rb /usr/lib/ruby/2.3.0/jma/receview/red2cairo.rb /usr/lib/ruby/2.3.0/jma/receview/strconv.rb /usr/lib/ruby/2.3.0/jma/receview/log.rb /usr/lib/ruby/2.3.0/jma/receview/hokenconv.rb /usr/lib/ruby/2.3.0/jma/receview/keybind.rb /usr/lib/ruby/2.3.0/jma/receview/version.rb /usr/lib/ruby/2.3.0/jma/receview/gtk2_fix.rb /usr/lib/ruby/2.3.0/jma/receview/preview_widget.rb /usr/lib/ruby/2.3.0/jma/receview/command.rb # /usr/bin/jma-receview も ruby で書かれてました(12000 行以上ある…) まとめ 今回は電子レセプトとレセ電ビューアについて、簡単に紹介しました。 わたしたちの生活とは切り離せない「医療」に関するシステムや仕様は、意外と一般公開されているものもあり、誰でも触れることができます。ただ、動作環境が制限されていたり、インターネット界隈のエンジニアがよく目にする技術とは異なるスタックで構築されていたり、それなりにハードルがあるように感じています。 これらのハードルを下げ、より多くの人が「調べてみよう」「ちょっと触ってみよう」と思うようになれば、医療に関わるシステムや仕様もよりシンプルで使いやすいものになり、ひいては各医療問題の解決・患者体験の改善につながっていくのではないかな、メドレーがつなげていきたいなと思っています。 最後はいいことを言って締めたい性分なのですが、いかがだったでしょうか。 ここまでお読みいただき、ありがとうございました。 お知らせ メドレーの開発にご興味ある方は、こちらからご連絡ください。 メンバーのストーリー | 株式会社メドレー メンバーのストーリー 家族や友人が病気になった時に救いの手を差しのべる医療の力。... www.medley.jp 7/27 に開催されるデブサミ 2018Summer に協賛させていただきます。ぜひ遊びにいらしてください。 Developers Summit 2018 Summer ITエンジニアの祭典「Developers Summit 2018 Summer」(デブサミ2018夏)は、2018年7月27日に開催! event.shoeisha.jp
アバター
こんにちは、開発本部の竹内です。最近子どものプリンセスへの強い憧れに若干引いております。 さて先日、TechLunch という社内勉強会で「電子レセプト」について話しましたので、こちらでも簡単に紹介させていただきます。 レセプトとは ところで、みなさまは「レセプト」についてご存知でしょうか?私はメドレーに入社するまで知りませんでした。 レセプトとは医療機関が支払基金へ診療報酬を請求するための明細書情報のことです。 と言っても、初めて聞かれる方もいらっしゃると思いますので、医療機関におけるお金の流れとともに簡単に説明します。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) 医療機関は「診療」の対価として、被保険者等(≒ 患者)からお金を受け取るわけですが、被保険者の加入する保険や公費によってその額は変わります。負担割合が 3 割の場合、残りの 7 割を被保険者が加入する保険組合などへ請求する必要があります。 この保険組合などへの請求を取りまとめ、内容を審査しているのが支払基金と呼ばれる組織で、医療機関は月に一度、前月の患者ごとの診療点数を計算し「レセプト」として支払基金に提出することになります。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) レセプトには、請求する診療点数のほか、医療機関の情報、被保険者の情報(氏名などの基本情報、加入している保険者情報)、診療行為や傷病名に関する情報などが含まれています。 「レセプト」には紙と電子データとありますが、現在は原則として電子レセプトを提出することが求められているそうです( 電子レセプト請求に係る猶予措置及び免除措置について|社会保険診療報酬支払基金 )。 電子レセプトとレセ電ビューア 電子レセプトについての仕様は支払基金によって公開されています。今回は「 電子レセプト作成の手引き 」という資料を元に「医科」のレセプトについて調べて発表しました。 発表資料はこちら。 電子レセプトの実体は CSV 形式のシンプルなテキストファイルです(拡張子は UKE なので、UKE ファイルと呼ぶこともあるようです)。ただ、電子レセプトの仕様を把握したとしても、やはり CSV ファイルを見て内容を把握するのは至難の業です。ファイル上では診療行為や医薬品、傷病名はコードとして表現されているため、 マスタデータ を参照しなければその内容まで理解することはできないからです。 そこで登場するのが、レセ電ビューアというツールで、ORCA Project によって公開されているフリーの電子レセプトビューアです。「レセ電=電子レセプト」ですね。 https://www.orca.med.or.jp/receipt/use/jma-receview.html (※上記ページでは「レセ電ビューア」と「レセ電ビューワ」が混在していますが、本ブログでは「レセ電ビューア」で統一しています) レセ電ビューアは Windows と Ubuntu で動作し、上述した UKE ファイルを読み込み、見やすく表示してくれる便利ツールです。ここからはレセ電ビューアをインストールし、電子レセプトを読み込んで表示するところまでを紹介したいと思います。 レセ電ビューアのインストール レセ電ビューアは Windows と Ubuntu 上で動作しますので、まずは Ubuntu 環境を準備します。私は VirtualBox 上に Ubuntu 環境を用意しました。 公式のインストールマニュアル( ubuntu 環境へのレセ電ビューアインストール )に基づいて作業していきます。 # Keyring と apt-line の追加 $ sudo su $ wget -q https://ftp.orca.med.or.jp/pub/ubuntu/archive.key $ apt-key add archive.key $ wget -q -O \ /etc/apt/sources.list.d/jma-receipt-xenial50.list \ https://ftp.orca.med.or.jp/pub/ubuntu/jma-receipt-xenial50.list $ apt-get update $ apt-get dist-upgrade # レセ電ビューアパッケージインストール $ apt-get install jma-receview $ apt-get install jma-receview-server # レセ電ビューア起動 $ jma-receview レセ電ビューアの設定 電子レセプトに含まれる診療行為などのコードに対応するマスタデータを参照するため、日レセ(jma-receipt)の DB を利用することができます。今回は DBFile 形式で DB に接続します。レセ電ビューアに付属するスクリプトを実行し、jma-receipt の DB から必要なテーブルをダンプすることができます。このファイルをレセ電ビューアに設定することで、電子レセプトの表示がよりわかりやすくなります。 # レセ電ビューアで使う DBFile を作る # https://ftp.orca.med.or.jp/pub/receview/manual/jma-receview.pdf # 「2.7.4 DBFile の作成方法」 $ sudo su orca $ ls -la /usr/share/jma-receview/db/ $ mkdir /var/tmp/dbfile $ cp /usr/share/jma-receview/db/make_dbfile.sh /var/tmp/dbfile/ $ cd /var/tmp/dbfile/ $ sh ./make_dbfile.sh 20170101 $ ls -lh 合計 6.9M -rwxr-xr-x 1 orca orca 2.9K 5 月 18 14:55 make_dbfile.sh -rw-r--r-- 1 orca orca 1.2M 5 月 18 14:57 tbl_byomei.rdb -rw-r--r-- 1 orca orca 89 5 月 18 14:57 tbl_dbkanri.rdb -rw-r--r-- 1 orca orca 330K 5 月 18 14:57 tbl_hknjainf.rdb -rw-r--r-- 1 orca orca 9.6K 5 月 18 14:57 tbl_labor_sio.rdb -rw-r--r-- 1 orca orca 343K 5 月 18 14:57 tbl_syskanri.rdb -rw-r--r-- 1 orca orca 5.1M 5 月 18 14:57 tbl_tensu.rdb 接続設定で「DBFile」を選択し、先ほど作成した DBFile を選択します。 レセ電ビューア近影 「ファイル」から UKE ファイルを開くと、レセプトに基づいて患者基本情報、保険・公費情報、診療行為情報のほか、紙レセプトのプレビューや患者単位での電子レセプトを表示することができます。また、「編集モード」に切り替えることで患者情報や病名の編集が可能で、編集した内容でレセプトを再出力することもできるようです。 おまけ ここまで紹介してきたレセ電ビューアですが、調べてみるとどうやら Ruby で実装されているようです。これらのコードを読んでいくことで新たな地平を開くことができるかもしれません。 $ dpkg -L jma-receview | grep ruby /usr/lib/ruby /usr/lib/ruby/2.3.0 /usr/lib/ruby/2.3.0/jma /usr/lib/ruby/2.3.0/jma/receview /usr/lib/ruby/2.3.0/jma/receview/menu.rb /usr/lib/ruby/2.3.0/jma/receview/intconv.rb /usr/lib/ruby/2.3.0/jma/receview/base.rb /usr/lib/ruby/2.3.0/jma/receview/dbfile_lib.rb /usr/lib/ruby/2.3.0/jma/receview/gui.rb /usr/lib/ruby/2.3.0/jma/receview/yearconv.rb /usr/lib/ruby/2.3.0/jma/receview/exception.rb /usr/lib/ruby/2.3.0/jma/receview/api.rb /usr/lib/ruby/2.3.0/jma/receview/config.rb /usr/lib/ruby/2.3.0/jma/receview/image.rb /usr/lib/ruby/2.3.0/jma/receview/dialog.rb /usr/lib/ruby/2.3.0/jma/receview/upstart.rb /usr/lib/ruby/2.3.0/jma/receview/dbslib.rb /usr/lib/ruby/2.3.0/jma/receview/thread.rb /usr/lib/ruby/2.3.0/jma/receview/receview.rb /usr/lib/ruby/2.3.0/jma/receview/sickname_edit.rb /usr/lib/ruby/2.3.0/jma/receview/dayconv.rb /usr/lib/ruby/2.3.0/jma/receview/isoimage.rb /usr/lib/ruby/2.3.0/jma/receview/help.rb /usr/lib/ruby/2.3.0/jma/receview/other_csv.rb /usr/lib/ruby/2.3.0/jma/receview/print.rb /usr/lib/ruby/2.3.0/jma/receview/env.rb /usr/lib/ruby/2.3.0/jma/receview/generation.rb /usr/lib/ruby/2.3.0/jma/receview/red2cairo.rb /usr/lib/ruby/2.3.0/jma/receview/strconv.rb /usr/lib/ruby/2.3.0/jma/receview/log.rb /usr/lib/ruby/2.3.0/jma/receview/hokenconv.rb /usr/lib/ruby/2.3.0/jma/receview/keybind.rb /usr/lib/ruby/2.3.0/jma/receview/version.rb /usr/lib/ruby/2.3.0/jma/receview/gtk2_fix.rb /usr/lib/ruby/2.3.0/jma/receview/preview_widget.rb /usr/lib/ruby/2.3.0/jma/receview/command.rb # /usr/bin/jma-receview も ruby で書かれてました(12000 行以上ある…) まとめ 今回は電子レセプトとレセ電ビューアについて、簡単に紹介しました。 わたしたちの生活とは切り離せない「医療」に関するシステムや仕様は、意外と一般公開されているものもあり、誰でも触れることができます。ただ、動作環境が制限されていたり、インターネット界隈のエンジニアがよく目にする技術とは異なるスタックで構築されていたり、それなりにハードルがあるように感じています。 これらのハードルを下げ、より多くの人が「調べてみよう」「ちょっと触ってみよう」と思うようになれば、医療に関わるシステムや仕様もよりシンプルで使いやすいものになり、ひいては各医療問題の解決・患者体験の改善につながっていくのではないかな、メドレーがつなげていきたいなと思っています。 最後はいいことを言って締めたい性分なのですが、いかがだったでしょうか。 ここまでお読みいただき、ありがとうございました。 お知らせ メドレーの開発にご興味ある方は、こちらからご連絡ください。 https://www.medley.jp/recruit/creative.html 7/27 に開催されるデブサミ 2018Summer に協賛させていただきます。ぜひ遊びにいらしてください。 https://event.shoeisha.jp/devsumi/20180727
アバター
こんにちは、開発本部の竹内です。最近子どものプリンセスへの強い憧れに若干引いております。 さて先日、TechLunch という社内勉強会で「電子レセプト」について話しましたので、こちらでも簡単に紹介させていただきます。 レセプトとは ところで、みなさまは「レセプト」についてご存知でしょうか?私はメドレーに入社するまで知りませんでした。 レセプトとは医療機関が支払基金へ診療報酬を請求するための明細書情報のことです。 と言っても、初めて聞かれる方もいらっしゃると思いますので、医療機関におけるお金の流れとともに簡単に説明します。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) 医療機関は「診療」の対価として、被保険者等(≒ 患者)からお金を受け取るわけですが、被保険者の加入する保険や公費によってその額は変わります。負担割合が 3 割の場合、残りの 7 割を被保険者が加入する保険組合などへ請求する必要があります。 この保険組合などへの請求を取りまとめ、内容を審査しているのが支払基金と呼ばれる組織で、医療機関は月に一度、前月の患者ごとの診療点数を計算し「レセプト」として支払基金に提出することになります。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) レセプトには、請求する診療点数のほか、医療機関の情報、被保険者の情報(氏名などの基本情報、加入している保険者情報)、診療行為や傷病名に関する情報などが含まれています。 「レセプト」には紙と電子データとありますが、現在は原則として電子レセプトを提出することが求められているそうです( 電子レセプト請求に係る猶予措置及び免除措置について|社会保険診療報酬支払基金 )。 電子レセプトとレセ電ビューア 電子レセプトについての仕様は支払基金によって公開されています。今回は「 電子レセプト作成の手引き 」という資料を元に「医科」のレセプトについて調べて発表しました。 発表資料はこちら。 電子レセプトの実体は CSV 形式のシンプルなテキストファイルです(拡張子は UKE なので、UKE ファイルと呼ぶこともあるようです)。ただ、電子レセプトの仕様を把握したとしても、やはり CSV ファイルを見て内容を把握するのは至難の業です。ファイル上では診療行為や医薬品、傷病名はコードとして表現されているため、 マスタデータ を参照しなければその内容まで理解することはできないからです。 そこで登場するのが、レセ電ビューアというツールで、ORCA Project によって公開されているフリーの電子レセプトビューアです。「レセ電=電子レセプト」ですね。 ORCA Project: レセ電ビューワ 日本医師会開発・日医標準レセプトソフトウェアのサイトです www.orca.med.or.jp (※上記ページでは「レセ電ビューア」と「レセ電ビューワ」が混在していますが、本ブログでは「レセ電ビューア」で統一しています) レセ電ビューアは Windows と Ubuntu で動作し、上述した UKE ファイルを読み込み、見やすく表示してくれる便利ツールです。ここからはレセ電ビューアをインストールし、電子レセプトを読み込んで表示するところまでを紹介したいと思います。 レセ電ビューアのインストール レセ電ビューアは Windows と Ubuntu 上で動作しますので、まずは Ubuntu 環境を準備します。私は VirtualBox 上に Ubuntu 環境を用意しました。 公式のインストールマニュアル( ubuntu 環境へのレセ電ビューアインストール )に基づいて作業していきます。 # Keyring と apt-line の追加 $ sudo su $ wget -q https://ftp.orca.med.or.jp/pub/ubuntu/archive.key $ apt-key add archive.key $ wget -q -O \ /etc/apt/sources.list.d/jma-receipt-xenial50.list \ https://ftp.orca.med.or.jp/pub/ubuntu/jma-receipt-xenial50.list $ apt-get update $ apt-get dist-upgrade # レセ電ビューアパッケージインストール $ apt-get install jma-receview $ apt-get install jma-receview-server # レセ電ビューア起動 $ jma-receview レセ電ビューアの設定 電子レセプトに含まれる診療行為などのコードに対応するマスタデータを参照するため、日レセ(jma-receipt)の DB を利用することができます。今回は DBFile 形式で DB に接続します。レセ電ビューアに付属するスクリプトを実行し、jma-receipt の DB から必要なテーブルをダンプすることができます。このファイルをレセ電ビューアに設定することで、電子レセプトの表示がよりわかりやすくなります。 # レセ電ビューアで使う DBFile を作る # https://ftp.orca.med.or.jp/pub/receview/manual/jma-receview.pdf # 「2.7.4 DBFile の作成方法」 $ sudo su orca $ ls -la /usr/share/jma-receview/db/ $ mkdir /var/tmp/dbfile $ cp /usr/share/jma-receview/db/make_dbfile.sh /var/tmp/dbfile/ $ cd /var/tmp/dbfile/ $ sh ./make_dbfile.sh 20170101 $ ls -lh 合計 6.9M -rwxr-xr-x 1 orca orca 2.9K 5 月 18 14:55 make_dbfile.sh -rw-r--r-- 1 orca orca 1.2M 5 月 18 14:57 tbl_byomei.rdb -rw-r--r-- 1 orca orca 89 5 月 18 14:57 tbl_dbkanri.rdb -rw-r--r-- 1 orca orca 330K 5 月 18 14:57 tbl_hknjainf.rdb -rw-r--r-- 1 orca orca 9.6K 5 月 18 14:57 tbl_labor_sio.rdb -rw-r--r-- 1 orca orca 343K 5 月 18 14:57 tbl_syskanri.rdb -rw-r--r-- 1 orca orca 5.1M 5 月 18 14:57 tbl_tensu.rdb 接続設定で「DBFile」を選択し、先ほど作成した DBFile を選択します。 レセ電ビューア近影 「ファイル」から UKE ファイルを開くと、レセプトに基づいて患者基本情報、保険・公費情報、診療行為情報のほか、紙レセプトのプレビューや患者単位での電子レセプトを表示することができます。また、「編集モード」に切り替えることで患者情報や病名の編集が可能で、編集した内容でレセプトを再出力することもできるようです。 おまけ ここまで紹介してきたレセ電ビューアですが、調べてみるとどうやら Ruby で実装されているようです。これらのコードを読んでいくことで新たな地平を開くことができるかもしれません。 $ dpkg -L jma-receview | grep ruby /usr/lib/ruby /usr/lib/ruby/2.3.0 /usr/lib/ruby/2.3.0/jma /usr/lib/ruby/2.3.0/jma/receview /usr/lib/ruby/2.3.0/jma/receview/menu.rb /usr/lib/ruby/2.3.0/jma/receview/intconv.rb /usr/lib/ruby/2.3.0/jma/receview/base.rb /usr/lib/ruby/2.3.0/jma/receview/dbfile_lib.rb /usr/lib/ruby/2.3.0/jma/receview/gui.rb /usr/lib/ruby/2.3.0/jma/receview/yearconv.rb /usr/lib/ruby/2.3.0/jma/receview/exception.rb /usr/lib/ruby/2.3.0/jma/receview/api.rb /usr/lib/ruby/2.3.0/jma/receview/config.rb /usr/lib/ruby/2.3.0/jma/receview/image.rb /usr/lib/ruby/2.3.0/jma/receview/dialog.rb /usr/lib/ruby/2.3.0/jma/receview/upstart.rb /usr/lib/ruby/2.3.0/jma/receview/dbslib.rb /usr/lib/ruby/2.3.0/jma/receview/thread.rb /usr/lib/ruby/2.3.0/jma/receview/receview.rb /usr/lib/ruby/2.3.0/jma/receview/sickname_edit.rb /usr/lib/ruby/2.3.0/jma/receview/dayconv.rb /usr/lib/ruby/2.3.0/jma/receview/isoimage.rb /usr/lib/ruby/2.3.0/jma/receview/help.rb /usr/lib/ruby/2.3.0/jma/receview/other_csv.rb /usr/lib/ruby/2.3.0/jma/receview/print.rb /usr/lib/ruby/2.3.0/jma/receview/env.rb /usr/lib/ruby/2.3.0/jma/receview/generation.rb /usr/lib/ruby/2.3.0/jma/receview/red2cairo.rb /usr/lib/ruby/2.3.0/jma/receview/strconv.rb /usr/lib/ruby/2.3.0/jma/receview/log.rb /usr/lib/ruby/2.3.0/jma/receview/hokenconv.rb /usr/lib/ruby/2.3.0/jma/receview/keybind.rb /usr/lib/ruby/2.3.0/jma/receview/version.rb /usr/lib/ruby/2.3.0/jma/receview/gtk2_fix.rb /usr/lib/ruby/2.3.0/jma/receview/preview_widget.rb /usr/lib/ruby/2.3.0/jma/receview/command.rb # /usr/bin/jma-receview も ruby で書かれてました(12000 行以上ある…) まとめ 今回は電子レセプトとレセ電ビューアについて、簡単に紹介しました。 わたしたちの生活とは切り離せない「医療」に関するシステムや仕様は、意外と一般公開されているものもあり、誰でも触れることができます。ただ、動作環境が制限されていたり、インターネット界隈のエンジニアがよく目にする技術とは異なるスタックで構築されていたり、それなりにハードルがあるように感じています。 これらのハードルを下げ、より多くの人が「調べてみよう」「ちょっと触ってみよう」と思うようになれば、医療に関わるシステムや仕様もよりシンプルで使いやすいものになり、ひいては各医療問題の解決・患者体験の改善につながっていくのではないかな、メドレーがつなげていきたいなと思っています。 最後はいいことを言って締めたい性分なのですが、いかがだったでしょうか。 ここまでお読みいただき、ありがとうございました。 お知らせ メドレーの開発にご興味ある方は、こちらからご連絡ください。 メンバーのストーリー | 株式会社メドレー メンバーのストーリー 家族や友人が病気になった時に救いの手を差しのべる医療の力。... www.medley.jp 7/27 に開催されるデブサミ 2018Summer に協賛させていただきます。ぜひ遊びにいらしてください。 Developers Summit 2018 Summer ITエンジニアの祭典「Developers Summit 2018 Summer」(デブサミ2018夏)は、2018年7月27日に開催! event.shoeisha.jp
アバター
こんにちは、開発本部の竹内です。最近子どものプリンセスへの強い憧れに若干引いております。 さて先日、TechLunch という社内勉強会で「電子レセプト」について話しましたので、こちらでも簡単に紹介させていただきます。 レセプトとは ところで、みなさまは「レセプト」についてご存知でしょうか?私はメドレーに入社するまで知りませんでした。 レセプトとは医療機関が支払基金へ診療報酬を請求するための明細書情報のことです。 と言っても、初めて聞かれる方もいらっしゃると思いますので、医療機関におけるお金の流れとともに簡単に説明します。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) 医療機関は「診療」の対価として、被保険者等(≒ 患者)からお金を受け取るわけですが、被保険者の加入する保険や公費によってその額は変わります。負担割合が 3 割の場合、残りの 7 割を被保険者が加入する保険組合などへ請求する必要があります。 この保険組合などへの請求を取りまとめ、内容を審査しているのが支払基金と呼ばれる組織で、医療機関は月に一度、前月の患者ごとの診療点数を計算し「レセプト」として支払基金に提出することになります。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) レセプトには、請求する診療点数のほか、医療機関の情報、被保険者の情報(氏名などの基本情報、加入している保険者情報)、診療行為や傷病名に関する情報などが含まれています。 「レセプト」には紙と電子データとありますが、現在は原則として電子レセプトを提出することが求められているそうです( 電子レセプト請求に係る猶予措置及び免除措置について|社会保険診療報酬支払基金 )。 電子レセプトとレセ電ビューア 電子レセプトについての仕様は支払基金によって公開されています。今回は「 電子レセプト作成の手引き 」という資料を元に「医科」のレセプトについて調べて発表しました。 発表資料はこちら。 電子レセプトの実体は CSV 形式のシンプルなテキストファイルです(拡張子は UKE なので、UKE ファイルと呼ぶこともあるようです)。ただ、電子レセプトの仕様を把握したとしても、やはり CSV ファイルを見て内容を把握するのは至難の業です。ファイル上では診療行為や医薬品、傷病名はコードとして表現されているため、 マスタデータ を参照しなければその内容まで理解することはできないからです。 そこで登場するのが、レセ電ビューアというツールで、ORCA Project によって公開されているフリーの電子レセプトビューアです。「レセ電=電子レセプト」ですね。 ORCA Project: レセ電ビューワ 日本医師会開発・日医標準レセプトソフトウェアのサイトです www.orca.med.or.jp (※上記ページでは「レセ電ビューア」と「レセ電ビューワ」が混在していますが、本ブログでは「レセ電ビューア」で統一しています) レセ電ビューアは Windows と Ubuntu で動作し、上述した UKE ファイルを読み込み、見やすく表示してくれる便利ツールです。ここからはレセ電ビューアをインストールし、電子レセプトを読み込んで表示するところまでを紹介したいと思います。 レセ電ビューアのインストール レセ電ビューアは Windows と Ubuntu 上で動作しますので、まずは Ubuntu 環境を準備します。私は VirtualBox 上に Ubuntu 環境を用意しました。 公式のインストールマニュアル( ubuntu 環境へのレセ電ビューアインストール )に基づいて作業していきます。 # Keyring と apt-line の追加 $ sudo su $ wget -q https://ftp.orca.med.or.jp/pub/ubuntu/archive.key $ apt-key add archive.key $ wget -q -O \ /etc/apt/sources.list.d/jma-receipt-xenial50.list \ https://ftp.orca.med.or.jp/pub/ubuntu/jma-receipt-xenial50.list $ apt-get update $ apt-get dist-upgrade # レセ電ビューアパッケージインストール $ apt-get install jma-receview $ apt-get install jma-receview-server # レセ電ビューア起動 $ jma-receview レセ電ビューアの設定 電子レセプトに含まれる診療行為などのコードに対応するマスタデータを参照するため、日レセ(jma-receipt)の DB を利用することができます。今回は DBFile 形式で DB に接続します。レセ電ビューアに付属するスクリプトを実行し、jma-receipt の DB から必要なテーブルをダンプすることができます。このファイルをレセ電ビューアに設定することで、電子レセプトの表示がよりわかりやすくなります。 # レセ電ビューアで使う DBFile を作る # https://ftp.orca.med.or.jp/pub/receview/manual/jma-receview.pdf # 「2.7.4 DBFile の作成方法」 $ sudo su orca $ ls -la /usr/share/jma-receview/db/ $ mkdir /var/tmp/dbfile $ cp /usr/share/jma-receview/db/make_dbfile.sh /var/tmp/dbfile/ $ cd /var/tmp/dbfile/ $ sh ./make_dbfile.sh 20170101 $ ls -lh 合計 6.9M -rwxr-xr-x 1 orca orca 2.9K 5 月 18 14:55 make_dbfile.sh -rw-r--r-- 1 orca orca 1.2M 5 月 18 14:57 tbl_byomei.rdb -rw-r--r-- 1 orca orca 89 5 月 18 14:57 tbl_dbkanri.rdb -rw-r--r-- 1 orca orca 330K 5 月 18 14:57 tbl_hknjainf.rdb -rw-r--r-- 1 orca orca 9.6K 5 月 18 14:57 tbl_labor_sio.rdb -rw-r--r-- 1 orca orca 343K 5 月 18 14:57 tbl_syskanri.rdb -rw-r--r-- 1 orca orca 5.1M 5 月 18 14:57 tbl_tensu.rdb 接続設定で「DBFile」を選択し、先ほど作成した DBFile を選択します。 レセ電ビューア近影 「ファイル」から UKE ファイルを開くと、レセプトに基づいて患者基本情報、保険・公費情報、診療行為情報のほか、紙レセプトのプレビューや患者単位での電子レセプトを表示することができます。また、「編集モード」に切り替えることで患者情報や病名の編集が可能で、編集した内容でレセプトを再出力することもできるようです。 おまけ ここまで紹介してきたレセ電ビューアですが、調べてみるとどうやら Ruby で実装されているようです。これらのコードを読んでいくことで新たな地平を開くことができるかもしれません。 $ dpkg -L jma-receview | grep ruby /usr/lib/ruby /usr/lib/ruby/2.3.0 /usr/lib/ruby/2.3.0/jma /usr/lib/ruby/2.3.0/jma/receview /usr/lib/ruby/2.3.0/jma/receview/menu.rb /usr/lib/ruby/2.3.0/jma/receview/intconv.rb /usr/lib/ruby/2.3.0/jma/receview/base.rb /usr/lib/ruby/2.3.0/jma/receview/dbfile_lib.rb /usr/lib/ruby/2.3.0/jma/receview/gui.rb /usr/lib/ruby/2.3.0/jma/receview/yearconv.rb /usr/lib/ruby/2.3.0/jma/receview/exception.rb /usr/lib/ruby/2.3.0/jma/receview/api.rb /usr/lib/ruby/2.3.0/jma/receview/config.rb /usr/lib/ruby/2.3.0/jma/receview/image.rb /usr/lib/ruby/2.3.0/jma/receview/dialog.rb /usr/lib/ruby/2.3.0/jma/receview/upstart.rb /usr/lib/ruby/2.3.0/jma/receview/dbslib.rb /usr/lib/ruby/2.3.0/jma/receview/thread.rb /usr/lib/ruby/2.3.0/jma/receview/receview.rb /usr/lib/ruby/2.3.0/jma/receview/sickname_edit.rb /usr/lib/ruby/2.3.0/jma/receview/dayconv.rb /usr/lib/ruby/2.3.0/jma/receview/isoimage.rb /usr/lib/ruby/2.3.0/jma/receview/help.rb /usr/lib/ruby/2.3.0/jma/receview/other_csv.rb /usr/lib/ruby/2.3.0/jma/receview/print.rb /usr/lib/ruby/2.3.0/jma/receview/env.rb /usr/lib/ruby/2.3.0/jma/receview/generation.rb /usr/lib/ruby/2.3.0/jma/receview/red2cairo.rb /usr/lib/ruby/2.3.0/jma/receview/strconv.rb /usr/lib/ruby/2.3.0/jma/receview/log.rb /usr/lib/ruby/2.3.0/jma/receview/hokenconv.rb /usr/lib/ruby/2.3.0/jma/receview/keybind.rb /usr/lib/ruby/2.3.0/jma/receview/version.rb /usr/lib/ruby/2.3.0/jma/receview/gtk2_fix.rb /usr/lib/ruby/2.3.0/jma/receview/preview_widget.rb /usr/lib/ruby/2.3.0/jma/receview/command.rb # /usr/bin/jma-receview も ruby で書かれてました(12000 行以上ある…) まとめ 今回は電子レセプトとレセ電ビューアについて、簡単に紹介しました。 わたしたちの生活とは切り離せない「医療」に関するシステムや仕様は、意外と一般公開されているものもあり、誰でも触れることができます。ただ、動作環境が制限されていたり、インターネット界隈のエンジニアがよく目にする技術とは異なるスタックで構築されていたり、それなりにハードルがあるように感じています。 これらのハードルを下げ、より多くの人が「調べてみよう」「ちょっと触ってみよう」と思うようになれば、医療に関わるシステムや仕様もよりシンプルで使いやすいものになり、ひいては各医療問題の解決・患者体験の改善につながっていくのではないかな、メドレーがつなげていきたいなと思っています。 最後はいいことを言って締めたい性分なのですが、いかがだったでしょうか。 ここまでお読みいただき、ありがとうございました。 お知らせ メドレーの開発にご興味ある方は、こちらからご連絡ください。 メンバーのストーリー | 株式会社メドレー メンバーのストーリー 家族や友人が病気になった時に救いの手を差しのべる医療の力。... www.medley.jp 7/27 に開催されるデブサミ 2018Summer に協賛させていただきます。ぜひ遊びにいらしてください。 Developers Summit 2018 Summer ITエンジニアの祭典「Developers Summit 2018 Summer」(デブサミ2018夏)は、2018年7月27日に開催! event.shoeisha.jp
アバター
こんにちは、開発本部の竹内です。最近子どものプリンセスへの強い憧れに若干引いております。 さて先日、TechLunch という社内勉強会で「電子レセプト」について話しましたので、こちらでも簡単に紹介させていただきます。 レセプトとは ところで、みなさまは「レセプト」についてご存知でしょうか?私はメドレーに入社するまで知りませんでした。 レセプトとは医療機関が支払基金へ診療報酬を請求するための明細書情報のことです。 と言っても、初めて聞かれる方もいらっしゃると思いますので、医療機関におけるお金の流れとともに簡単に説明します。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) 医療機関は「診療」の対価として、被保険者等(≒ 患者)からお金を受け取るわけですが、被保険者の加入する保険や公費によってその額は変わります。負担割合が 3 割の場合、残りの 7 割を被保険者が加入する保険組合などへ請求する必要があります。 この保険組合などへの請求を取りまとめ、内容を審査しているのが支払基金と呼ばれる組織で、医療機関は月に一度、前月の患者ごとの診療点数を計算し「レセプト」として支払基金に提出することになります。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) レセプトには、請求する診療点数のほか、医療機関の情報、被保険者の情報(氏名などの基本情報、加入している保険者情報)、診療行為や傷病名に関する情報などが含まれています。 「レセプト」には紙と電子データとありますが、現在は原則として電子レセプトを提出することが求められているそうです( 電子レセプト請求に係る猶予措置及び免除措置について|社会保険診療報酬支払基金 )。 電子レセプトとレセ電ビューア 電子レセプトについての仕様は支払基金によって公開されています。今回は「 電子レセプト作成の手引き 」という資料を元に「医科」のレセプトについて調べて発表しました。 発表資料はこちら。 電子レセプトの実体は CSV 形式のシンプルなテキストファイルです(拡張子は UKE なので、UKE ファイルと呼ぶこともあるようです)。ただ、電子レセプトの仕様を把握したとしても、やはり CSV ファイルを見て内容を把握するのは至難の業です。ファイル上では診療行為や医薬品、傷病名はコードとして表現されているため、 マスタデータ を参照しなければその内容まで理解することはできないからです。 そこで登場するのが、レセ電ビューアというツールで、ORCA Project によって公開されているフリーの電子レセプトビューアです。「レセ電=電子レセプト」ですね。 ORCA Project: レセ電ビューワ 日本医師会開発・日医標準レセプトソフトウェアのサイトです www.orca.med.or.jp (※上記ページでは「レセ電ビューア」と「レセ電ビューワ」が混在していますが、本ブログでは「レセ電ビューア」で統一しています) レセ電ビューアは Windows と Ubuntu で動作し、上述した UKE ファイルを読み込み、見やすく表示してくれる便利ツールです。ここからはレセ電ビューアをインストールし、電子レセプトを読み込んで表示するところまでを紹介したいと思います。 レセ電ビューアのインストール レセ電ビューアは Windows と Ubuntu 上で動作しますので、まずは Ubuntu 環境を準備します。私は VirtualBox 上に Ubuntu 環境を用意しました。 公式のインストールマニュアル( ubuntu 環境へのレセ電ビューアインストール )に基づいて作業していきます。 # Keyring と apt-line の追加 $ sudo su $ wget -q https://ftp.orca.med.or.jp/pub/ubuntu/archive.key $ apt-key add archive.key $ wget -q -O \ /etc/apt/sources.list.d/jma-receipt-xenial50.list \ https://ftp.orca.med.or.jp/pub/ubuntu/jma-receipt-xenial50.list $ apt-get update $ apt-get dist-upgrade # レセ電ビューアパッケージインストール $ apt-get install jma-receview $ apt-get install jma-receview-server # レセ電ビューア起動 $ jma-receview レセ電ビューアの設定 電子レセプトに含まれる診療行為などのコードに対応するマスタデータを参照するため、日レセ(jma-receipt)の DB を利用することができます。今回は DBFile 形式で DB に接続します。レセ電ビューアに付属するスクリプトを実行し、jma-receipt の DB から必要なテーブルをダンプすることができます。このファイルをレセ電ビューアに設定することで、電子レセプトの表示がよりわかりやすくなります。 # レセ電ビューアで使う DBFile を作る # https://ftp.orca.med.or.jp/pub/receview/manual/jma-receview.pdf # 「2.7.4 DBFile の作成方法」 $ sudo su orca $ ls -la /usr/share/jma-receview/db/ $ mkdir /var/tmp/dbfile $ cp /usr/share/jma-receview/db/make_dbfile.sh /var/tmp/dbfile/ $ cd /var/tmp/dbfile/ $ sh ./make_dbfile.sh 20170101 $ ls -lh 合計 6.9M -rwxr-xr-x 1 orca orca 2.9K 5 月 18 14:55 make_dbfile.sh -rw-r--r-- 1 orca orca 1.2M 5 月 18 14:57 tbl_byomei.rdb -rw-r--r-- 1 orca orca 89 5 月 18 14:57 tbl_dbkanri.rdb -rw-r--r-- 1 orca orca 330K 5 月 18 14:57 tbl_hknjainf.rdb -rw-r--r-- 1 orca orca 9.6K 5 月 18 14:57 tbl_labor_sio.rdb -rw-r--r-- 1 orca orca 343K 5 月 18 14:57 tbl_syskanri.rdb -rw-r--r-- 1 orca orca 5.1M 5 月 18 14:57 tbl_tensu.rdb 接続設定で「DBFile」を選択し、先ほど作成した DBFile を選択します。 レセ電ビューア近影 「ファイル」から UKE ファイルを開くと、レセプトに基づいて患者基本情報、保険・公費情報、診療行為情報のほか、紙レセプトのプレビューや患者単位での電子レセプトを表示することができます。また、「編集モード」に切り替えることで患者情報や病名の編集が可能で、編集した内容でレセプトを再出力することもできるようです。 おまけ ここまで紹介してきたレセ電ビューアですが、調べてみるとどうやら Ruby で実装されているようです。これらのコードを読んでいくことで新たな地平を開くことができるかもしれません。 $ dpkg -L jma-receview | grep ruby /usr/lib/ruby /usr/lib/ruby/2.3.0 /usr/lib/ruby/2.3.0/jma /usr/lib/ruby/2.3.0/jma/receview /usr/lib/ruby/2.3.0/jma/receview/menu.rb /usr/lib/ruby/2.3.0/jma/receview/intconv.rb /usr/lib/ruby/2.3.0/jma/receview/base.rb /usr/lib/ruby/2.3.0/jma/receview/dbfile_lib.rb /usr/lib/ruby/2.3.0/jma/receview/gui.rb /usr/lib/ruby/2.3.0/jma/receview/yearconv.rb /usr/lib/ruby/2.3.0/jma/receview/exception.rb /usr/lib/ruby/2.3.0/jma/receview/api.rb /usr/lib/ruby/2.3.0/jma/receview/config.rb /usr/lib/ruby/2.3.0/jma/receview/image.rb /usr/lib/ruby/2.3.0/jma/receview/dialog.rb /usr/lib/ruby/2.3.0/jma/receview/upstart.rb /usr/lib/ruby/2.3.0/jma/receview/dbslib.rb /usr/lib/ruby/2.3.0/jma/receview/thread.rb /usr/lib/ruby/2.3.0/jma/receview/receview.rb /usr/lib/ruby/2.3.0/jma/receview/sickname_edit.rb /usr/lib/ruby/2.3.0/jma/receview/dayconv.rb /usr/lib/ruby/2.3.0/jma/receview/isoimage.rb /usr/lib/ruby/2.3.0/jma/receview/help.rb /usr/lib/ruby/2.3.0/jma/receview/other_csv.rb /usr/lib/ruby/2.3.0/jma/receview/print.rb /usr/lib/ruby/2.3.0/jma/receview/env.rb /usr/lib/ruby/2.3.0/jma/receview/generation.rb /usr/lib/ruby/2.3.0/jma/receview/red2cairo.rb /usr/lib/ruby/2.3.0/jma/receview/strconv.rb /usr/lib/ruby/2.3.0/jma/receview/log.rb /usr/lib/ruby/2.3.0/jma/receview/hokenconv.rb /usr/lib/ruby/2.3.0/jma/receview/keybind.rb /usr/lib/ruby/2.3.0/jma/receview/version.rb /usr/lib/ruby/2.3.0/jma/receview/gtk2_fix.rb /usr/lib/ruby/2.3.0/jma/receview/preview_widget.rb /usr/lib/ruby/2.3.0/jma/receview/command.rb # /usr/bin/jma-receview も ruby で書かれてました(12000 行以上ある…) まとめ 今回は電子レセプトとレセ電ビューアについて、簡単に紹介しました。 わたしたちの生活とは切り離せない「医療」に関するシステムや仕様は、意外と一般公開されているものもあり、誰でも触れることができます。ただ、動作環境が制限されていたり、インターネット界隈のエンジニアがよく目にする技術とは異なるスタックで構築されていたり、それなりにハードルがあるように感じています。 これらのハードルを下げ、より多くの人が「調べてみよう」「ちょっと触ってみよう」と思うようになれば、医療に関わるシステムや仕様もよりシンプルで使いやすいものになり、ひいては各医療問題の解決・患者体験の改善につながっていくのではないかな、メドレーがつなげていきたいなと思っています。 最後はいいことを言って締めたい性分なのですが、いかがだったでしょうか。 ここまでお読みいただき、ありがとうございました。 お知らせ メドレーの開発にご興味ある方は、こちらからご連絡ください。 メンバーのストーリー | 株式会社メドレー メンバーのストーリー 家族や友人が病気になった時に救いの手を差しのべる医療の力。... www.medley.jp 7/27 に開催されるデブサミ 2018Summer に協賛させていただきます。ぜひ遊びにいらしてください。 Developers Summit 2018 Summer ITエンジニアの祭典「Developers Summit 2018 Summer」(デブサミ2018夏)は、2018年7月27日に開催! event.shoeisha.jp
アバター
こんにちは、開発本部の竹内です。最近子どものプリンセスへの強い憧れに若干引いております。 さて先日、TechLunch という社内勉強会で「電子レセプト」について話しましたので、こちらでも簡単に紹介させていただきます。 レセプトとは ところで、みなさまは「レセプト」についてご存知でしょうか?私はメドレーに入社するまで知りませんでした。 レセプトとは医療機関が支払基金へ診療報酬を請求するための明細書情報のことです。 と言っても、初めて聞かれる方もいらっしゃると思いますので、医療機関におけるお金の流れとともに簡単に説明します。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) 医療機関は「診療」の対価として、被保険者等(≒ 患者)からお金を受け取るわけですが、被保険者の加入する保険や公費によってその額は変わります。負担割合が 3 割の場合、残りの 7 割を被保険者が加入する保険組合などへ請求する必要があります。 この保険組合などへの請求を取りまとめ、内容を審査しているのが支払基金と呼ばれる組織で、医療機関は月に一度、前月の患者ごとの診療点数を計算し「レセプト」として支払基金に提出することになります。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) レセプトには、請求する診療点数のほか、医療機関の情報、被保険者の情報(氏名などの基本情報、加入している保険者情報)、診療行為や傷病名に関する情報などが含まれています。 「レセプト」には紙と電子データとありますが、現在は原則として電子レセプトを提出することが求められているそうです( 電子レセプト請求に係る猶予措置及び免除措置について|社会保険診療報酬支払基金 )。 電子レセプトとレセ電ビューア 電子レセプトについての仕様は支払基金によって公開されています。今回は「 電子レセプト作成の手引き 」という資料を元に「医科」のレセプトについて調べて発表しました。 発表資料はこちら。 電子レセプトの実体は CSV 形式のシンプルなテキストファイルです(拡張子は UKE なので、UKE ファイルと呼ぶこともあるようです)。ただ、電子レセプトの仕様を把握したとしても、やはり CSV ファイルを見て内容を把握するのは至難の業です。ファイル上では診療行為や医薬品、傷病名はコードとして表現されているため、 マスタデータ を参照しなければその内容まで理解することはできないからです。 そこで登場するのが、レセ電ビューアというツールで、ORCA Project によって公開されているフリーの電子レセプトビューアです。「レセ電=電子レセプト」ですね。 ORCA Project: レセ電ビューワ 日本医師会開発・日医標準レセプトソフトウェアのサイトです www.orca.med.or.jp (※上記ページでは「レセ電ビューア」と「レセ電ビューワ」が混在していますが、本ブログでは「レセ電ビューア」で統一しています) レセ電ビューアは Windows と Ubuntu で動作し、上述した UKE ファイルを読み込み、見やすく表示してくれる便利ツールです。ここからはレセ電ビューアをインストールし、電子レセプトを読み込んで表示するところまでを紹介したいと思います。 レセ電ビューアのインストール レセ電ビューアは Windows と Ubuntu 上で動作しますので、まずは Ubuntu 環境を準備します。私は VirtualBox 上に Ubuntu 環境を用意しました。 公式のインストールマニュアル( ubuntu 環境へのレセ電ビューアインストール )に基づいて作業していきます。 # Keyring と apt-line の追加 $ sudo su $ wget -q https://ftp.orca.med.or.jp/pub/ubuntu/archive.key $ apt-key add archive.key $ wget -q -O \ /etc/apt/sources.list.d/jma-receipt-xenial50.list \ https://ftp.orca.med.or.jp/pub/ubuntu/jma-receipt-xenial50.list $ apt-get update $ apt-get dist-upgrade # レセ電ビューアパッケージインストール $ apt-get install jma-receview $ apt-get install jma-receview-server # レセ電ビューア起動 $ jma-receview レセ電ビューアの設定 電子レセプトに含まれる診療行為などのコードに対応するマスタデータを参照するため、日レセ(jma-receipt)の DB を利用することができます。今回は DBFile 形式で DB に接続します。レセ電ビューアに付属するスクリプトを実行し、jma-receipt の DB から必要なテーブルをダンプすることができます。このファイルをレセ電ビューアに設定することで、電子レセプトの表示がよりわかりやすくなります。 # レセ電ビューアで使う DBFile を作る # https://ftp.orca.med.or.jp/pub/receview/manual/jma-receview.pdf # 「2.7.4 DBFile の作成方法」 $ sudo su orca $ ls -la /usr/share/jma-receview/db/ $ mkdir /var/tmp/dbfile $ cp /usr/share/jma-receview/db/make_dbfile.sh /var/tmp/dbfile/ $ cd /var/tmp/dbfile/ $ sh ./make_dbfile.sh 20170101 $ ls -lh 合計 6.9M -rwxr-xr-x 1 orca orca 2.9K 5 月 18 14:55 make_dbfile.sh -rw-r--r-- 1 orca orca 1.2M 5 月 18 14:57 tbl_byomei.rdb -rw-r--r-- 1 orca orca 89 5 月 18 14:57 tbl_dbkanri.rdb -rw-r--r-- 1 orca orca 330K 5 月 18 14:57 tbl_hknjainf.rdb -rw-r--r-- 1 orca orca 9.6K 5 月 18 14:57 tbl_labor_sio.rdb -rw-r--r-- 1 orca orca 343K 5 月 18 14:57 tbl_syskanri.rdb -rw-r--r-- 1 orca orca 5.1M 5 月 18 14:57 tbl_tensu.rdb 接続設定で「DBFile」を選択し、先ほど作成した DBFile を選択します。 レセ電ビューア近影 「ファイル」から UKE ファイルを開くと、レセプトに基づいて患者基本情報、保険・公費情報、診療行為情報のほか、紙レセプトのプレビューや患者単位での電子レセプトを表示することができます。また、「編集モード」に切り替えることで患者情報や病名の編集が可能で、編集した内容でレセプトを再出力することもできるようです。 おまけ ここまで紹介してきたレセ電ビューアですが、調べてみるとどうやら Ruby で実装されているようです。これらのコードを読んでいくことで新たな地平を開くことができるかもしれません。 $ dpkg -L jma-receview | grep ruby /usr/lib/ruby /usr/lib/ruby/2.3.0 /usr/lib/ruby/2.3.0/jma /usr/lib/ruby/2.3.0/jma/receview /usr/lib/ruby/2.3.0/jma/receview/menu.rb /usr/lib/ruby/2.3.0/jma/receview/intconv.rb /usr/lib/ruby/2.3.0/jma/receview/base.rb /usr/lib/ruby/2.3.0/jma/receview/dbfile_lib.rb /usr/lib/ruby/2.3.0/jma/receview/gui.rb /usr/lib/ruby/2.3.0/jma/receview/yearconv.rb /usr/lib/ruby/2.3.0/jma/receview/exception.rb /usr/lib/ruby/2.3.0/jma/receview/api.rb /usr/lib/ruby/2.3.0/jma/receview/config.rb /usr/lib/ruby/2.3.0/jma/receview/image.rb /usr/lib/ruby/2.3.0/jma/receview/dialog.rb /usr/lib/ruby/2.3.0/jma/receview/upstart.rb /usr/lib/ruby/2.3.0/jma/receview/dbslib.rb /usr/lib/ruby/2.3.0/jma/receview/thread.rb /usr/lib/ruby/2.3.0/jma/receview/receview.rb /usr/lib/ruby/2.3.0/jma/receview/sickname_edit.rb /usr/lib/ruby/2.3.0/jma/receview/dayconv.rb /usr/lib/ruby/2.3.0/jma/receview/isoimage.rb /usr/lib/ruby/2.3.0/jma/receview/help.rb /usr/lib/ruby/2.3.0/jma/receview/other_csv.rb /usr/lib/ruby/2.3.0/jma/receview/print.rb /usr/lib/ruby/2.3.0/jma/receview/env.rb /usr/lib/ruby/2.3.0/jma/receview/generation.rb /usr/lib/ruby/2.3.0/jma/receview/red2cairo.rb /usr/lib/ruby/2.3.0/jma/receview/strconv.rb /usr/lib/ruby/2.3.0/jma/receview/log.rb /usr/lib/ruby/2.3.0/jma/receview/hokenconv.rb /usr/lib/ruby/2.3.0/jma/receview/keybind.rb /usr/lib/ruby/2.3.0/jma/receview/version.rb /usr/lib/ruby/2.3.0/jma/receview/gtk2_fix.rb /usr/lib/ruby/2.3.0/jma/receview/preview_widget.rb /usr/lib/ruby/2.3.0/jma/receview/command.rb # /usr/bin/jma-receview も ruby で書かれてました(12000 行以上ある…) まとめ 今回は電子レセプトとレセ電ビューアについて、簡単に紹介しました。 わたしたちの生活とは切り離せない「医療」に関するシステムや仕様は、意外と一般公開されているものもあり、誰でも触れることができます。ただ、動作環境が制限されていたり、インターネット界隈のエンジニアがよく目にする技術とは異なるスタックで構築されていたり、それなりにハードルがあるように感じています。 これらのハードルを下げ、より多くの人が「調べてみよう」「ちょっと触ってみよう」と思うようになれば、医療に関わるシステムや仕様もよりシンプルで使いやすいものになり、ひいては各医療問題の解決・患者体験の改善につながっていくのではないかな、メドレーがつなげていきたいなと思っています。 最後はいいことを言って締めたい性分なのですが、いかがだったでしょうか。 ここまでお読みいただき、ありがとうございました。 お知らせ メドレーの開発にご興味ある方は、こちらからご連絡ください。 メンバーのストーリー | 株式会社メドレー メンバーのストーリー 家族や友人が病気になった時に救いの手を差しのべる医療の力。... www.medley.jp 7/27 に開催されるデブサミ 2018Summer に協賛させていただきます。ぜひ遊びにいらしてください。 Developers Summit 2018 Summer ITエンジニアの祭典「Developers Summit 2018 Summer」(デブサミ2018夏)は、2018年7月27日に開催! event.shoeisha.jp
アバター
こんにちは、開発本部の竹内です。最近子どものプリンセスへの強い憧れに若干引いております。 さて先日、TechLunch という社内勉強会で「電子レセプト」について話しましたので、こちらでも簡単に紹介させていただきます。 レセプトとは ところで、みなさまは「レセプト」についてご存知でしょうか?私はメドレーに入社するまで知りませんでした。 レセプトとは医療機関が支払基金へ診療報酬を請求するための明細書情報のことです。 と言っても、初めて聞かれる方もいらっしゃると思いますので、医療機関におけるお金の流れとともに簡単に説明します。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) 医療機関は「診療」の対価として、被保険者等(≒ 患者)からお金を受け取るわけですが、被保険者の加入する保険や公費によってその額は変わります。負担割合が 3 割の場合、残りの 7 割を被保険者が加入する保険組合などへ請求する必要があります。 この保険組合などへの請求を取りまとめ、内容を審査しているのが支払基金と呼ばれる組織で、医療機関は月に一度、前月の患者ごとの診療点数を計算し「レセプト」として支払基金に提出することになります。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) レセプトには、請求する診療点数のほか、医療機関の情報、被保険者の情報(氏名などの基本情報、加入している保険者情報)、診療行為や傷病名に関する情報などが含まれています。 「レセプト」には紙と電子データとありますが、現在は原則として電子レセプトを提出することが求められているそうです( 電子レセプト請求に係る猶予措置及び免除措置について|社会保険診療報酬支払基金 )。 電子レセプトとレセ電ビューア 電子レセプトについての仕様は支払基金によって公開されています。今回は「 電子レセプト作成の手引き 」という資料を元に「医科」のレセプトについて調べて発表しました。 発表資料はこちら。 電子レセプトの実体は CSV 形式のシンプルなテキストファイルです(拡張子は UKE なので、UKE ファイルと呼ぶこともあるようです)。ただ、電子レセプトの仕様を把握したとしても、やはり CSV ファイルを見て内容を把握するのは至難の業です。ファイル上では診療行為や医薬品、傷病名はコードとして表現されているため、 マスタデータ を参照しなければその内容まで理解することはできないからです。 そこで登場するのが、レセ電ビューアというツールで、ORCA Project によって公開されているフリーの電子レセプトビューアです。「レセ電=電子レセプト」ですね。 ORCA Project: レセ電ビューワ 日本医師会開発・日医標準レセプトソフトウェアのサイトです www.orca.med.or.jp (※上記ページでは「レセ電ビューア」と「レセ電ビューワ」が混在していますが、本ブログでは「レセ電ビューア」で統一しています) レセ電ビューアは Windows と Ubuntu で動作し、上述した UKE ファイルを読み込み、見やすく表示してくれる便利ツールです。ここからはレセ電ビューアをインストールし、電子レセプトを読み込んで表示するところまでを紹介したいと思います。 レセ電ビューアのインストール レセ電ビューアは Windows と Ubuntu 上で動作しますので、まずは Ubuntu 環境を準備します。私は VirtualBox 上に Ubuntu 環境を用意しました。 公式のインストールマニュアル( ubuntu 環境へのレセ電ビューアインストール )に基づいて作業していきます。 # Keyring と apt-line の追加 $ sudo su $ wget -q https://ftp.orca.med.or.jp/pub/ubuntu/archive.key $ apt-key add archive.key $ wget -q -O \ /etc/apt/sources.list.d/jma-receipt-xenial50.list \ https://ftp.orca.med.or.jp/pub/ubuntu/jma-receipt-xenial50.list $ apt-get update $ apt-get dist-upgrade # レセ電ビューアパッケージインストール $ apt-get install jma-receview $ apt-get install jma-receview-server # レセ電ビューア起動 $ jma-receview レセ電ビューアの設定 電子レセプトに含まれる診療行為などのコードに対応するマスタデータを参照するため、日レセ(jma-receipt)の DB を利用することができます。今回は DBFile 形式で DB に接続します。レセ電ビューアに付属するスクリプトを実行し、jma-receipt の DB から必要なテーブルをダンプすることができます。このファイルをレセ電ビューアに設定することで、電子レセプトの表示がよりわかりやすくなります。 # レセ電ビューアで使う DBFile を作る # https://ftp.orca.med.or.jp/pub/receview/manual/jma-receview.pdf # 「2.7.4 DBFile の作成方法」 $ sudo su orca $ ls -la /usr/share/jma-receview/db/ $ mkdir /var/tmp/dbfile $ cp /usr/share/jma-receview/db/make_dbfile.sh /var/tmp/dbfile/ $ cd /var/tmp/dbfile/ $ sh ./make_dbfile.sh 20170101 $ ls -lh 合計 6.9M -rwxr-xr-x 1 orca orca 2.9K 5 月 18 14:55 make_dbfile.sh -rw-r--r-- 1 orca orca 1.2M 5 月 18 14:57 tbl_byomei.rdb -rw-r--r-- 1 orca orca 89 5 月 18 14:57 tbl_dbkanri.rdb -rw-r--r-- 1 orca orca 330K 5 月 18 14:57 tbl_hknjainf.rdb -rw-r--r-- 1 orca orca 9.6K 5 月 18 14:57 tbl_labor_sio.rdb -rw-r--r-- 1 orca orca 343K 5 月 18 14:57 tbl_syskanri.rdb -rw-r--r-- 1 orca orca 5.1M 5 月 18 14:57 tbl_tensu.rdb 接続設定で「DBFile」を選択し、先ほど作成した DBFile を選択します。 レセ電ビューア近影 「ファイル」から UKE ファイルを開くと、レセプトに基づいて患者基本情報、保険・公費情報、診療行為情報のほか、紙レセプトのプレビューや患者単位での電子レセプトを表示することができます。また、「編集モード」に切り替えることで患者情報や病名の編集が可能で、編集した内容でレセプトを再出力することもできるようです。 おまけ ここまで紹介してきたレセ電ビューアですが、調べてみるとどうやら Ruby で実装されているようです。これらのコードを読んでいくことで新たな地平を開くことができるかもしれません。 $ dpkg -L jma-receview | grep ruby /usr/lib/ruby /usr/lib/ruby/2.3.0 /usr/lib/ruby/2.3.0/jma /usr/lib/ruby/2.3.0/jma/receview /usr/lib/ruby/2.3.0/jma/receview/menu.rb /usr/lib/ruby/2.3.0/jma/receview/intconv.rb /usr/lib/ruby/2.3.0/jma/receview/base.rb /usr/lib/ruby/2.3.0/jma/receview/dbfile_lib.rb /usr/lib/ruby/2.3.0/jma/receview/gui.rb /usr/lib/ruby/2.3.0/jma/receview/yearconv.rb /usr/lib/ruby/2.3.0/jma/receview/exception.rb /usr/lib/ruby/2.3.0/jma/receview/api.rb /usr/lib/ruby/2.3.0/jma/receview/config.rb /usr/lib/ruby/2.3.0/jma/receview/image.rb /usr/lib/ruby/2.3.0/jma/receview/dialog.rb /usr/lib/ruby/2.3.0/jma/receview/upstart.rb /usr/lib/ruby/2.3.0/jma/receview/dbslib.rb /usr/lib/ruby/2.3.0/jma/receview/thread.rb /usr/lib/ruby/2.3.0/jma/receview/receview.rb /usr/lib/ruby/2.3.0/jma/receview/sickname_edit.rb /usr/lib/ruby/2.3.0/jma/receview/dayconv.rb /usr/lib/ruby/2.3.0/jma/receview/isoimage.rb /usr/lib/ruby/2.3.0/jma/receview/help.rb /usr/lib/ruby/2.3.0/jma/receview/other_csv.rb /usr/lib/ruby/2.3.0/jma/receview/print.rb /usr/lib/ruby/2.3.0/jma/receview/env.rb /usr/lib/ruby/2.3.0/jma/receview/generation.rb /usr/lib/ruby/2.3.0/jma/receview/red2cairo.rb /usr/lib/ruby/2.3.0/jma/receview/strconv.rb /usr/lib/ruby/2.3.0/jma/receview/log.rb /usr/lib/ruby/2.3.0/jma/receview/hokenconv.rb /usr/lib/ruby/2.3.0/jma/receview/keybind.rb /usr/lib/ruby/2.3.0/jma/receview/version.rb /usr/lib/ruby/2.3.0/jma/receview/gtk2_fix.rb /usr/lib/ruby/2.3.0/jma/receview/preview_widget.rb /usr/lib/ruby/2.3.0/jma/receview/command.rb # /usr/bin/jma-receview も ruby で書かれてました(12000 行以上ある…) まとめ 今回は電子レセプトとレセ電ビューアについて、簡単に紹介しました。 わたしたちの生活とは切り離せない「医療」に関するシステムや仕様は、意外と一般公開されているものもあり、誰でも触れることができます。ただ、動作環境が制限されていたり、インターネット界隈のエンジニアがよく目にする技術とは異なるスタックで構築されていたり、それなりにハードルがあるように感じています。 これらのハードルを下げ、より多くの人が「調べてみよう」「ちょっと触ってみよう」と思うようになれば、医療に関わるシステムや仕様もよりシンプルで使いやすいものになり、ひいては各医療問題の解決・患者体験の改善につながっていくのではないかな、メドレーがつなげていきたいなと思っています。 最後はいいことを言って締めたい性分なのですが、いかがだったでしょうか。 ここまでお読みいただき、ありがとうございました。 お知らせ メドレーの開発にご興味ある方は、こちらからご連絡ください。 メンバーのストーリー | 株式会社メドレー メンバーのストーリー 家族や友人が病気になった時に救いの手を差しのべる医療の力。... www.medley.jp 7/27 に開催されるデブサミ 2018Summer に協賛させていただきます。ぜひ遊びにいらしてください。 Developers Summit 2018 Summer ITエンジニアの祭典「Developers Summit 2018 Summer」(デブサミ2018夏)は、2018年7月27日に開催! event.shoeisha.jp
アバター
こんにちは、開発本部の竹内です。最近子どものプリンセスへの強い憧れに若干引いております。 さて先日、TechLunch という社内勉強会で「電子レセプト」について話しましたので、こちらでも簡単に紹介させていただきます。 レセプトとは ところで、みなさまは「レセプト」についてご存知でしょうか?私はメドレーに入社するまで知りませんでした。 レセプトとは医療機関が支払基金へ診療報酬を請求するための明細書情報のことです。 と言っても、初めて聞かれる方もいらっしゃると思いますので、医療機関におけるお金の流れとともに簡単に説明します。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) 医療機関は「診療」の対価として、被保険者等(≒ 患者)からお金を受け取るわけですが、被保険者の加入する保険や公費によってその額は変わります。負担割合が 3 割の場合、残りの 7 割を被保険者が加入する保険組合などへ請求する必要があります。 この保険組合などへの請求を取りまとめ、内容を審査しているのが支払基金と呼ばれる組織で、医療機関は月に一度、前月の患者ごとの診療点数を計算し「レセプト」として支払基金に提出することになります。 (支払基金ってどんなところ?|社会保険診療報酬支払基金より) レセプトには、請求する診療点数のほか、医療機関の情報、被保険者の情報(氏名などの基本情報、加入している保険者情報)、診療行為や傷病名に関する情報などが含まれています。 「レセプト」には紙と電子データとありますが、現在は原則として電子レセプトを提出することが求められているそうです( 電子レセプト請求に係る猶予措置及び免除措置について|社会保険診療報酬支払基金 )。 電子レセプトとレセ電ビューア 電子レセプトについての仕様は支払基金によって公開されています。今回は「 電子レセプト作成の手引き 」という資料を元に「医科」のレセプトについて調べて発表しました。 発表資料はこちら。 電子レセプトの実体は CSV 形式のシンプルなテキストファイルです(拡張子は UKE なので、UKE ファイルと呼ぶこともあるようです)。ただ、電子レセプトの仕様を把握したとしても、やはり CSV ファイルを見て内容を把握するのは至難の業です。ファイル上では診療行為や医薬品、傷病名はコードとして表現されているため、 マスタデータ を参照しなければその内容まで理解することはできないからです。 そこで登場するのが、レセ電ビューアというツールで、ORCA Project によって公開されているフリーの電子レセプトビューアです。「レセ電=電子レセプト」ですね。 ORCA Project: レセ電ビューワ 日本医師会開発・日医標準レセプトソフトウェアのサイトです www.orca.med.or.jp (※上記ページでは「レセ電ビューア」と「レセ電ビューワ」が混在していますが、本ブログでは「レセ電ビューア」で統一しています) レセ電ビューアは Windows と Ubuntu で動作し、上述した UKE ファイルを読み込み、見やすく表示してくれる便利ツールです。ここからはレセ電ビューアをインストールし、電子レセプトを読み込んで表示するところまでを紹介したいと思います。 レセ電ビューアのインストール レセ電ビューアは Windows と Ubuntu 上で動作しますので、まずは Ubuntu 環境を準備します。私は VirtualBox 上に Ubuntu 環境を用意しました。 公式のインストールマニュアル( ubuntu 環境へのレセ電ビューアインストール )に基づいて作業していきます。 # Keyring と apt-line の追加 $ sudo su $ wget -q https://ftp.orca.med.or.jp/pub/ubuntu/archive.key $ apt-key add archive.key $ wget -q -O \ /etc/apt/sources.list.d/jma-receipt-xenial50.list \ https://ftp.orca.med.or.jp/pub/ubuntu/jma-receipt-xenial50.list $ apt-get update $ apt-get dist-upgrade # レセ電ビューアパッケージインストール $ apt-get install jma-receview $ apt-get install jma-receview-server # レセ電ビューア起動 $ jma-receview レセ電ビューアの設定 電子レセプトに含まれる診療行為などのコードに対応するマスタデータを参照するため、日レセ(jma-receipt)の DB を利用することができます。今回は DBFile 形式で DB に接続します。レセ電ビューアに付属するスクリプトを実行し、jma-receipt の DB から必要なテーブルをダンプすることができます。このファイルをレセ電ビューアに設定することで、電子レセプトの表示がよりわかりやすくなります。 # レセ電ビューアで使う DBFile を作る # https://ftp.orca.med.or.jp/pub/receview/manual/jma-receview.pdf # 「2.7.4 DBFile の作成方法」 $ sudo su orca $ ls -la /usr/share/jma-receview/db/ $ mkdir /var/tmp/dbfile $ cp /usr/share/jma-receview/db/make_dbfile.sh /var/tmp/dbfile/ $ cd /var/tmp/dbfile/ $ sh ./make_dbfile.sh 20170101 $ ls -lh 合計 6.9M -rwxr-xr-x 1 orca orca 2.9K 5 月 18 14:55 make_dbfile.sh -rw-r--r-- 1 orca orca 1.2M 5 月 18 14:57 tbl_byomei.rdb -rw-r--r-- 1 orca orca 89 5 月 18 14:57 tbl_dbkanri.rdb -rw-r--r-- 1 orca orca 330K 5 月 18 14:57 tbl_hknjainf.rdb -rw-r--r-- 1 orca orca 9.6K 5 月 18 14:57 tbl_labor_sio.rdb -rw-r--r-- 1 orca orca 343K 5 月 18 14:57 tbl_syskanri.rdb -rw-r--r-- 1 orca orca 5.1M 5 月 18 14:57 tbl_tensu.rdb 接続設定で「DBFile」を選択し、先ほど作成した DBFile を選択します。 レセ電ビューア近影 「ファイル」から UKE ファイルを開くと、レセプトに基づいて患者基本情報、保険・公費情報、診療行為情報のほか、紙レセプトのプレビューや患者単位での電子レセプトを表示することができます。また、「編集モード」に切り替えることで患者情報や病名の編集が可能で、編集した内容でレセプトを再出力することもできるようです。 おまけ ここまで紹介してきたレセ電ビューアですが、調べてみるとどうやら Ruby で実装されているようです。これらのコードを読んでいくことで新たな地平を開くことができるかもしれません。 $ dpkg -L jma-receview | grep ruby /usr/lib/ruby /usr/lib/ruby/2.3.0 /usr/lib/ruby/2.3.0/jma /usr/lib/ruby/2.3.0/jma/receview /usr/lib/ruby/2.3.0/jma/receview/menu.rb /usr/lib/ruby/2.3.0/jma/receview/intconv.rb /usr/lib/ruby/2.3.0/jma/receview/base.rb /usr/lib/ruby/2.3.0/jma/receview/dbfile_lib.rb /usr/lib/ruby/2.3.0/jma/receview/gui.rb /usr/lib/ruby/2.3.0/jma/receview/yearconv.rb /usr/lib/ruby/2.3.0/jma/receview/exception.rb /usr/lib/ruby/2.3.0/jma/receview/api.rb /usr/lib/ruby/2.3.0/jma/receview/config.rb /usr/lib/ruby/2.3.0/jma/receview/image.rb /usr/lib/ruby/2.3.0/jma/receview/dialog.rb /usr/lib/ruby/2.3.0/jma/receview/upstart.rb /usr/lib/ruby/2.3.0/jma/receview/dbslib.rb /usr/lib/ruby/2.3.0/jma/receview/thread.rb /usr/lib/ruby/2.3.0/jma/receview/receview.rb /usr/lib/ruby/2.3.0/jma/receview/sickname_edit.rb /usr/lib/ruby/2.3.0/jma/receview/dayconv.rb /usr/lib/ruby/2.3.0/jma/receview/isoimage.rb /usr/lib/ruby/2.3.0/jma/receview/help.rb /usr/lib/ruby/2.3.0/jma/receview/other_csv.rb /usr/lib/ruby/2.3.0/jma/receview/print.rb /usr/lib/ruby/2.3.0/jma/receview/env.rb /usr/lib/ruby/2.3.0/jma/receview/generation.rb /usr/lib/ruby/2.3.0/jma/receview/red2cairo.rb /usr/lib/ruby/2.3.0/jma/receview/strconv.rb /usr/lib/ruby/2.3.0/jma/receview/log.rb /usr/lib/ruby/2.3.0/jma/receview/hokenconv.rb /usr/lib/ruby/2.3.0/jma/receview/keybind.rb /usr/lib/ruby/2.3.0/jma/receview/version.rb /usr/lib/ruby/2.3.0/jma/receview/gtk2_fix.rb /usr/lib/ruby/2.3.0/jma/receview/preview_widget.rb /usr/lib/ruby/2.3.0/jma/receview/command.rb # /usr/bin/jma-receview も ruby で書かれてました(12000 行以上ある…) まとめ 今回は電子レセプトとレセ電ビューアについて、簡単に紹介しました。 わたしたちの生活とは切り離せない「医療」に関するシステムや仕様は、意外と一般公開されているものもあり、誰でも触れることができます。ただ、動作環境が制限されていたり、インターネット界隈のエンジニアがよく目にする技術とは異なるスタックで構築されていたり、それなりにハードルがあるように感じています。 これらのハードルを下げ、より多くの人が「調べてみよう」「ちょっと触ってみよう」と思うようになれば、医療に関わるシステムや仕様もよりシンプルで使いやすいものになり、ひいては各医療問題の解決・患者体験の改善につながっていくのではないかな、メドレーがつなげていきたいなと思っています。 最後はいいことを言って締めたい性分なのですが、いかがだったでしょうか。 ここまでお読みいただき、ありがとうございました。 お知らせ メドレーの開発にご興味ある方は、こちらからご連絡ください。 メンバーのストーリー | 株式会社メドレー メンバーのストーリー 家族や友人が病気になった時に救いの手を差しのべる医療の力。... www.medley.jp 7/27 に開催されるデブサミ 2018Summer に協賛させていただきます。ぜひ遊びにいらしてください。 Developers Summit 2018 Summer ITエンジニアの祭典「Developers Summit 2018 Summer」(デブサミ2018夏)は、2018年7月27日に開催! event.shoeisha.jp
アバター
みなさん、こんにちは。開発本部エンジニアの平木です。こちらのブログの投稿自体はほぼ 1 年ぶりになりそうな勢いですが、みなさまお元気でしょうか? 弊社で定期的に開催してる社内勉強会 TechLunch で自分の順番が回ってきたため、どうしようか迷った末に JavaScript AST ことはじめ という発表をしたので、そのことについて書いていきます。 なぜ JavaScript AST について話そうと思ったのか 現在、弊社のエンジニアメンバーのバックグラウンドで一番多数派なのは「元サーバサイドエンジニア」です。もちろん、業務ではサーバサイド・フロントエンド・ネイティブアプリとバックグラウンドに関わらず、必要に応じて分け隔てなく開発しています。 とはいえ、ちゃんとサービス開発自体はできるとしても、やはり得意な分野以外で基本原理など含めて把握して開発できるかというと、ちょっと難しいところもあります。しかし、そういった基本原理なんかを知っていると、その言語やツールなどの理解が捗るのは確かですよね。 そんな中、弊社で開発している人間がほぼ全て恩恵を受けているはずなのに、具体的にどんな風に動いているのかが一番分かりにくいであろう Babel ひいては JavaScript AST の話をしたら、まあ興味持って話を聞いてくれるかなーということででこのテーマを選んだ次第です。 どのように伝えるか 自分は JavaScript AST についてとても詳しいわけではないのですが、以前仕事で acorn を使ってコンバータみたいなのを作ったりしていたので、それなりに興味は持っているという人間です。 ですので、どうやって紹介をしようかと悩んだ結果、ほぼ全面的に AST Explore に頼っていくというスタイルにしました。AST Explore は本当に最高ですね。前述の仕事をしていたときはこんな便利なツールはなかったんで、ひたすら AST に変換するコード書いては出来た AST を見て、それをトランスフォームさせて結果と睨めっこして試行錯誤するという毎日でした。 ということで、当日のスライドはこちらになります。 スライドで紹介したデモはそれぞれこちらになります。 https://astexplorer.net/#/gist/82742676286b2dced595ce36cdeb8aae/latest https://astexplorer.net/#/gist/52d871c2f3a8d9cefc68d17badf4f165/latest 今回伝えたかったこと まず、AST が JavaScript の発展にとても寄与しているものだということを知ってもらいたかったため、JavaScript AST の今までの簡単な流れや、現在どのような形で使われているのかの説明をしました。(個人的に Node.js の誕生と JavaScript AST の存在が現在のフロントエンドの発展にとても重要だと思っているので) 最初のうちは聞いてる人も「何の話なんだろ…」感がありましたが、やはり実際に自分が使っているツールなどに使われているという説明をしたあとだと、聞いているメンバーも俄然興味が出てきたという雰囲気になった気がします(当社比)。 AST の文法などは自分が説明するよりは、ちゃんと資料が揃っているので必要な部分以外簡略化しました。逆にちょっと端折りすぎたきらいもありますが、興味を持ったときに何となくでも調べる道標くらいにはなるかなと考えています。 次に知ってもらいたかったのは、やろうと思えば Babel のプラグインなんかも AST で作れちゃいますよということでした。仮にいきなり「Babel プラグイン作りましょう」となったとしても正直あまりピンと来ないと思いますが、どういう原理でプロダクトが動いているのか?が分かると、 babel-handbook などを読んでも理解が進むのではないかと思います。 AST Explore のこと このように今回の発表で全面的に活躍した AST Explore ですが、TechLunch 中でも軽い説明だけで使ってしまったので、使いかたなど簡単にご紹介していきます。 AST Explore とは AST Explore は Felix Kling さんが、2014 年頃から開発しているプロダクトです。 余談ですが、Felix さんは現在 Facebook で働いていらっしゃるようで、 facebook/jscodeshift や reactjs/react-docgen なんかの開発にも携わっていらっしゃる模様。(react-docgen は babylon を使っているようですが) ここまで書いてきた通りに、このツールは色々な言語をコピペするだけで AST をツリー形式で分かりやすく表示したり、トランスフォームさせることができたりするという AST を触るには大変便利なツールです。去年の v2.0 のアップデートにより、セーブすると gist を匿名で作ってくれてリンクが生成されるなどの便利機能が付きました。 プロジェクトの README に書いていますが、パーサだけであれば、かなりパースできるものが多く、また JavaScript / CSS / 正規表現 / Handlebars に関してはトランスフォームまでできるようになっています。 README から抜粋すると以下のような感じです。 AST Explore でパースできるもの CSS: cssom csstree postcss + postcss-safe-parser & postcss-scss rework GraphQL Graphviz: redot Handlebars: glimmer handlebars HTML: htmlparser2 parse5 ICU JavaScript: acorn + acorn-jsx babel-eslint babylon espree esformatter esprima flow-parser recast shift traceur typescript typescript-eslint-parser uglify-js JSON Lua: luaparse Markdown: remark PHP php-parser Regular Expressions: regexp-tree Scala Scalameta SQL: sqlite-parser WebIDL YAML 実験的だったりするけどパースできるもの ES6: arrow functions, destructuring, classes, … ES7 proposals: async/await, object rest / spread, … JSX Typed JavaScript Flow and TypeScript SASS パースしたものをトランスフォームできるもの JavaScript babel (v5, v6) ESLint (v1, v2, v3) jscodeshift tslint CSS postcss Regular Expressions regexp-tree Handlebars glimmer AST Explore の使い方の簡単な解説 サイトにアクセスするとこのような画面になっているはずです。 メイン画面 JavaScript にフォーカスして解説していきますと、左ペインが AST に変換したいソースコード、右ペインが変換後の AST をツリー構造で見せています。 初期表示時に、左ペインのソースコードをクリックすると該当箇所の AST ツリーが展開してハイライトします。また右ペインをポイントするとソースコードの該当箇所がハイライトします。お互いの関係が分かりやすい仕様になっています。 本来 JavaScript AST で生成されるものは JSON オブジェクトになりますが、右ペインの上の Tree と JSON のタブを切りかえることによって AST の表示を変更することができます。 ヘッダー部分 ヘッダーに色々な機能がまとまっています。 初期表示では以下のようになっているはずです。 Snippet 俗にいうファイルメニュー。 新規作成・(gist への)セーブ・(gist の)フォーク・シェアがある JavaScript パースする言語選択 ここで AST にしたい言語を切り替える 選んだ言語によって Transform が使えなくなる acorn パーサ選択 各言語のパーサを切り替える Transform トランスフォーマ選択 選択した言語にトランスフォーマがあれば選択できるようになる こちらを選択すると 2 ペインだったのが 4 ペインになる(後述) default ソースコードなどを書くときのキーバインド選択 default / Vim / Emacs / Sublime の 4 種類がある うれしみがあります ? ヘルプ GitHub の README に飛ばされるだけです… JavaScript のトランスフォーム 先程説明したトランスフォームを選ぶと、メインの画面が 4 画面になります。 今までの ソースコード と AST ツリー は変わりませんが、下に 2 つペインが追加されます。 左下がトランスフォーマコード、右下がトランスフォームした後のソースコードとなっています。 左下のトランスフォーマを色々触っていくと左上のソースコードが変換されて、右下に表示されるという流れですね。 以下 JavaScript コードのトランスフォームする際の Tips です jscodeshift を選択すると Ctrl + Space で jscodeshift の補完が効くようになります babel-plugin-macro を選ぶとトランスフォーマのコード自体がそのまま babel-plugin として使えるようになるので、プラグイン作るときに捗るはずです まとめ 後で参加メンバーに聞いてみましたが、伝えたかったことは、ちゃんと伝わっていた様子だったので安心しました。最後の Vue.js の v1 から v2 のマイグレーションのデモは紹介した結果、JavaScript AST 便利そうという感触になったと思います。 現在弊社のプロダクトで、JavaScript AST をガッツリと使うようなプロジェクトはないのですが、Babel などは全プロダクトで使用しており、結構プラグインを多用しているところもあるので、いざというときの基礎知識として覚えておいて損はないはずです。 こういった部分の勉強も欠かさず続けていきたいと改めて思う機会にもなりました。 弊社の開発文化など気になる方は、こちらからどうぞ。 メンバーのストーリー | 株式会社メドレー メンバーのストーリー 家族や友人が病気になった時に救いの手を差しのべる医療の力。... www.medley.jp
アバター
みなさん、こんにちは。開発本部エンジニアの平木です。こちらのブログの投稿自体はほぼ 1 年ぶりになりそうな勢いですが、みなさまお元気でしょうか? 弊社で定期的に開催してる社内勉強会 TechLunch で自分の順番が回ってきたため、どうしようか迷った末に JavaScript AST ことはじめ という発表をしたので、そのことについて書いていきます。 なぜ JavaScript AST について話そうと思ったのか 現在、弊社のエンジニアメンバーのバックグラウンドで一番多数派なのは「元サーバサイドエンジニア」です。もちろん、業務ではサーバサイド・フロントエンド・ネイティブアプリとバックグラウンドに関わらず、必要に応じて分け隔てなく開発しています。 とはいえ、ちゃんとサービス開発自体はできるとしても、やはり得意な分野以外で基本原理など含めて把握して開発できるかというと、ちょっと難しいところもあります。しかし、そういった基本原理なんかを知っていると、その言語やツールなどの理解が捗るのは確かですよね。 そんな中、弊社で開発している人間がほぼ全て恩恵を受けているはずなのに、具体的にどんな風に動いているのかが一番分かりにくいであろう Babel ひいては JavaScript AST の話をしたら、まあ興味持って話を聞いてくれるかなーということででこのテーマを選んだ次第です。 どのように伝えるか 自分は JavaScript AST についてとても詳しいわけではないのですが、以前仕事で acorn を使ってコンバータみたいなのを作ったりしていたので、それなりに興味は持っているという人間です。 ですので、どうやって紹介をしようかと悩んだ結果、ほぼ全面的に AST Explore に頼っていくというスタイルにしました。AST Explore は本当に最高ですね。前述の仕事をしていたときはこんな便利なツールはなかったんで、ひたすら AST に変換するコード書いては出来た AST を見て、それをトランスフォームさせて結果と睨めっこして試行錯誤するという毎日でした。 ということで、当日のスライドはこちらになります。 スライドで紹介したデモはそれぞれこちらになります。 https://astexplorer.net/#/gist/82742676286b2dced595ce36cdeb8aae/latest https://astexplorer.net/#/gist/52d871c2f3a8d9cefc68d17badf4f165/latest 今回伝えたかったこと まず、AST が JavaScript の発展にとても寄与しているものだということを知ってもらいたかったため、JavaScript AST の今までの簡単な流れや、現在どのような形で使われているのかの説明をしました。(個人的に Node.js の誕生と JavaScript AST の存在が現在のフロントエンドの発展にとても重要だと思っているので) 最初のうちは聞いてる人も「何の話なんだろ…」感がありましたが、やはり実際に自分が使っているツールなどに使われているという説明をしたあとだと、聞いているメンバーも俄然興味が出てきたという雰囲気になった気がします(当社比)。 AST の文法などは自分が説明するよりは、ちゃんと資料が揃っているので必要な部分以外簡略化しました。逆にちょっと端折りすぎたきらいもありますが、興味を持ったときに何となくでも調べる道標くらいにはなるかなと考えています。 次に知ってもらいたかったのは、やろうと思えば Babel のプラグインなんかも AST で作れちゃいますよということでした。仮にいきなり「Babel プラグイン作りましょう」となったとしても正直あまりピンと来ないと思いますが、どういう原理でプロダクトが動いているのか?が分かると、 babel-handbook などを読んでも理解が進むのではないかと思います。 AST Explore のこと このように今回の発表で全面的に活躍した AST Explore ですが、TechLunch 中でも軽い説明だけで使ってしまったので、使いかたなど簡単にご紹介していきます。 AST Explore とは AST Explore は Felix Kling さんが、2014 年頃から開発しているプロダクトです。 余談ですが、Felix さんは現在 Facebook で働いていらっしゃるようで、 facebook/jscodeshift や reactjs/react-docgen なんかの開発にも携わっていらっしゃる模様。(react-docgen は babylon を使っているようですが) ここまで書いてきた通りに、このツールは色々な言語をコピペするだけで AST をツリー形式で分かりやすく表示したり、トランスフォームさせることができたりするという AST を触るには大変便利なツールです。去年の v2.0 のアップデートにより、セーブすると gist を匿名で作ってくれてリンクが生成されるなどの便利機能が付きました。 プロジェクトの README に書いていますが、パーサだけであれば、かなりパースできるものが多く、また JavaScript / CSS / 正規表現 / Handlebars に関してはトランスフォームまでできるようになっています。 README から抜粋すると以下のような感じです。 AST Explore でパースできるもの CSS: cssom csstree postcss + postcss-safe-parser & postcss-scss rework GraphQL Graphviz: redot Handlebars: glimmer handlebars HTML: htmlparser2 parse5 ICU JavaScript: acorn + acorn-jsx babel-eslint babylon espree esformatter esprima flow-parser recast shift traceur typescript typescript-eslint-parser uglify-js JSON Lua: luaparse Markdown: remark PHP php-parser Regular Expressions: regexp-tree Scala Scalameta SQL: sqlite-parser WebIDL YAML 実験的だったりするけどパースできるもの ES6: arrow functions, destructuring, classes, … ES7 proposals: async/await, object rest / spread, … JSX Typed JavaScript Flow and TypeScript SASS パースしたものをトランスフォームできるもの JavaScript babel (v5, v6) ESLint (v1, v2, v3) jscodeshift tslint CSS postcss Regular Expressions regexp-tree Handlebars glimmer AST Explore の使い方の簡単な解説 サイトにアクセスするとこのような画面になっているはずです。 メイン画面 JavaScript にフォーカスして解説していきますと、左ペインが AST に変換したいソースコード、右ペインが変換後の AST をツリー構造で見せています。 初期表示時に、左ペインのソースコードをクリックすると該当箇所の AST ツリーが展開してハイライトします。また右ペインをポイントするとソースコードの該当箇所がハイライトします。お互いの関係が分かりやすい仕様になっています。 本来 JavaScript AST で生成されるものは JSON オブジェクトになりますが、右ペインの上の Tree と JSON のタブを切りかえることによって AST の表示を変更することができます。 ヘッダー部分 ヘッダーに色々な機能がまとまっています。 初期表示では以下のようになっているはずです。 Snippet 俗にいうファイルメニュー。 新規作成・(gist への)セーブ・(gist の)フォーク・シェアがある JavaScript パースする言語選択 ここで AST にしたい言語を切り替える 選んだ言語によって Transform が使えなくなる acorn パーサ選択 各言語のパーサを切り替える Transform トランスフォーマ選択 選択した言語にトランスフォーマがあれば選択できるようになる こちらを選択すると 2 ペインだったのが 4 ペインになる(後述) default ソースコードなどを書くときのキーバインド選択 default / Vim / Emacs / Sublime の 4 種類がある うれしみがあります ? ヘルプ GitHub の README に飛ばされるだけです… JavaScript のトランスフォーム 先程説明したトランスフォームを選ぶと、メインの画面が 4 画面になります。 今までの ソースコード と AST ツリー は変わりませんが、下に 2 つペインが追加されます。 左下がトランスフォーマコード、右下がトランスフォームした後のソースコードとなっています。 左下のトランスフォーマを色々触っていくと左上のソースコードが変換されて、右下に表示されるという流れですね。 以下 JavaScript コードのトランスフォームする際の Tips です jscodeshift を選択すると Ctrl + Space で jscodeshift の補完が効くようになります babel-plugin-macro を選ぶとトランスフォーマのコード自体がそのまま babel-plugin として使えるようになるので、プラグイン作るときに捗るはずです まとめ 後で参加メンバーに聞いてみましたが、伝えたかったことは、ちゃんと伝わっていた様子だったので安心しました。最後の Vue.js の v1 から v2 のマイグレーションのデモは紹介した結果、JavaScript AST 便利そうという感触になったと思います。 現在弊社のプロダクトで、JavaScript AST をガッツリと使うようなプロジェクトはないのですが、Babel などは全プロダクトで使用しており、結構プラグインを多用しているところもあるので、いざというときの基礎知識として覚えておいて損はないはずです。 こういった部分の勉強も欠かさず続けていきたいと改めて思う機会にもなりました。 弊社の開発文化など気になる方は、こちらからどうぞ。 https://www.medley.jp/recruit/creative.html
アバター