【C#】PKCS#1形式のRSA公開鍵ファイルを読み込んで暗号化する

目次

概要

「—–BEGIN RSA PUBLIC KEY—–」で始まるPKCS#1形式のPEMファイルを読み込み、その公開鍵を使用してデータを暗号化する実装です。 .NET Core 3.0以降で追加された ImportRSAPublicKey メソッドを使用し、Base64デコードされたバイナリデータ(DER)からキー情報を復元します。

仕様(入出力)

  • 入力:
    • RSA公開鍵ファイル(PKCS#1形式 PEM: publickey.pem
    • 暗号化対象の文字列
  • 出力: 暗号化されたBase64文字列
  • ファイル形式: ヘッダーが -----BEGIN RSA PUBLIC KEY----- であること(BEGIN PUBLIC KEY の場合は扱いが異なります)。

基本の使い方

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

// ファイルからPEM文字列を読み込み、ヘッダー・フッター・改行を除去してバイナリ化
var pemStr = File.ReadAllText("publickey.pem");
var base64 = pemStr.Replace("-----BEGIN RSA PUBLIC KEY-----", "")
                   .Replace("-----END RSA PUBLIC KEY-----", "")
                   .Replace("\r", "").Replace("\n", "");
var keyBytes = Convert.FromBase64String(base64);

using var rsa = RSA.Create();
rsa.ImportRSAPublicKey(keyBytes, out _);

// 暗号化
var data = Encoding.UTF8.GetBytes("Secret");
var encrypted = rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);

コード全文

.NET 6以降の環境で動作するコンソールアプリケーション形式です。

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

class Program
{
    static void Main()
    {
        // 1. PKCS#1形式の公開鍵ファイルを読み込む
        // ファイルが存在しない場合はダミー作成する処理を入れています
        string pemFilePath = "publickey.pem";
        EnsureDummyKeyFileExists(pemFilePath);

        try
        {
            // 2. PEMファイルの解析(ヘッダー削除とBase64デコード)
            // ImportRSAPublicKey はバイナリデータ(DER)を要求するため、
            // テキスト装飾(ヘッダー、フッター、改行)を全て削除する必要があります。
            string pemContent = File.ReadAllText(pemFilePath);
            
            string publicKeyBase64 = pemContent
                .Replace("-----BEGIN RSA PUBLIC KEY-----", "")
                .Replace("-----END RSA PUBLIC KEY-----", "")
                .Replace("\r", "") // Windowsの改行対策
                .Replace("\n", "") // Linux/Macの改行対策
                .Trim();

            byte[] publicKeyBytes = Convert.FromBase64String(publicKeyBase64);

            // 3. RSAインスタンスの生成とキーのインポート
            using var rsa = RSA.Create();
            
            // PKCS#1形式(RSAPublicKey)としてインポート
            rsa.ImportRSAPublicKey(publicKeyBytes, out int bytesRead);
            Console.WriteLine($"Key imported successfully. Read {bytesRead} bytes.");

            // 4. 暗号化処理
            string message = "これは秘密です。";
            byte[] messageBytes = Encoding.UTF8.GetBytes(message);

            // OAEP SHA256パディングを使用して暗号化
            byte[] encryptedBytes = rsa.Encrypt(messageBytes, RSAEncryptionPadding.OaepSHA256);

            // 5. 結果の出力
            string outputBase64 = Convert.ToBase64String(encryptedBytes);
            Console.WriteLine("\n[Encrypted Output (Base64)]");
            Console.WriteLine(outputBase64);

            // 参考: 読み込んだキーをXML形式で確認(必要であれば)
            // Console.WriteLine(rsa.ToXmlString(false));
        }
        catch (FormatException)
        {
            Console.WriteLine("Error: Invalid Base64 format in PEM file.");
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine($"Crypto Error: {ex.Message}");
            Console.WriteLine("Check if the key is actually PKCS#1 (BEGIN RSA PUBLIC KEY).");
        }
    }

    /// <summary>
    /// 動作確認用にPKCS#1形式の公開鍵ファイルを生成します
    /// </summary>
    static void EnsureDummyKeyFileExists(string path)
    {
        if (!File.Exists(path))
        {
            using var rsa = RSA.Create(2048);
            // PKCS#1形式のバイト配列を取得
            byte[] keyBytes = rsa.ExportRSAPublicKey();
            
            // 手動でPEM形式に整形
            var sb = new StringBuilder();
            sb.AppendLine("-----BEGIN RSA PUBLIC KEY-----");
            sb.AppendLine(Convert.ToBase64String(keyBytes, Base64FormattingOptions.InsertLineBreaks));
            sb.AppendLine("-----END RSA PUBLIC KEY-----");
            
            File.WriteAllText(path, sb.ToString());
            Console.WriteLine($"[Setup] Generated dummy key file: {path}");
        }
    }
}

カスタムポイント

  • PEM解析の簡略化: .NET 5以降を使用している場合、rsa.ImportFromPem(File.ReadAllText(path)) というメソッドを使用すれば、ヘッダーの削除やBase64デコードを自前で行う必要がなくなり、コードが劇的に短くなります。
  • 鍵形式の判別: もしヘッダーが -----BEGIN PUBLIC KEY-----(RSAが付かない)の場合は、PKCS#8(SubjectPublicKeyInfo)形式です。その場合は ImportSubjectPublicKeyInfo メソッドを使用するか、前述の ImportFromPem(形式を自動判別)を使用してください。

注意点

  1. ヘッダーの厳密な一致: コード内の Replace 文字列は、ファイル内のヘッダーと完全に一致している必要があります。スペースの数などが異なると置換に失敗し、FormatException になります。
  2. パディングモード: 入力コードにあった fOAEP: true(OAEP SHA1)に相当する RSAEncryptionPadding.OaepSHA1 よりも、現在は RSAEncryptionPadding.OaepSHA256 が推奨されます。復号側と設定を合わせることを忘れないでください。

応用

.NET 5以降でのモダンな実装(ImportFromPem)

手動の文字列操作を排除した推奨実装です。

using var rsa = RSA.Create();
string pemStr = File.ReadAllText("publickey.pem");

// ヘッダーを見て自動的に形式(PKCS#1 or PKCS#8)を判断して読み込む
rsa.ImportFromPem(pemStr);

// 以降の暗号化処理は同じ

まとめ

外部ファイルからRSA公開鍵を読み込む際は、そのファイルが「PKCS#1形式」なのか「PKCS#8/SPKI形式」なのかをヘッダーで区別する必要があります。ImportRSAPublicKey メソッドはバイト配列を受け取るため、事前にBase64デコードが必要ですが、モダンな .NET 環境では ImportFromPem を使用することでこれらの煩雑な手順を省略できます。

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

この記事を書いた人

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

目次