手を動かしてDNSSECの検証をやってみよう

2020年12月09日 水曜日


【この記事を書いた人】
草場 健

2018年新卒入社でルータのファームウェアを開発しています。デバイスドライバやネットワークスタックなどの低レイヤーに興味があります。

「手を動かしてDNSSECの検証をやってみよう」のイメージ

IIJ 2020 TECHアドベントカレンダー 12/9(水)の記事です】

はじめに

DNSSECはDNSの応答が改ざんされていないかを確かめる仕組みです。
1997年にベースとなるRFC2065が発表された後も更新され、DNSSECに付随する議論は今でも様々な場で行われています。

お手元のdigコマンドで何かしらのレコードを引いてみてください。もしその応答のflagsにAD bitが立っていれば、裏でDNSSEC検証が行われています。

普段はリゾルバが行ってくれるこの検証、ぱっと見では何をやっているのか分かりづらいですが、実は公開鍵暗号とハッシュ関数の2つを扱う事で検証する事ができます。
というわけで、DNSSECの検証を自分の手でコマンドを打ってやってみましょう。

手を動かそう

さて、では早速手を動かしていきましょう。
今回検証するリソースレコードはこちら。

電子署名を見てみよう

このAレコードの電子署名であるRRSIGレコードがこちらです。

フィールドに分けて出力してくれてはいますが、これだとなんだかよくわからないですね。というわけで対応するRFC4034を見てみましょう。
DNSSEC含むDNSに関連するRFCの一部はJPRS様による和訳が公開されています。
JPRS様に感謝しつつ、引用させていただきます。

digの出力内容を各フィールドと照らし合わせて見てみましょう。

フィールド名 説明
署名対象タイプ 署名対象のリソースレコードタイプ (eng-blog.iij.ad.jpの)Aレコード
アルゴリズム 署名に用いたアルゴリズム RSASHA256
ラベル 署名対象のラベル数 4つ (eng-blog, iij, ad, jp)
オリジナルTTL 署名対象のリソースレコードのTTL 86400
有効期間終了 署名の有効期間の終了時刻 2020年12月17日15時10分06秒
有効期間開始 署名の有効期間の開始時刻 2020年11月17日15時10分06秒
鍵タグ 署名に使われた鍵を絞るための値 5628
署名者名 署名に使われたDNSKEYの所有者名 iij.ad.jp
署名 電子署名 検証するための電子署名 (詳細は後ほど)

署名の検証に使う鍵を見てみよう

では、この署名の検証に使うための公開鍵を探しましょう。署名者名は iij.ad.jp なので、iij.ad.jpのDNSKEYレコードを引いてみます。

こちらもRFC4034の説明を引用してみてみましょう。

鍵が2つあります。どちらが署名に使われた鍵でしょうか?
フラグが256の方がZSK(Zone Signing Key)であることを知っていると一目瞭然ですが、RRSIGには署名に使われた鍵を絞るための 鍵タグ フィールドがあります。
これはチェックサムのようなもので、計算方法はRFC4034の付録Bに記載があります。
今回はPythonスクリプトを用意したので、これで計算してみましょう。

RRSIGレコードの方に署名に用いた鍵の鍵タグは5628とあるので、確かにフラグが256の方の鍵を使うことがこれで確認できました。
// ちなみに unique にDNSKEYが定まらない場合もあります。その時はどっちも試してください(-_-;;)

検証してみよう

アルゴリズムを見てみる

RRSIGのアルゴリズムフィールドを見るとRSASHA256が利用されているようです。まずはこのアルゴリズムがどのようになっているかを確認しましょう。RFC5702を見てみます。

また署名対象となるデータはRFC4034に書いてあります。

署名の検証には以下2つのハッシュが一致するかを確認します。

  • RRSIGの署名を復号して得られるハッシュ
  • RRSIGの署名以外の部分 + 署名対象のAレコードのデータから得られるハッシュ

署名を復号してハッシュを確認する

この署名はRSAで暗号化されているだけなので、公開鍵から必要な情報を取り出して復号してみましょう。公開鍵のフォーマットはRFC3110に書いてあります。

DNSKEYレコードの公開鍵の部分をhexdumpして照らし合わせてみましょう。

