C# Tips | セキュリティ:RSA署名を検証する

C# C#
スポンサーリンク
スポンサーリンク

RSA署名を検証することは「そのデータが本物であり、途中で改ざんされていないことを確認する技術」です

業務システム、API連携、ライセンス検証、認証基盤などを設計する読者のみなさんにとって、RSA署名の検証は「なりすまし防止」と「改ざん検知」を同時に満たす、とても重要な仕組みになります。 ここでは、プログラミング初心者の方にも分かるように、RSA署名検証の考え方をステップバイステップで整理しながら、C#でそのまま使えるユーティリティとテンプレートを詳しく解説していきます。

RSA署名検証の基本概念をかみ砕いて理解する

公開鍵と秘密鍵の役割

RSAは「公開鍵」と「秘密鍵」のペアで動く公開鍵暗号方式です。

  • 秘密鍵(Private Key):署名を「作る」ために使う。署名者だけが持つべきものです。
  • 公開鍵(Public Key):署名を「検証する」ために使う。誰に配ってもよい鍵です。

署名検証側は秘密鍵を持っていなくても、公開鍵さえあれば次のことを確認できます。

  • この署名は、対応する秘密鍵を持つ人によって作られたか
  • 署名対象のデータが途中で改ざんされていないか

つまり、RSA署名検証は「そのデータが本物であること」と「改ざんされていないこと」を同時に保証するための技術です。

ステップ1:RSA署名検証の全体フローを整理する

RSA署名検証は、次の材料を使って行います。

  1. 検証対象のデータ(メッセージ)
  2. 署名(通常はBase64文字列)
  3. 公開鍵(PEM形式など)
  4. 署名に使われたハッシュアルゴリズム(例:SHA-256)
  5. 署名に使われたパディング方式(例:PSS)

検証の流れは次のようになります。

  1. 公開鍵PEMから RSA インスタンスを作る
  2. メッセージをバイト配列(UTF-8など)に変換する
  3. 署名をBase64からバイト配列に変換する
  4. RSA.VerifyData を使って署名を検証する
  5. 結果(true/false)に応じて処理を分岐する

この流れをユーティリティとしてまとめることで、どのプロジェクトでも再利用できる「業務用部品」になります。

ステップ2:公開鍵PEMからRSAインスタンスを作るユーティリティ

まずは、公開鍵をPEM形式の文字列として扱い、そこから RSA インスタンスを復元するユーティリティを用意します。 PEM形式は次のようなテキストです。

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----

このPEMからBase64部分だけを取り出し、ImportSubjectPublicKeyInfo で読み込みます。

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

public static class RsaImportUtility
{
    public static RSA CreateFromPublicKeyPem(string publicKeyPem)
    {
        string base64 = ExtractBase64(publicKeyPem);
        byte[] bytes = Convert.FromBase64String(base64);

        var rsa = RSA.Create();
        rsa.ImportSubjectPublicKeyInfo(bytes, out _);
        return rsa;
    }

    private static string ExtractBase64(string pem)
    {
        var lines = pem.Split('\n', StringSplitOptions.RemoveEmptyEntries);
        var sb = new StringBuilder();

        foreach (var line in lines)
        {
            if (line.StartsWith("-----")) continue;
            sb.Append(line.Trim());
        }

        return sb.ToString();
    }
}
C#

このユーティリティにより、設定ファイルやKeyVaultなどに保存してある公開鍵PEMから、いつでも署名検証用の RSA インスタンスを作れるようになります。

ステップ3:RSA署名を検証するユーティリティ(テンプレート)

次に、公開鍵を使って署名を検証するユーティリティを作ります。 ここでは、メッセージは文字列、署名はBase64文字列、公開鍵はPEM文字列として扱います。

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

public static class RsaSignatureVerifier
{
    /// <summary>
    /// 公開鍵PEM・メッセージ・Base64署名を使ってRSA署名を検証します。
    /// </summary>
    public static bool VerifySignature(
        string message,
        string signatureBase64,
        string publicKeyPem)
    {
        using var rsa = RsaImportUtility.CreateFromPublicKeyPem(publicKeyPem);

        byte[] dataBytes = Encoding.UTF8.GetBytes(message);
        byte[] signatureBytes = Convert.FromBase64String(signatureBase64);

        // 署名時に使われたハッシュアルゴリズムとパディング方式を一致させることが重要です。
        return rsa.VerifyData(
            dataBytes,
            signatureBytes,
            HashAlgorithmName.SHA256,
            RSASignaturePadding.Pss);
    }
}
C#

ここでの重要ポイントは次の2つです。

  • HashAlgorithmName.SHA256:署名側がSHA-256で署名しているなら、検証側もSHA-256を指定する必要があります。
  • RSASignaturePadding.Pss:新規実装では、古いPKCS1ではなくPSSパディングが推奨されます。

署名側と検証側で「ハッシュアルゴリズム」と「パディング方式」が一致していないと、署名は必ず検証に失敗します。

ステップ4:署名検証ユーティリティの使用例

例1:メッセージと署名を検証する基本パターン

string message = "これは署名対象のメッセージです。";
string signatureBase64 = "送信者から渡された署名のBase64文字列";
string publicKeyPem = "送信者の公開鍵PEM";

