search
LoginSignup
6
Help us understand the problem. What are the problem?

posted at

updated at

.NETで暗号化

はじめに

最近なら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);

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6
Help us understand the problem. What are the problem?