今回はファイルではなく文字列のみを暗号化します。そして暗号化された文字列を表示させます。
暗号化したあと、base64で表示させています。
base64とは、64進数を意味する言葉で、すべてのデータをアルファベット(a~z, A~z)と数字(0~9)、一部の記号(+,/)の64文字で表すエンコード方式です。
ただ、データ長を揃えるためにパディングとして末尾に記号の=を使用するので、厳密にはbase64は、65文字の英数字から表現されます。
base64とは
これなら暗号化されたデータがどのように変化するのかがわかります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
public partial class Form1 : Form { private const int KEY_SIZE = 256; // 256Bit private const int BLOCK_SIZE = 128; // 128Bit private const int BUFFER_SIZE = BLOCK_SIZE * 32; // バッファーサイズはBlockSizeの倍数にする private const int SALT_SIZE = 8; // 8以上 private void buttonEncode_Click(object sender, EventArgs e) { richTextBoxEncode.Text = EncodeString(richTextBoxPlaneText.Text, textBoxPassword.Text); } /// <summary> /// Saltを生成する /// </summary> /// <param name="str">8文字以上の文字列を指定すること</param> private byte[] CreateSalt(string str) { Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(str, SALT_SIZE); return deriveBytes.Salt; } /// <summary> /// 初期化ベクトルを作成する /// </summary> /// <param name="str">8文字以上の文字列を指定すること</param> private byte[] CreateIV(string str) { Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(str, BLOCK_SIZE / 8); return deriveBytes.GetBytes(BLOCK_SIZE / 8); } //パスワードとソルトから暗号鍵を求める private byte[] GetKeyFromPassword(string password, byte[] salt) { //Rfc2898DeriveBytesオブジェクトを作成する Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(password, salt); //反復処理回数を指定する デフォルトで1000回 deriveBytes.IterationCount = 1000; //キーを生成する return deriveBytes.GetBytes(KEY_SIZE / 8); } string EncodeString(string srcString, string password) { byte[] salt; if(!checkBoxSalt.Checked) { salt = CreateSalt("8文字以上の文字列を指定すること"); textBoxSalt.Text = Convert.ToBase64String(salt); } else { // [ソルト固定]がチェックされている場合はすでにテキストボックスに表示されているものを使う try { salt = Convert.FromBase64String(textBoxSalt.Text); } catch { // テキストボックスに表示されている文字列が使えない場合は作り直す salt = CreateSalt("8文字以上の文字列を指定すること"); textBoxSalt.Text = Convert.ToBase64String(salt); } } byte[] iv; if(!checkBoxIV.Checked) { iv = CreateIV("8文字以上の文字列を指定すること"); textBoxIV.Text = Convert.ToBase64String(iv); } else { // [IV固定]がチェックされている場合はすでにテキストボックスに表示されているものを使う try { iv = Convert.FromBase64String(textBoxIV.Text); } catch { // テキストボックスに表示されている文字列が使えない場合は作り直す iv = CreateIV("8文字以上の文字列を指定すること"); textBoxIV.Text = Convert.ToBase64String(iv); } } byte[] key = GetKeyFromPassword(password, salt); textBoxKey.Text = Convert.ToBase64String(key); byte[] outBytes = null; using(AesManaged aes = new AesManaged()) { // AESインスタンスのパラメータ設定 aes.KeySize = KEY_SIZE; aes.BlockSize = BLOCK_SIZE; aes.Mode = CipherMode.CBC; aes.Key = key; aes.IV = iv; aes.Padding = PaddingMode.ISO10126; using(ICryptoTransform ct = aes.CreateEncryptor(aes.Key, aes.IV)) // 暗号化オブジェクト生成 using(MemoryStream outStream = new MemoryStream()) // 出力ストリーム { using(CryptoStream cryptoStream = new CryptoStream(outStream, ct, CryptoStreamMode.Write)) { byte[] buf = Encoding.Unicode.GetBytes(srcString); cryptoStream.Write(buf, 0, buf.Length); } outBytes = outStream.ToArray(); } } List<byte> vs = new List<byte>(); vs.AddRange(salt); vs.AddRange(iv); vs.AddRange(outBytes); return Convert.ToBase64String(vs.ToArray()); } } |
また[暗号化]をクリックし続けると、平文が同じでパスワードも同じでも暗号文は変化することがわかります(IVが変わるため)。またSaltもその都度変わるので同じパスワードでも異なる暗号鍵が生成されています。
ではSaltとIVを両方とも固定してしまうと同じ暗号文になるのでしょうか? パディングをPaddingMode.ISO10126(ランダムな数値で埋める)にしているので同じにはなりませんが、前のほうは同じになります(長めの文字列をいれるとわかる)。
復号化をクリックすると元の平文が表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
public partial class Form1 : Form { private void buttonDecode_Click(object sender, EventArgs e) { richTextBoxPlaneText.Text = DecodeString(richTextBoxEncode.Text, textBoxPassword.Text); } string DecodeString(string srcString, string password) { byte[] bytes = Convert.FromBase64String(srcString); byte[] salt = new byte[SALT_SIZE]; byte[] iv = new byte[BLOCK_SIZE / 8]; byte[] data = new byte[bytes.Length - SALT_SIZE - BLOCK_SIZE / 8]; // ソルトとIVと暗号データを読み取る using(MemoryStream ms = new MemoryStream(bytes)) { ms.Read(salt, 0, SALT_SIZE); ms.Read(iv, 0, BLOCK_SIZE / 8); ms.Read(data, 0, bytes.Length - SALT_SIZE - BLOCK_SIZE / 8); } textBoxSalt.Text = Convert.ToBase64String(salt); textBoxIV.Text = Convert.ToBase64String(iv); byte[] key = GetKeyFromPassword(password, salt); textBoxKey.Text = Convert.ToBase64String(key); string decodeString = ""; using(AesManaged aes = new AesManaged()) { // AESインスタンスのパラメータ設定 aes.KeySize = KEY_SIZE; aes.BlockSize = BLOCK_SIZE; aes.Mode = CipherMode.CBC; aes.Key = key; aes.IV = iv; aes.Padding = PaddingMode.ISO10126; // 暗号化オブジェクト生成 ICryptoTransform ct = aes.CreateDecryptor(aes.Key, aes.IV); using(var inStream = new System.IO.MemoryStream(data, false)) // 入力ストリームを開く using(var outStream = new System.IO.MemoryStream()) // 出力ストリーム { using(CryptoStream cryptoStream = new CryptoStream(inStream, ct, CryptoStreamMode.Read)) { byte[] buffer = new byte[BUFFER_SIZE]; int len = 0; while((len = cryptoStream.Read(buffer, 0, BUFFER_SIZE)) > 0) outStream.Write(buffer, 0, len); } byte[] outBytes = outStream.ToArray(); decodeString = Encoding.Unicode.GetString(outBytes); } } return decodeString; } } |
はじめまして。質問をさせてください。
文字列を暗号化した後に、なぜBase64エンコードをするのでしょうか?
よろしければ、理由を教えていただきたいです。
Base64エンコードしないと文字化けする疑いがあるとかでしょうか?
文字列を暗号化した後に、なぜBase64エンコードをするのでしょうか?
最初から「暗号化の対象は文字列である」という前提があるからです。
だから暗号化されたデータも文字列にしたかったというそれだけの理由です。
また暗号化の結果をテキストボックスに表示していますが、そのためには暗号化されたデータはテキストでないと表示させることができないのです。
それで暗号化した後にBase64エンコードをしました。