【C#】PKCS#1形式のRSA秘密鍵ファイルを読み込んで復号する

目次

概要

「—–BEGIN RSA PRIVATE KEY—–」で始まるPKCS#1形式のPEMファイルを読み込み、その秘密鍵を使用して暗号化データを復号する実装です。 .NET Core 3.0以降で追加された ImportRSAPrivateKey メソッドを使用し、テキスト処理で抽出したバイナリデータから鍵情報を復元します。

仕様(入出力)

  • 入力:
    • RSA秘密鍵ファイル(PKCS#1形式 PEM: private.key
    • 暗号化されたデータ(Base64文字列)
  • 出力: 復号された平文(UTF-8)
  • 前提: 読み込むファイルはPKCS#1形式(ヘッダーにRSAが含まれる)であること。

基本の使い方

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

// 秘密鍵のPEM文字列からヘッダー・フッターを除去してバイナリ化
var pemStr = File.ReadAllText("private.key");
var base64 = pemStr.Replace("-----BEGIN RSA PRIVATE KEY-----", "")
                   .Replace("-----END RSA PRIVATE KEY-----", "")
                   .Replace("\r", "").Replace("\n", "");
var keyBytes = Convert.FromBase64String(base64);

// RSA初期化とインポート
using var rsa = RSA.Create();
rsa.ImportRSAPrivateKey(keyBytes, out _);

// 復号(暗号化時と同じパディングを指定)
var encrypted = Convert.FromBase64String("Base64Data...");
var decrypted = rsa.Decrypt(encrypted, RSAEncryptionPadding.OaepSHA256);

コード全文

このコードは、ファイル読み込みから復号までの一連のプロセスを、自己完結するコンソールアプリとして記述しています。

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

class Program
{
    static void Main()
    {
        // 1. テストデータの準備
        // 実際には外部から受け取ったBase64文字列を使用します
        string encryptedBase64 = "(ここに暗号化されたBase64文字列が入ります)";
        
        // 動作検証用にダミーの鍵と暗号データを作成します(本来は不要な処理)
        CreateDummyEnvironment(out string keyFilePath, out encryptedBase64);

        try
        {
            Console.WriteLine($"Reading Private Key from: {keyFilePath}");

            // 2. PEMファイルの読み込みと整形
            // PKCS#1形式のヘッダーとフッター、改行コードを削除してBase64部分のみ抽出します
            string pemContent = File.ReadAllText(keyFilePath);
            
            string privateKeyBase64 = pemContent
                .Replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .Replace("-----END RSA PRIVATE KEY-----", "")
                .Replace("\r", "")
                .Replace("\n", "")
                .Trim();

            byte[] privateKeyBytes = Convert.FromBase64String(privateKeyBase64);

            // 3. RSAインスタンスへの秘密鍵インポート
            using var rsa = RSA.Create();
            
            // PKCS#1形式としてインポート
            rsa.ImportRSAPrivateKey(privateKeyBytes, out int bytesRead);
            Console.WriteLine($"Key imported. ({bytesRead} bytes read)");

            // 4. 復号処理
            byte[] encryptedData = Convert.FromBase64String(encryptedBase64);

            // 暗号化時と一致するパディングモードを指定(推奨: OAEP SHA256)
            byte[] decryptedData = rsa.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA256);

            // 5. 結果出力
            string plainText = Encoding.UTF8.GetString(decryptedData);
            Console.WriteLine("\n[Decrypted Message]");
            Console.WriteLine(plainText);
        }
        catch (FormatException)
        {
            Console.WriteLine("Error: The key file format is invalid.");
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine($"Decryption Error: {ex.Message}");
            Console.WriteLine("Hint: Check padding mode or if the key matches the encryption key.");
        }
    }

    // 検証用:ダミーの鍵ファイルと暗号化データを作成するヘルパー
    static void CreateDummyEnvironment(out string path, out string encryptedBase64)
    {
        path = "test_private_pkcs1.pem";
        using var rsa = RSA.Create(2048);

        // 秘密鍵ファイルの作成
        byte[] keyBytes = rsa.ExportRSAPrivateKey();
        var sb = new StringBuilder();
        sb.AppendLine("-----BEGIN RSA PRIVATE KEY-----");
        sb.AppendLine(Convert.ToBase64String(keyBytes, Base64FormattingOptions.InsertLineBreaks));
        sb.AppendLine("-----END RSA PRIVATE KEY-----");
        File.WriteAllText(path, sb.ToString());

        // 暗号化データの作成
        byte[] data = Encoding.UTF8.GetBytes("Secret Code: 777");
        byte[] enc = rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);
        encryptedBase64 = Convert.ToBase64String(enc);
    }
}

カスタムポイント

  • パディングの変更: 入力されたコード例の fOAEP: true は古い実装(SHA1使用)を指すことが多いです。互換性が必要な場合は RSAEncryptionPadding.OaepSHA1 を使用してください。新規開発では OaepSHA256 が標準的です。
  • キーファイルの形式: ファイルの中身が -----BEGIN PRIVATE KEY-----(RSAが付かない)で始まっている場合は、PKCS#8形式です。その場合は ImportPkcs8PrivateKey メソッドを使用してください。

注意点

  1. 秘密鍵の管理: 秘密鍵ファイルはシステムの最も重要な機密情報です。ファイルシステムのアクセス権限(ACL)を厳格に設定し、アプリケーション実行ユーザーのみが読み取れるように制限してください。
  2. ヘッダー文字列の正確性: 手動で置換を行う場合、ヘッダー文字列(-----BEGIN...)が一文字でも間違っていると正しくBase64文字列にならず、例外が発生します。
  3. 例外処理: パスワードで保護されたPEMファイル(Encrypted Private Key)はこの方法では読み込めません。その場合は ImportEncryptedPkcs8PrivateKey などの専用メソッドが必要になります。

応用

ImportFromPemによる簡略化(.NET 5以降)

手動の文字列操作(Replace)を廃止し、.NETの機能でPEMを自動解析する推奨パターンです。

using var rsa = RSA.Create();

// ファイルの中身をそのまま渡すだけで、ヘッダーや形式(PKCS#1/PKCS#8)を自動判別
string pemContent = File.ReadAllText("private.key");
rsa.ImportFromPem(pemContent);

// 以降は同じ
var result = rsa.Decrypt(data, RSAEncryptionPadding.OaepSHA256);

まとめ

PKCS#1形式の秘密鍵を利用するには、ImportRSAPrivateKey メソッドを使用します。これにより、従来のXML形式への変換を経ることなく、OpenSSL等で生成された標準的な鍵ファイルを直接扱えるようになります。ただし、より現代的な .NET 5以降の環境では、ヘッダー処理や形式判別を自動化してくれる ImportFromPem メソッドの使用が最も安全で効率的です。

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

この記事を書いた人

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

目次