「パスキーの仕組みを図解」「パスキーとは」で、スマホが秘密鍵で「署名」して、サイトが公開鍵で確かめる、と書きました。では、その「署名」とは何なのか。ここでは、よく混同される「暗号化」との違いから説明します。
暗号化と署名は、目的が逆
どちらも「秘密鍵と公開鍵のペア」を使いますが、何のために使うのか、どちらの鍵を先に使うのかが逆です。
| 暗号化 | 署名 | |
|---|---|---|
| 目的 | 中身を隠す(ほかの人に読ませない) | だれが作ったか、改ざんされていないかを証明する(中身は隠さない) |
| 送る人が使う鍵 | 受け取る人の公開鍵(だれでも使える) | 自分の秘密鍵(自分しか使えない) |
| 送るもの | 暗号文だけ | 平文と署名の 2 つ |
| 受け取る人が使う鍵 | 自分の秘密鍵で元に戻す | 送った人の公開鍵で確かめる(だれでもできる) |
| 結果 | 元の中身が読める | 「正しい」か「正しくない」かが分かる(平文には戻らない) |
パスキーで使っているのは署名のほうです。サイトが出すチャレンジ(問いかけ)は秘密ではないので、隠す必要がありません。知りたいのは「本物のスマホが答えたかどうか」だけです。
署名はどんなデータ?
「署名」と聞くと、手書きの「サイン」のような短い印を思い浮かべるかもしれません。実際の署名は、人には読めない数値(バイナリーのデータ)です。パスキーと同じ方式で、実際に作ってみました。
平文:こんにちは 署名(中身を 16 進数で表したものの先頭): b6d360feca771345189aa64cf206849f… 署名(base64url という方法で文字にしたもの): ttNg_sp3E0UYmqZM8gaEn1WhFI48Vl4qYzb5swrDjhtUfdcwql9aurZNYTI3Tz7REk3Yi3MX9sjFrFMDKABAoQ
中身は意味のある文字ではなく、ただの数値です。Web で送るときは、そのままでは送りにくいので、文字に変換して送っています。
平文が長くても、署名の大きさは同じ
| 平文 | 平文の大きさ | 署名の大きさ |
|---|---|---|
| 「こんにちは」(5 文字) | 15 バイト | 64 バイト |
| 「あ」を 1 万文字 | 30,000 バイト | 64 バイト |
署名の計算では、最初に平文から「要約(ハッシュ)」を作ります。要約は、平文が何文字でもいつも同じ長さです。その要約に秘密鍵で計算をかけるので、署名もいつも同じ大きさになります。64 バイトの署名に 1 万文字の平文が入っているはずはないので、署名から平文を取り出せないことも分かります。
※ ここで作った署名は、パスキーで主に使われている ES256 という方式のもので、32 バイトの数値 2 つを並べた 64 バイトです(RFC 7518)。パスキーが実際にサイトへ送るときは、決まり(WebAuthn)で DER という別の包み方にするので、69〜72 バイトくらいになります。どちらにしても、平文の長さには左右されません(手元で 2,000 回作って確かめました)。
平文(何文字でも) → 要約(いつも 32 バイト) → 秘密鍵で計算 → 署名(いつも同じ大きさ)
手書きの「サイン」との違い
| 手書きのサイン | デジタルの署名 | |
|---|---|---|
| 同じ書類にもう一度 | ほぼ同じ形 | 毎回まったく違う値になる(計算に乱数を使う方式のため。どれも検証は「正しい」) |
| 書類の中身を 1 文字変えると | サインは同じ | まったく違う値になる |
| 別の書類に使い回すと | 使い回せてしまう | 「正しくない」になる(「こんにちは」の署名で「こんにちわ」を検証すると「正しくない」) |
手書きのサインは「その人の印」で、書類の中身とは関係ありません。デジタルの署名は、その平文と、その秘密鍵の組み合わせからしか作れない値です。
「秘密鍵で暗号化して、公開鍵で復号」ではない?
署名は、よく「秘密鍵で暗号化して、公開鍵で復号できれば本人」と説明されます。大筋のイメージとしては分かりやすいのですが、正確には少し違います。
- この説明は、昔からある RSA という方式の計算の形から来ています。RSA では、署名に公開鍵で計算をかけると何かが出てきます。でも、それも平文そのものではなく、平文の「要約(ハッシュ)」です。サイトは、届いた平文から自分でも要約を作り、2 つが一致するかを比べています。要約から平文に戻すことはできません(RSA の署名の決まり:RFC 8017)。
- パスキーで主に使われている方式(楕円曲線暗号を使った署名)には、署名から元の中身を取り出す(復号する)手順がそもそもありません。決まり(FIPS 186-5)にあるのは、署名を作る手順と確かめる手順だけです。
- 確かめるときは、公開鍵・元の中身・署名の 3 つを使って、「この署名は、ペアの秘密鍵で、この中身に対して作られたものか」を判定します。答えは「正しい」か「正しくない」だけです。
そもそもサイトは、自分が出したチャレンジの中身を知っています。だから中身を取り出す必要はなく、「自分が出したチャレンジへの、正しい署名か」を確かめれば十分なのです。
式のような形で書くと、こうなります。確かめるときに、平文そのものも材料に使うのがポイントです。
署名を作る(スマホ):署名 = 署名する計算(秘密鍵, 平文) 確かめる(サイト) :検証する計算(公開鍵, 平文, 署名) → 「正しい」か「正しくない」
パスキーで、スマホが平文と署名の両方をサイトに送っているのは、このためです。署名の中に平文が入っていて取り出せるのなら、平文を一緒に送る必要はありません。
パスキーでの署名と検証の流れ
- チャレンジ
- 今開いているサイトのアドレス
- 顔認証などをした印
- 公開鍵・平文・署名の 3 つで、署名が正しいか
- チャレンジは、自分が出したものか
- アドレスは、自分のサイトか
スマホからサイトへ送られるのは、署名した中身(平文)と署名だけです。秘密鍵は一度もスマホの外に出ません。
なぜ偽物の署名は作れないの?
- 秘密鍵がないと作れない:正しい署名を作れるのは、ペアの秘密鍵を持っている人(スマホ)だけです。
- 公開鍵から秘密鍵は割り出せない:公開鍵はだれに見せてもかまいません。公開鍵から秘密鍵を計算で求めることは、今のコンピューターでは事実上できないように作られています。
- 中身を 1 文字でも変えると合わなくなる:署名は、その中身に対してだけ正しくなります。途中で書き換えられると、検証で「正しくない」になります。
- 前の署名は使い回せない:チャレンジは毎回変わるので、前回の署名を盗んで送っても、今回のチャレンジには合いません。
パスキーは何に署名しているの?
| 署名する中身 | それで防げること |
|---|---|
| チャレンジ(問いかけ) | 前の署名の使い回し |
| 今開いているサイトのアドレス | 偽サイトへの誘導(偽サイトのアドレスで署名しても、本物のサイトでは通らない) |
| 顔認証などで本人確認をした印 | スマホを拾った人が、本人確認なしに使うこと |
まとめ
- 暗号化は「中身を隠す」、署名は「本物であることを証明する」。鍵の使い方が逆。
- パスキーは、毎回変わるチャレンジとサイトのアドレスに、スマホの秘密鍵で署名して本人を証明している。
- サイトは公開鍵で「正しいかどうか」を確かめるだけ。秘密鍵もパスワードも、ネットワークを流れない。
