【C#】RSA公開鍵を使用したデジタル署名の検証

目次

概要

受信したデータとデジタル署名、そして送信者の公開鍵を使用して、データが改ざんされていないこと(完全性)と、正しい送信者から送られたこと(真正性)を検証する実装です。 .NETのモダンな実装では、古い RSAPKCS1SignatureDeformatter クラスではなく、RSA クラスの VerifyData メソッドを使用してシンプルかつ安全に判定を行います。

仕様(入出力)

  • 入力:
    • 検証対象のテキストデータ(UTF-8)
    • デジタル署名(Base64文字列)
    • 送信者のRSA公開鍵(XML形式文字列)
  • 出力: 検証結果(bool: 改ざんがなければ true、不整合なら false
  • 前提: 署名作成時と同じハッシュアルゴリズム(例: SHA256)とパディングモード(例: PKCS#1)を使用すること。

基本の使い方

using System.Security.Cryptography;
using System.Text;

// 署名検証用のメソッド呼び出し
bool isValid = ValidateSignature(originalData, signatureBase64, publicKeyXml);

if (isValid)
{
    Console.WriteLine("署名は有効です。信頼できるデータです。");
}
else
{
    Console.WriteLine("警告:署名が無効、またはデータが改ざんされています。");
}

コード全文

署名の検証ロジックと、動作確認用のテストコードを含んだコンソールアプリケーションです。

using System;
using System.Security.Cryptography;
using System.Text;

class Program
{
    static void Main()
    {
        // 1. テストデータの準備
        // 本来はネットワーク経由などで受け取ったデータと署名を使用します
        string receivedMessage = "OrderConfirmed: ID-998877";
        
        // テスト用に、この場限りのキーペアと署名を生成します(本来は不要な処理)
        GenerateTestEnvironment(receivedMessage, out string publicKeyXml, out string validSignature);

        Console.WriteLine($"[Received Message] {receivedMessage}");
        Console.WriteLine($"[Public Key] Found XML key info.");
        Console.WriteLine($"[Signature] {validSignature.Substring(0, 20)}...");

        // 2. 正常な署名の検証
        Console.WriteLine("\n--- Test 1: Valid Signature ---");
        bool result1 = VerifyDigitalSignature(receivedMessage, validSignature, publicKeyXml);
        Console.WriteLine($"Verification Result: {(result1 ? "OK (Trusted)" : "NG (Tampered)")}");

        // 3. 改ざんされたデータの検証
        Console.WriteLine("\n--- Test 2: Tampered Data ---");
        string tamperedMessage = "OrderConfirmed: ID-000000"; // IDを書き換え
        bool result2 = VerifyDigitalSignature(tamperedMessage, validSignature, publicKeyXml);
        Console.WriteLine($"Verification Result: {(result2 ? "OK (Trusted)" : "NG (Tampered)")}");
    }

    /// <summary>
    /// メッセージ、署名、公開鍵を受け取り、正当性を検証します
    /// </summary>
    /// <param name="data">検証対象の元データ</param>
    /// <param name="signatureBase64">Base64形式の署名</param>
    /// <param name="publicKeyXml">XML形式のRSA公開鍵</param>
    /// <returns>署名が有効ならtrue</returns>
    static bool VerifyDigitalSignature(string data, string signatureBase64, string publicKeyXml)
    {
        try
        {
            // データのバイト配列化
            byte[] dataBytes = Encoding.UTF8.GetBytes(data);
            
            // 署名のバイト配列化
            byte[] signatureBytes = Convert.FromBase64String(signatureBase64);

            // RSAインスタンスの作成
            using var rsa = RSA.Create();
            
            // 公開鍵の読み込み
            // ※XML形式以外(PEM等)の場合は ImportFromPem を使用してください
            rsa.FromXmlString(publicKeyXml);

            // 検証の実行
            // 重要: 署名作成時と同じ「ハッシュアルゴリズム」と「パディング」を指定する必要があります
            return rsa.VerifyData(
                dataBytes,
                signatureBytes,
                HashAlgorithmName.SHA256,
                RSASignaturePadding.Pkcs1);
        }
        catch (FormatException)
        {
            Console.WriteLine("Error: Invalid Base64 signature format.");
            return false;
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            return false;
        }
    }

    // テスト用の署名生成ヘルパー(署名作成側のロジック)
    static void GenerateTestEnvironment(string msg, out string pubKey, out string sig)
    {
        using var rsa = RSA.Create(2048);
        pubKey = rsa.ToXmlString(false);
        byte[] data = Encoding.UTF8.GetBytes(msg);
        byte[] sigBytes = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        sig = Convert.ToBase64String(sigBytes);
    }
}

カスタムポイント

  • ハッシュアルゴリズムの整合性: HashAlgorithmName.SHA256 は署名作成側と完全に一致している必要があります。もし署名側が SHA1SHA512 を使用している場合は、ここを変更してください。
  • パディングモード: RSASignaturePadding.Pkcs1 は最も一般的ですが、新しいシステムではより堅牢な Pss が使われることがあります。仕様書を確認して選択してください。
  • 公開鍵の形式: 入力例に合わせて FromXmlString を使用していますが、OpenSSL系で生成されたキーを使用する場合は ImportFromPemImportSubjectPublicKeyInfo を使用する実装に切り替えてください。

注意点

  1. 検証の失敗要因: VerifyDatafalse を返す場合、データが改ざんされている以外にも、「公開鍵が違う」「ハッシュアルゴリズムが不一致」「エンコーディング(UTF-8/Shift-JIS)が違う」といった原因が考えられます。
  2. 古いAPIの利用: RSAPKCS1SignatureDeformatter クラスを使用するコードは、現代の .NET 環境では冗長であり推奨されません。RSA クラスに統合されたメソッドを使用することで、簡潔かつクロスプラットフォームなコードになります。
  3. 例外処理: Base64文字列が壊れている場合や、XMLキーの形式が不正な場合は例外が発生します。検証メソッド内ではこれらを適切にキャッチし、システムのクラッシュを防ぐ必要があります。

応用

ハッシュ値(ダイジェスト)を直接検証する場合

データ全体ではなく、計算済みのハッシュ値だけを受け取って検証する場合の実装です。

static bool VerifyHashDirectly(byte[] hashDigest, byte[] signature, string publicKeyXml)
{
    using var rsa = RSA.Create();
    rsa.FromXmlString(publicKeyXml);

    // VerifyDataではなくVerifyHashを使用
    return rsa.VerifyHash(
        hashDigest, 
        signature, 
        HashAlgorithmName.SHA256, 
        RSASignaturePadding.Pkcs1);
}

まとめ

デジタル署名の検証は、RSA.VerifyData メソッド一つで完結します。重要なのは、署名作成時と「ハッシュアルゴリズム」「パディング方式」を完全に一致させることです。これらが食い違っていると、正しいデータであっても検証は必ず失敗します。XML形式のキーは.NET特有の形式であるため、外部システムと連携する際はPEM形式への対応も視野に入れた設計を行うと柔軟性が向上します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次