2009年1月27日火曜日

[.NET]文字列の暗号化(DPAPI)

「暗号化する=暗号用キーが必要」というのがあり、システム構築の上で、この鍵をどこに保存するかという問題が発生します。コレを解決したのがDPAPI(データ保護 API)です。Windows 2000以降のOSで利用できるようです。

3DESやAESのように暗号用のキー情報を保持する必要はありませんが、故障などでマシンが死亡すると復旧できません。 コードはこんなかんじ。

(ボタン押下の処理です。txtHashはTextBox。共通処理は3DESと同じ)
/// <summary>
/// DPAPIで暗号化
/// </summary>
/// <remarks>system.security.dllが必要</remarks>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnDPC_Click(object senderEventArgs e)
{
    //対象を16進バイト列化
    byte[] data = Encoding.UTF8.GetBytes(txtOriginal.Text);
    //バイト列を暗号化
    byte[] encData = ProtectedData.Protect(data
                                , entropy   //暗号用追加バイト列
                                , DataProtectionScope.LocalMachine);//暗号化スコープ(マシン範囲)
    //16進数文字列に変換
    txtHash.Text = byte2HexString(encData);

}

/// <summary>
/// DPAPI複合化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnDPD_Click(object senderEventArgs e)
{
    //対象を16進バイト列化
    byte[] data = hexString2Byte(txtHash.Text);
    //バイト列を複合化
    byte[] decData = ProtectedData.Unprotect(data 
                                , entropy   //暗号用追加バイト列
                                , DataProtectionScope.LocalMachine);//暗号化スコープ(マシン範囲)
    // 複合化されたデータを取得
    string code = Encoding.UTF8.GetString(decData);
    txtRestre.Text = code;

}

2009年1月25日日曜日

[.NET]文字列の暗号化(RSA)

標準的な暗号化方式の1つに公開鍵暗号があります。
コードはこんなかんじ。このサンプルでは、動的に公開鍵と秘密鍵を生成しています。

(ボタン押下の処理です。txtOriginalとtxtHashはTextBox。共通処理は3DESと同じ)
//公開鍵をXML形式で取得
private string publicKey = string.Empty;
//秘密鍵をXML形式で取得
private string privateKey = string.Empty;

/// <summary>
/// RSAで暗号化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnRSAC_Click(object senderEventArgs e)
{
    //対象を16進バイト列化
    byte[] data = Encoding.UTF8.GetBytes(txtOriginal.Text);

    using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
    {

        //公開鍵・秘密鍵をこのタイミングで生成する
        publicKey = rsa.ToXmlString(false);
        privateKey = rsa.ToXmlString(true);

        //公開鍵を指定
        rsa.FromXmlString(publicKey);

        //バイト列を暗号化
        byte[] encData = rsa.Encrypt(datatrue);
        //16進数文字列に変換
        txtHash.Text = byte2HexString(encData);
    }
}

/// <summary>
/// RSAで複合化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnRSAD_Click(object senderEventArgs e)
{
    byte[] data = hexString2Byte(txtHash.Text);
    using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
    {

        //秘密鍵を指定
        rsa.FromXmlString(privateKey);

        //バイト列を複合化
        byte[] decData = rsa.Decrypt(datatrue);
        // 複合化されたデータを取得
        string code = Encoding.UTF8.GetString(decData);
        txtRestre.Text = code;
    }
}

2009年1月24日土曜日

[.NET]文字列の暗号化(AES)

.NET Frameworkではいくつか暗号化のアルゴリズムが提供されています。前回は3DES(トリプルDES)を利用した暗号化を実施しました。今回はAESというアルゴリズムを利用します。コードは3DESとほとんど同じですが、暗号化用のキー長が32bitになり、かつ3DESよりも高速に動作するようです。
3DES同様に暗号用のキー情報を保持する必要があり、その点が問題として残ります。コードはこんなかんじ。

(ボタン押下の処理です。txtOriginalとtxtHashはTextBox。共通処理は3DESと同じ)

//using System.Security.Cryptography;
//using System.IO;

