1. はじめに
やってみた系自分用メモ。「署名」について勉強しました。
2. 署名って何だ?
「改ざんされていないことを保証するもの」です。それ以外の目的はなく、中身が読めても知ったこっちゃない、というのが署名です。
3. 用意するもの
3-1. 送受信者双方が用意するもの(すりあわせておくべきもの)
- 署名アルゴリズム
有名な所ではRSAとかECDSAとか。片方はRSAでやって、片方はECDSAでやるなんてのは無茶苦茶な話です - ハッシュアルゴリズム
ハッシュアルゴリズムもいろいろあります。送受信双方で合わせておかないと大変なことになります
3-2. 送信側が用意するもの
- 本文
- 秘密鍵・公開鍵
署名アルゴリズムと鍵生成方法は大抵セットになっています。RSAがそうです。この影響を受けて(?)「ECDSAで鍵生成をする」という誤解を招く表現が広まっているようです。
ECDSAは署名をするためのアルゴリズムで、鍵生成の能力は持ちません。
・ECDSA署名で使う鍵生成は ECCという楕円曲線暗号方式をとっていて、楕円曲線暗号方式のパラメータが P-256(NIST P-256)をとる(こともある)。
・NIST P-256 は nistP256, secp256r1, prime256v1 というパラメータ名になっている(ところもある)。
というのが正しい理解だと思います(←弱w)。上のキャプチャで言うと、「ECDSA署名方式で使用される鍵ペアをファイルにするには」とかそんな表現になると思います(←弱w)。「細かいことを言えば」ですよ。
閑話休題。
3-3. 受信側が用意するもの
「双方が用意するもの」が揃っていればいいです。
4. 原理的な話
4-1. 送信側
1.本文を用意します。
2.本文をハッシュアルゴリズムでハッシュし、ハッシュ値を求めます。
3.できたハッシュ値に対して秘密鍵で署名します。
混乱を避けるためにこれを「秘密鍵署名」と呼ぶことにします。
4.本文+秘密鍵署名+公開鍵を受信側に送ります。
4-2. 受信側
1.受け取ったメッセージを 本文, 秘密鍵署名, 公開鍵に分けます。
2.本文をハッシュアルゴリズムでハッシュし、ハッシュ値を求めます。
3.できたハッシュ値に対して公開鍵で 署名します。
これを「公開鍵署名」と呼ぶことにします。
4.「秘密鍵署名」と「公開鍵署名」は数学的な手法で検証できます。
数学的な手法についてはここでは触れません。興味がある方は調べてください。
受信側に渡っているのは「秘密鍵で署名したもの」であって「秘密鍵そのもの」ではないということです。うまくできていますね!
プログラムでは公開鍵で検証インスタンス(verifier)を作って、こんな風に呼んでいます。
verifier.VerifyData(本文, 署名, ハッシュアルゴリズム)
6. コード
ChatGPTの出力が元になっています。利用される方は自己責任で。
署名アルゴリズムは最近流行りのECDSA、それにあわせて鍵生成は ECC PNIST-256, ハッシュアルゴリズムには定番のSHA256を使いました。
.NET Frameworkだけでできました。
// 署名
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace SignatureTest
{
internal class Program
{
static void Main()
{
var message = "Hello world!";
var data = Encoding.UTF8.GetBytes(message);
CheckECDSA(data);
}
static void CheckECDSA(byte[] data)
{
// 鍵ペア生成
// ECC(NIST P-256)
using (var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256))
{
var privateKey = ecdsa.ExportParameters(true);
var publicKey = ecdsa.ExportParameters(false);
// --- 本文をハッシュ化し、秘密鍵で署名 ---
// コードの字面だけ見ていたら、「秘密鍵でやってる」という読みかたができないですね
var signature = ecdsa.SignData(data, HashAlgorithmName.SHA256);
// --- 検証 ---
// 公開鍵で初期化
using (var verifier = ECDsa.Create(publicKey))
{
// 検証(本文, 署名, ハッシュアルゴリズム)
bool valid = verifier.VerifyData(data, signature,
HashAlgorithmName.SHA256);
Console.WriteLine(valid ? "署名は正当です" : "署名は不正です");
}
}
}
}
}