bool isValid = RsaSignatureVerifier.VerifySignature(
    message,
    signatureBase64,
    publicKeyPem);

if (isValid)
{
    Console.WriteLine("署名は正しく、メッセージは改ざんされていません。");
}
else
{
    Console.WriteLine("署名が不正か、メッセージが改ざんされています。");
}
C#

このようにすることで、「このメッセージは本当にその公開鍵の持ち主が署名したものか」を検証できます。

ステップ5:署名側の実装イメージを理解する(検証の前提)

検証を正しく行うためには、署名側がどのように署名を作っているかを理解しておく必要があります。 典型的な署名側のコードは次のようになります。

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

public static class RsaSigner
{
    public static string SignMessage(string message, string privateKeyPem)
    {
        using var rsa = CreateFromPrivateKeyPem(privateKeyPem);

        byte[] dataBytes = Encoding.UTF8.GetBytes(message);
        byte[] signatureBytes = rsa.SignData(
            dataBytes,
            HashAlgorithmName.SHA256,
            RSASignaturePadding.Pss);

        return Convert.ToBase64String(signatureBytes);
    }

    private static RSA CreateFromPrivateKeyPem(string privateKeyPem)
    {
        string base64 = ExtractBase64(privateKeyPem);
        byte[] bytes = Convert.FromBase64String(base64);

        var rsa = RSA.Create();
        rsa.ImportPkcs8PrivateKey(bytes, out _);
        return rsa;
    }

    private static string ExtractBase64(string pem)
    {
        var lines = pem.Split('\n', StringSplitOptions.RemoveEmptyEntries);
        var sb = new StringBuilder();

        foreach (var line in lines)
        {
            if (line.StartsWith("-----")) continue;
            sb.Append(line.Trim());
        }

        return sb.ToString();
    }
}
C#

ここで使っている

  • HashAlgorithmName.SHA256
  • RSASignaturePadding.Pss

は、検証側でも同じものを使う必要があります。 署名側と検証側の設定が一致していることが、RSA署名検証の大前提です。

ステップ6:何を「署名対象」にするかを明確にする(重要な設計ポイント)

署名検証で最も重要なのは、「どのデータを署名対象にしているか」を明確にすることです。

生のメッセージを署名する場合

単純な例では、そのまま文字列をUTF-8に変換して署名します。

  • メッセージ:"Hello"
  • 署名対象:UTF-8バイト列

この場合、検証側も同じ文字列・同じエンコーディングでバイト列を作る必要があります。 エンコーディングが違うと、同じ文字列でも署名検証は失敗します。

JSONやフィールド連結文字列を署名する場合

APIやライセンス検証などでは、JSON文字列や特定のフィールドを連結した文字列を署名対象にすることが多いです。

注意すべき点は次のとおりです。

  • 署名対象の文字列生成ルールを「厳密に固定」する
  • 余計なスペースや改行を入れない
  • フィールドの順番を変えない
  • シリアライズ方法(例:JSONのフォーマット)を統一する

署名対象の文字列が少しでも違うと、同じデータでも署名検証は失敗します。 仕様書やドキュメントに「署名対象文字列の生成ルール」を明記しておくことが非常に重要です。

ステップ7:RSA署名検証で絶対にやってはいけないこと

RSA署名検証において、次のようなコードや運用は避けるべきです。

  • 署名対象のデータ生成ルールを曖昧にする
  • 署名側と検証側でハッシュアルゴリズムを変える
  • 署名側と検証側でパディング方式を変える
  • 公開鍵を信頼できない経路で受け取る
  • 署名検証に失敗しても処理を続行する

特に「検証に失敗しても処理を続行する」ことは、 なりすましや改ざんを見逃す直接的な原因になります。

ステップ8:実務的ベストプラクティス

RSA署名検証を業務システムに組み込む際のベストプラクティスを整理します。

  • 署名対象の文字列生成ルールを仕様書に明記する
  • ハッシュアルゴリズムはSHA-256以上を使う
  • パディングはPSS(RSASignaturePadding.Pss)を使う
  • 公開鍵は安全な経路(HTTPS、署名付き配布物など)で配布する
  • 公開鍵そのものが改ざんされていないことを別途確認する(例:ハッシュ値や署名付き配布)
  • 署名検証に失敗した場合は必ずエラーとして扱い、処理を中止する
  • ログには「署名が不正であること」だけを記録し、署名そのものや秘密鍵情報は記録しない

これらを守ることで、RSA署名検証は「信頼のゲート」として機能します。

まとめ:RSA署名検証ユーティリティは「なりすましと改ざんを防ぐための信頼のゲート」

RSA署名を検証する本質は次の3つです。

  • 公開鍵だけで「その人の署名かどうか」を確認できること
  • 署名対象のデータが1ビットでも変われば検証は失敗すること
  • ハッシュアルゴリズムとパディング方式を署名側と完全に一致させること

ここで紹介したユーティリティとテンプレートを使うことで、 読者のみなさんは業務システムの中に「なりすましと改ざんを確実に防ぐためのRSA署名検証レイヤー」を自然に組み込めるようになります。

タイトルとURLをコピーしました