string key = "0123456789ABCDEF0123456789ABCDEF";
string vec = "0123456789ABCDEF";

/// <summary>
/// AES暗号化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAESC_Click(object senderEventArgs e)
{
    //対象を16進バイト列化
    byte[] data = Encoding.UTF8.GetBytes(txtOriginal.Text);

    //暗号用のキー情報をセットする
    byte[] aesKey = Encoding.UTF8.GetBytes(key);
    byte[] aesIV = Encoding.UTF8.GetBytes(vec);

    //暗号化オブジェクトとストリームを作成する
    using (RijndaelManaged aes = new RijndaelManaged())
    using (MemoryStream ms = new MemoryStream())
    using (CryptoStream cs = new CryptoStream(msaes.CreateEncryptor(aesKeyaesIV),
                                       CryptoStreamMode.Write))
    {
        // ストリームに暗号化するデータを出力
        cs.Write(data0data.Length);
        cs.Close();

        // 暗号化されたデータを取得
        string code = byte2HexString(ms.ToArray());
        txtHash.Text = code;

        ms.Close();
    }
}

/// <summary>
/// AES複合化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAESD_Click(object senderEventArgs e)
{
    //対象を16進バイト列化
    byte[] data = hexString2Byte(txtHash.Text);

    //暗号用のキー情報をセットする
    byte[] aesKey = Encoding.UTF8.GetBytes(key);
    byte[] aesIV = Encoding.UTF8.GetBytes(vec);

    //暗号化オブジェクトとストリームを作成する
    using (RijndaelManaged aes = new RijndaelManaged())
    using (MemoryStream ms = new MemoryStream())
    using (CryptoStream cs = new CryptoStream(msaes.CreateDecryptor(aesKeyaesIV),
                                       CryptoStreamMode.Write))
    {
        // ストリームに複合化するデータを出力
        cs.Write(data0data.Length);
        cs.Close();

        // 複合化されたデータを取得
        string code = Encoding.UTF8.GetString(ms.ToArray());
        txtRestre.Text = code;

        ms.Close();
    }
}

2009年1月19日月曜日

[.NET]文字列の暗号化(3DES)

MD5などのハッシュアルゴリズムは可逆変換ができない(前提である)ことと、常に同じ結果が出ることを利用し、符号化した情報を照合するという方法を取っていますが、今回は他システム連携する場合に、その他システム用のパスワードを保存したい場合に利用する暗号化について書きます。
.NET Frameworkではいくつか暗号化のアルゴリズムが提供されています。以下では3DES(トリプルDES)を利用した暗号化を実施しています。

しかし、3DESではどこかしらに暗号用のキー情報を保持する必要があり、その点が問題として残ります。コードはこんなかんじ。

■ハッシュ
(ボタン押下の処理です。txtOriginalとtxtHashはTextBox)
//using System.Security.Cryptography;
//using System.IO;

string key = "123456789ABCDEF123454678";
string vec = "12345678";

/// <summary>
/// 3DES暗号化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn3DESC_Click(object senderEventArgs e)
{
    //対象を16進バイト列化
    byte[] data = Encoding.UTF8.GetBytes(txtOriginal.Text);

    //暗号用のキー情報をセットする
    byte[] desKey = Encoding.UTF8.GetBytes(key);
    byte[] desIV = Encoding.UTF8.GetBytes(vec);

    //暗号化オブジェクトとストリームを作成する
    using (TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider())
    using (MemoryStream ms = new MemoryStream())
    using (CryptoStream cs = new CryptoStream(mstdes.CreateEncryptor(desKeydesIV),
                                       CryptoStreamMode.Write))
    {
        // ストリームに暗号化するデータを出力
        cs.Write(data0data.Length);
        cs.Close();

        // 暗号化されたデータを取得
        string code = byte2HexString(ms.ToArray());
        txtHash.Text = code;

        ms.Close();
    }
}