フィールド名
指数長 3 bytes
指数 65537
モジュラス 0xed から始まる128bytes(=1024 bits)

あとは暗号文の65537乗をmodulo (上記のモジュラス)で計算すれば復号できます。

頭についている大量の0xffやprefixを除くと、ハッシュが c7b0d6d4cfef7d0db0939b400ca37d810e6bfb773e47d2dab543887ccecaaa13  であることがわかります。

リソースレコードからハッシュを計算する

RRSIGのRDATA部分と署名対象のAレコードのデータを並べてsha-256を計算します。
Pythonスクリプトを書き疲れたので、printfを使って対象のバイナリをベタ書きします。

署名から得たハッシュと一致していますね。
これにより、ZSKを信頼出来ると仮定すれば、eng-blog.iij.ad.jpのAレコードを信頼出来ることになります。

信頼の連鎖を繋ごう

ZSKが格納されているDNSKEYレコードにも同様に対応するRRSIGレコードがあります。

手順は同様なので詳細は割愛しますが、こちらは先ほど使われなかった方のDNSKEYであるKSK(=Key Signing Key)を使って署名されています。
則ち、KSKを信頼出来ると仮定すれば、ZSKを信頼出来ることになります。

ではKSKはどのように信頼するのかというと、親ゾーンであるad.jpで管理されているiij.ad.jpのDSレコードにiij.ad.jpのKSKのハッシュが登録されています。以下のレコードです。

RFC4034を見て、当てはめてみましょう。

フィールド名 説明
鍵タグ 対象のDNSKEYレコードの鍵タグ 48472 (=KSKの鍵タグ)
アルゴリズム 対象のDNSKEYレコードのアルゴリズム 8 (=KSKと同じ)
ダイジェストタイプ 利用するハッシュの種類 2: SHA-256
ダイジェスト ハッシュ 7E73…

ハッシュを計算する dnssec-dsfromkey というコマンドがありますが、それは使わずにDNSKEYレコードに対応するハッシュ値を計算してみましょう。またもprintfでゴリ押しします。

大文字小文字が違うので見づらいですが、これはiij.ad.jpのDSレコードの中身と一致していますね。
これで(ad.jpの権威サーバが持つ)iij.ad.jpのDSレコードが信頼出来ると仮定すると、iij.ad.jpのKSKが信頼出来ることがわかります。これで連鎖が繋がりました。

トラストアンカーをチェックしよう

次はDSレコードのRRSIG、ad.jpのDNSKEYのRRSIG… と検証を繰り返すことによって、最終的には「あとはルートゾーンのKSKが信頼出来ればOK」というところまでたどり着きます。

DNSSECを検証しているレゾルバは、予め信頼するルートゾーンのKSKの情報(=トラストアンカー)を保持しています。そのため、トラストアンカーからルートゾーンのKSKを信頼する事が出来れば検証が完了します。

最新のトラストアンカーは http://data.iana.org/root-anchors/root-anchors.xml から入手出来ます。

ではルートゾーンのKSKのハッシュを計算してみましょう。

またもや大文字小文字が違うので見づらいですが… 一致していますね!

というわけでこのようにして、eng-blog.iij.ad.jpのAレコードを検証する事ができました。

まとめ

リソースレコードをDNSSEC検証する方法を紹介しました。

今回は検証にあたってRFCを3つ紹介しましたが、DNSSECのRFCは他にもまだ多く存在しますし、今も増え続けています。
JPRS様がDNSに関連するRFCの和訳を公開されていますので、今回の記事でDNS(SEC)に興味を持った方は是非RFCを読んでみてください。また、エンジニアブログでも過去にDNS(SEC)関連の記事があります。

ここまでお読みいただき、ありがとうございました。

IIJ Engineers blog読者プレゼントキャンペーン
  • Twitterフォロー&条件付きツイートで「IoT米」を抽選で20名にプレゼント!
    応募期間は2020/12/01~2020/12/31まで。詳細はこちらをご覧ください。
    今すぐツイートするならこちら→ フォローもお忘れなく!

草場 健

2020年12月09日 水曜日

2018年新卒入社でルータのファームウェアを開発しています。デバイスドライバやネットワークスタックなどの低レイヤーに興味があります。

Related
関連記事