はじめに
最近ならAES256/GCMを使っておけば大丈夫な気がするので、その使い方。
.NETの場合、.NET Framework では最新の4.8でもAesGcmが無いので使えないことに注意、.NET Frameworkでなければならない場合はAES256/CBCとかにするのかな。CBCではパディングオラクル攻撃に注意。パディングのエラーをそれとわかるように通知してはいけない。
.NET Core 3.0 以降なら大丈夫、もちろん .NET 5, 6, 7もOK。
Javaで暗号化、.NETで復号化とかしようとすると、tagが別になっているのでうまくいかない、注意。
Java版の話はこちら⇨ Javaで暗号化
暗号鍵
Javaと違って単なるbyte配列で良い。パスワードから作る場合はSHA256で。
using System.Text;
using System.Security.Cryptography;
.
.
.
public byte[] generateRandomKey()
{
byte[] key = new byte[32];
RandomNumberGenerator.Fill(key);
return key;
}
public byte[] getKeyFromPassword(string pass)
{
SHA256 sha = SHA256.Create();
byte[] ret = sha.ComputeHash(Encoding.UTF8.GetBytes(pass));
return ret;
}
IVとtag
GCMでは改ざんを検知するために暗号文と同時にtagが出力される。Javaでは暗号文と一緒のbyte配列になって出力されたが、.NETでは別に領域を用意しておく必要がある。
IVをnonceから作るのはJavaと同じ。
バッファサイズは一応定数定義があるので、それを使っている。
nonce, tag, 暗号文は復号側にそのまま送って構わない。
using System.Security.Cryptography;
.
.
.
public byte[] generateNonce()
{
byte[] nonce = new byte[AesGcm.NonceByteSizes.MaxSize];
return nonce;
}
public byte[] getTagBuffer()
{
byte[] tag = new byte[AesGcm.TagByteSizes.MaxSize];
return tag;
}
AAD
AADについてもJavaと同じで、任意のbyte配列を使う。
サンプルでは「AADは追加認証データ、中身はなんでも良いが64kBytes以内にする」の文字列をUTF-8のバイト列として使っている。
暗号化
AesGcmを使って暗号化する。暗号文とtagが別々になっていて、tag用の領域は先に確保しておく必要がある。
using System.Security.Cryptography;
.
.
.
public byte[] encrypt(byte[] src, byte[] key, byte[] nonce, byte[] tag_dest)
{
AesGcm aes = new AesGcm(key);
byte[] dest_buf = new byte[src.Length];
byte[] aad = Encoding.UTF8.GetBytes("AADは追加認証データ、中身はなんでも良いが64kBytes以内にする");
aes.Encrypt(nonce, src, dest_buf, tag_dest, aad);
return dest_buf;
}
復号化
暗号化とほぼ変わらないが、tagは入力になる。
using System.Security.Cryptography;
.
.
.
public byte[] decrypt(byte[] src, byte[] key, byte[] nonce, byte[] tag_src)
{
AesGcm aes = new AesGcm(key);
byte[] dest_buf = new byte[src.Length];
byte[] aad = Encoding.UTF8.GetBytes("AADは追加認証データ、中身はなんでも良いが64kBytes以内にする");
aes.Decrypt(nonce, src, tag_src, dest_buf, aad);
return dest_buf;
}
まとめ
実際に使う場合はこんな感じ。
// 暗号化
byte[] key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = generateNonce();
byte[] tag = getTagBuffer();
byte[] encdata = encrypt(srcdata, key, nonce, tag);
// 復号化
byte[] key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = <送られてきたnonce>;
byte[] tag = <送られてきたtag>;
byte[] decdata = decrypt(encdata, key, nonce, tag);