/// <summary>
/// 3DES複合化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn3DESD_Click(object senderEventArgs e)
{
    //対象を16進バイト列化
    byte[] data = hexString2Byte(txtHash.Text);

    //暗号用のキー情報をセットする
    byte[] desKey = Encoding.UTF8.GetBytes(key);
    byte[] desIV = Encoding.UTF8.GetBytes(vec);

    //暗号化オブジェクトとストリームを作成する
    using (TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider())
    using (MemoryStream ms = new MemoryStream())
    using (CryptoStream cs = new CryptoStream(mstdes.CreateDecryptor(desKeydesIV),
                                       CryptoStreamMode.Write))
    {
        // ストリームに複合化するデータを出力
        cs.Write(data0data.Length);
        cs.Close();

        // 複合化されたデータを取得
        string code = Encoding.UTF8.GetString(ms.ToArray());
        txtRestre.Text = code;

        ms.Close();
    }
}


■文字列・バイト列操作の共通メソッド
/// <summary>
/// 16進数の文字列をバイト列に変換する
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private byte[] hexString2Byte(string data)
{

    int size = data.Length / 2;
    byte[] result = new byte[size];

    //2文字ずつ進み、バイトに変換
    for (int i = 0i < sizei++)
    {
        string target = data.Substring(i * 22);
        result[i] = Convert.ToByte(target16);
    }

    return result;
}

/// <summary>
/// バイト列を16進数の文字列に変換する
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private string byte2HexString(byte[] data)
{
    //00-11-22形式を001122に変換
    return BitConverter.ToString(data).Replace("-""");
}

2009年1月14日水曜日

[.NET]ハッシュ(MD5,SHA)

システム上、ユーザに入力されたパスワードのようなデータを定義ファイルやレジストリに保存しておきたい場合があります。しかし平文でおいておくと、第三者がそのファイルより情報を盗み見ることができます。結果本人に成りすましてシステムを悪用することも可能になります。
そのため、パスワードを人が読めない形式に変換して保存しておくなどの工夫が必要になります。
MD5やSHAはハッシュのアルゴリズムで、特定の情報を可逆変換不可能な値に変換することができます。そして同じインプットであれば同じ結果となります。コレを利用し、以下のような手順で処理を実現します。
  1. ユーザにパスワードを入力してもらう
  2. MD5やSHAでハッシュした情報を定義ファイルに保存する

そして、再度認証が必要な場合、

  1. ユーザにパスワードを入力してもらう
  2. 入力値をMD5やSHAでハッシュする
  3. 定義ファイルよりハッシュしたパスワードを取得する
  4. 2と3を比較して同じなら認証成功

というかんじ。.NET Frameworkでは、MD5やSHAをはじめいくつかのハッシュアルゴリズム提供されています。以下では、MD5,SHA,SHA512を使ってハッシュしています。コードはこんな感じ。



■ハッシュ
(ボタン押下の処理です。txtOriginalとtxtHashはTextBox)
//using System.Security.Cryptography;


/// <summary>
/// MD5ハッシュ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnMD5H_Click(object senderEventArgs e)
{
    //入力値よりUTF8のバイト列を取得する
    byte[] data = Encoding.UTF8.GetBytes(txtOriginal.Text);

    //ハッシュする
    using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
    {
        string code = byte2HexString(md5.ComputeHash(data));
        txtHash.Text = code;
    }

}

/// <summary>
/// SHAハッシュ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSHAH_Click(object senderEventArgs e)
{
    //入力値よりUTF8のバイト列を取得する
    byte[] data = Encoding.UTF8.GetBytes(txtOriginal.Text);

    //ハッシュする
    using (SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider())
    {
        string code = byte2HexString(sha.ComputeHash(data));
        txtHash.Text = code;
    }

}

/// <summary>
/// SHA512ハッシュ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSHA512H_Click(object senderEventArgs e)
{
    //入力値よりUTF8のバイト列を取得する
    byte[] data = Encoding.UTF8.GetBytes(txtOriginal.Text);

    //ハッシュする
    using (SHA512Managed sha = new SHA512Managed())
    {
        string code = byte2HexString(sha.ComputeHash(data));
        txtHash.Text = code;
    }
}


