2019/03/01(金)ディジタル証明書の形式 pem と der

https:// 接続などでサーバ証明書を扱ったことがある方は、下記フォーマットを見たことがあると思います。
※現在、実運用で使っていませんが念のため一部端折っています。
-----BEGIN CERTIFICATE-----
MIIC4TCCAkqgAwIBAgIBADANBgkqhkiG9w0BAQUFADB4MQswCQYDVQQGEwJKUDER
MA8GA1UECBMISG9ra2FpZG8xGzAZBgNVBAoTEkJhc2UgS2VybmVsIENvIEx0ZDEg
MB4GA1UECxMXTmV0d29yayBPcHJhdGlvbiBDZW50ZXIxFzAVBgNVBAMTDkJhc2Ug
S2VybmVsIENBMB4XDTA4MDcxMzEzMTU0NVoXDTI4MDcxMzEzMTU0NVoweDELMAkG

・・・・一部省略・・・・

A1UEBhMCSlAxETAPBgNVBAgTCEhva2thaWRvMRswGQYDVQQKExJCYXNlIEtlcm5l
+6EEruiOlNKYnViRTdjEoAPRYfgM+eqjnfbVOxB0wEV7w5GjcqbGe9ePrEUs1wYS
ktnZC7H0p5H01z92udKE3BaCEACQ
-----END CERTIFICATE-----
これは、pem 形式と呼ばれるディジタル証明書で、通常はこの形式で管理します。
ところが、時々、 der 形式しか受け付けないディジタル証明書アプリケーションがあるのです。

今回、コントロールパネルのサーバ設定部分の一部が時代に合わなくなっていて、この部分の改良を施しているところで、その一環でサーバ証明書をある程度自力管理できるようにしているのですが、その過程でディジタル証明書の内容を解析する Perl モジュールに Crypt::X509 という便利なものがあってそれを使おうとしたら、このモジュールが der 形式しか対応していないのです。

もっと便利そうなものに Crypt::OpenSSL::X509 というのがあるが、今のところ、OpenSSL 1.x 系への対応があまり芳しくなく、避けざるを得ない状況。

der 形式はバイナリファイルなので扱いにくいのです。ただ、具体的中身まではよく知りません。
調べると、 pem 形式ファイルの -----BEGIN CERTIFICATE----- 行と、-----END CERTIFICATE----- 行を取っ払った残りの部分を、Base64 デコードしてあげればいいだけであることが判りました。

つまり、pem 形式は、der 形式で生成したディジタル証明書のデータ構造を Base64 エンコードして、-----BEGIN CERTIFICATE----- 行と、-----END CERTIFICATE----- 行を付け足しただけなのです。

ここまで判れば、ディジタル証明書の内容確認は、Perl でやる場合は以下の要領で出来ます:
#!/usr/local/bin/perl

use MIME::Base64 ;                                     # Base64ライブラリ使用宣言
use Crypt::X509 ;                                      # 証明書内容参照ライブラリ使用宣言

$csr_pem = "" ;
$status  = open (CSRPEM,"hoge.pem") ;
if ($status) {
  local $/ = undef ; $csr_pem = <CSRPEM> ; close(CSRPEM) ;
}

$csr_pem =~ /\-\-\-\-\-BEGIN\s+CERTIFICATE\-\-\-\-\-(.+)\-\-\-\-\-END\s+CERTIFICATE\-\-\-\-\-/s ;
$x509_tmp = $1 ;
%outstr   = {} ;

$x509_decode = Crypt::X509->new(cert => decode_base64($x509_tmp)) ;
@nb = localtime($x509_decode->not_before) ;
@na = localtime($x509_decode->not_after) ;
@jw = ('日','月','火','水','木','金','土') ;
$outstr{'not_before'}     = sprintf("%d/%02d/%02d(%s) %02d:%02d:%02d",$nb[5] + 1900,$nb[4] + 1,$nb[3],$jw[$nb[6]],$nb[2],$nb[1],$nb[0]) ;
$outstr{'not_after'}      = sprintf("%d/%02d/%02d(%s) %02d:%02d:%02d",$na[5] + 1900,$na[4] + 1,$na[3],$jw[$na[6]],$na[2],$na[1],$na[0]) ;

$outstr{'subj_country'}   = $x509_decode->subject_country ;
$outstr{'subj_state'}     = $x509_decode->subject_state ;
$outstr{'subj_local'}     = $x509_decode->subject_locality ;
$outstr{'subj_org'}       = $x509_decode->subject_org ;
$outstr{'subj_ou'}        = $x509_decode->subject_ou ;
$outstr{'subj_cn'}        = $x509_decode->subject_cn ;

$outstr{'issuer_cn'}      = $x509_decode->issuer_cn ;
$serial16 = sprintf("%X",$x509_decode->serial) ;
$serial16 =  '0' . $serial16 if (length($serial16) % 2) ;
1 while $serial16 =~ s/^([\da-fA-F]+)([\da-fA-F][\da-fA-F])/$1\:$2/ ;
$outstr{'issuer_serial'}  = $serial16 ;
$outstr{'issuer_country'} = $x509_decode->issuer_country ;
$outstr{'issuer_state'}   = $x509_decode->issuer_state ;
$outstr{'issuer_local'}   = $x509_decode->issuer_locality ;
$outstr{'issuer_org'}     = $x509_decode->issuer_org ;
$outstr{'sigalgo'}        = $x509_decode->sig_algorithm ;
上記サンプルにて、連想配列 %outstr に表示可能な文字列にて解析内容が入ります。
参考:https://metacpan.org/pod/Crypt::X509