シリーズもの第2回。
第1回: https://qiita.com/macchan/items/c649d8f8b1aeb4cf3552
1. はじめに
と、いうことで続きです。
Aさん、Bさんという登場人物が解っています。せっかくなのでクラス化しましょう。
2. 便利な PemTool クラス
何かと使うことになる PemTool クラスを作ります。
やっていることは PEM形式の入力と出力です。
PemTool.cs
// PemTool.cs
using Org.BouncyCastle.OpenSsl;
using System.IO;
namespace MyCryptRSA
{
static class PemTool
{
// =========================
// PEM出力関数
// =========================
static public string ToPemString(object key)
{
using (var sw = new StringWriter())
{
var pemWriter = new PemWriter(sw);
pemWriter.WriteObject(key);
pemWriter.Writer.Flush();
return sw.ToString();
}
}
// =========================
// PEM入力関数
// =========================
static public object ReadKeyFromPem(string pem)
{
using (var sr = new StringReader(pem))
{
var pemReader = new PemReader(sr);
return pemReader.ReadObject();
}
}
}
}
3. Aさんクラス
Aさんがやることは秘密鍵-公開鍵の作成と復号です。
Bさんからの暗号文は Base64でもらうことにします。
PersonA.cs
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Security;
using System;
using System.Text;
namespace MyCryptRSA
{
internal class PersonA
{
// 秘密鍵
private string _privateKeyPem;
// =========================
// 1. Aさんが鍵ペアを生成
// =========================
public string CreateKey()
{
var keyGen = new RsaKeyPairGenerator();
// 2048ビットの鍵
keyGen.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
AsymmetricCipherKeyPair keyPair = keyGen.GenerateKeyPair();
// 公開鍵・秘密鍵をPEM形式で保存(ファイル保存は任意)
string publicKeyPem = PemTool.ToPemString(keyPair.Public);
// 秘密鍵は後で使うので保存しておく
_privateKeyPem = PemTool.ToPemString(keyPair.Private);
Console.WriteLine("公開鍵(Aさん):");
Console.WriteLine(publicKeyPem);
Console.WriteLine("秘密鍵(Aさん):");
Console.WriteLine(_privateKeyPem);
// 公開鍵を返す
return publicKeyPem;
}
// =========================
// 3. Aさんが秘密鍵で復号
// =========================
public void Decode(string strCipher)
{
// Base64 を バイナリデータに変換
byte[] encryptedBytes = Convert.FromBase64String(strCipher);
// PEMから鍵を取得
var keyPair = PemTool.ReadKeyFromPem(_privateKeyPem) as AsymmetricCipherKeyPair;
// AsymmetricCipherKeyPair から Private を取り出す
AsymmetricKeyParameter privateKey = keyPair.Private;
var decryptCipher = new Pkcs1Encoding(new RsaEngine());
// false = 復号
decryptCipher.Init(false, privateKey);
byte[] decryptedBytes = decryptCipher.ProcessBlock(encryptedBytes, 0, encryptedBytes.Length);
string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
Console.WriteLine("\nAさんが復号した平文:");
Console.WriteLine(decryptedText);
}
}
}
4. Bさんクラス
Bさんがやることは公開鍵で自分が持っている平文の暗号化です。
引数で公開鍵を受け取り、暗号文を返します。
PersonB.cs
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using System;
using System.Text;
namespace MyCryptRSA
{
class PersonB
{
// =========================
// 2. Bさんが公開鍵で暗号化
// =========================
public string Encode(string strPubKey)
{
string plainText = "こんにちは、私はBさんです。Aさん! 秘密メッセージです!";
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
// 公開鍵を読み込む(BさんがAさんから受け取った鍵を利用)
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)PemTool.ReadKeyFromPem(strPubKey);
var encryptCipher = new Pkcs1Encoding(new RsaEngine());
// true = 暗号化
encryptCipher.Init(true, publicKey);
// 暗号文
byte[] encryptedBytes = encryptCipher.ProcessBlock(plainBytes, 0, plainBytes.Length);
// 暗号文をBase64化
string strCipher = Convert.ToBase64String(encryptedBytes);
Console.WriteLine("\nBさんが暗号化したデータ(Base64):");
Console.WriteLine(strCipher);
return strCipher;
}
}
}
5. 最後に流れを作ります
Program.cs
// RSA公開鍵暗号
using MyCryptRSA;
class Program
{
static void Main()
{
PersonA a = new PersonA();
PersonB b = new PersonB();
// =========================
// 1. Aさんが鍵ペアを生成。公開鍵を得る。
// =========================
string strPubKey = a.CreateKey();
// =========================
// 2. Bさんが公開鍵で暗号化
// =========================
string strCipher = b.Encode(strPubKey);
// =========================
// 3. Aさんが秘密鍵で復号
// =========================
a.Decode(strCipher);
}
}
6. 最後に
RSA公開鍵暗号方式の欠点というべきかどうかわかりませんが、以下のように言われています。
・鍵が短いと破られやすい(2048ビット以上が推奨)。
・長い(平)文の暗号化には向かない(鍵長よりも短いことが望ましい)
そこで、別の暗号方式と組み合わせて暗号化するやり方が主流になっているようです。
例えば、
- 共通鍵暗号化方式で使う共通鍵をこの公開鍵暗号方式
- 主流の暗号-復号は AES-GCM-256などの共通鍵暗号方式
なのだそうです。
暗号化-復号 といってもいろいろあって大変ですね!