■文字列・バイト列操作の共通メソッド
/// <summary>
/// 16進数の文字列をバイト列に変換する
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private byte[] hexString2Byte(string data)
{

    int size = data.Length / 2;
    byte[] result = new byte[size];

    //2文字ずつ進み、バイトに変換
    for (int i = 0i < sizei++)
    {
        string target = data.Substring(i * 22);
        result[i] = Convert.ToByte(target16);
    }

    return result;
}

/// <summary>
/// バイト列を16進数の文字列に変換する
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private string byte2HexString(byte[] data)
{
    //00-11-22形式を001122に変換
    return BitConverter.ToString(data).Replace("-""");
}

2009年1月8日木曜日

[.NET]文字列の16進数変換&復元

特定の文字列をバイト列に変換し、変換したバイト列を16進数の文字列に変換する。みたいな事をしたい場合が時々あります。そういった場合、.NET FrameworkのBitConverterを使うと楽に処理できます。
コードはこんな感じ。

// 文字列からUTF8のバイト列に変換
byte[] data = Encoding.UTF8.GetBytes("1192つくろう鎌倉幕府!!");

//00-11-22形式の文字列に変換
string hexText = BitConverter.ToString(data);
System.Diagnostics.Debug.WriteLine("16進文字列 [" + hexText + "]");

//00-11-22の文字列よりbyte[]に変換
string[] hexChars = hexText.Split('-');
//16進文字列をbyteに変換
byte[] decData = new byte[hexChars.Length];
for (int i=0i <hexChars.Length ;i++)
{
    decData[i] = Convert.ToByte(hexChars[i], 16);
}

// UTF8のバイト列からstringに変換
string decText = Encoding.UTF8.GetString(decData);

System.Diagnostics.Debug.WriteLine("復元結果 [" + decText + "]");


■実行結果
16進文字列 [31-31-39-32-E3-81-A4-E3-81-8F-E3-82-8D-E3-81-86-E9-8E-8C-E5-80-89-E5-B9-95-E5-BA-9C-EF-BC-81-EF-BC-81]
復元結果 [1192つくろう鎌倉幕府!!]


BitConverter.ToString(data).Replace("-", "")とすれば、「-」がなくなるので文字数を少なくすることができます。また、先頭に"0x"をつけてBitConverter.ToString(data).Replace("-", ",0x")とすれば、おなじみの形式になりますね。

2009年1月7日水曜日

[.NET]コマンドラインから一気にビルドする

.NET Frameworkに存在するコマンドで、VS.NETのソリューションを指定してビルドすることができます。
複数のプロジェクトやバッチでビルドしたい場合、VS.NETを起動しなくてもビルドすることができちゃいます。.NET Framework2.0系での書き方はこんなかんじ。

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\msbuild abc.sln

・・・もしかするとVS.NETが無くてもビルドできる?のかも(未検証)

2009年1月4日日曜日

[.NET]削除されているDataRowよりデータを取り出す

たとえば、DataGridViewのDataSourceにDataTableをセットしておけば、DataGridViewでの編集をそのままDataTableに反映させることができます。
その編集で行の削除をした場合、DataTableのDataRowは実際には削除されずRowStateがDataRowState.Deletedになります。この状態で該当のDataRowの内容を参照しようとすると、例外が発生します。そこでDataRowの配列?アクセサのオーバーロードを利用します。コードはこんなかんじ。

string id = dataRow[1DataRowVersion.Originalas string;

2009年1月2日金曜日

[.NET]Stopwatch

パフォーマンスチューニングするときに、まずやらなければならないことは、メソッドなり処理なりの時間を計測することです。少し直しては時間計測します。この作業を目標に達成するまで繰り返します。

.NET Frameworkでは簡単に時間を計測するためにStopwatchというクラスが用意されています。なぜか意外と知られていないっぽい。コードは。こんなかんじ。


System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();

//何か処理

sw.Stop();
toolStripStatusLabel.Text = String.Format(toolStripStatusLabel.Text + " 完了{0:#,##0}ms"sw.ElapsedMilliseconds);