共通鍵 (対称鍵) 暗号化アルゴリズム

前回の SqlMembershipProvider のハッシュ アルゴリズムでは SqlMembershipProvider の内部でのハッシュ化のコードについて紹介しましたが、
今回は同様に、ASP.NET の内部での共通鍵 (対称鍵) 暗号化のコードについて記述します。
 
具体的には、MachineKeySection クラスの中にコードがあります。
そこではだいたい以下のような処理が実行されています。
SqlMembershipProvider で共通鍵 (対称鍵) 暗号化を利用する場合にもこのコードが使用されます。

static void Main()
{
    // Rijndael (128 ビット) で暗号化および復号します。
    string encryptedPassword = Encrypt("P@ssw0rd", "D74A4970EBF85F9291C0D28FE01296B2");
    string decryptedPassword = Decrypt(encryptedPassword, "D74A4970EBF85F9291C0D28FE01296B2");
}

private static string symmetricAlgorithmName = "Rijndael"; // 変更可能
private static Encoding encoding = Encoding.Unicode;

public static byte[] Encrypt(byte[] data, byte[] decryptionKey)
{
    return Transform(data, decryptionKey, true);
}

public static string Encrypt(string text, string decryptionKey)
{
    return Convert.ToBase64String(Encrypt(encoding.GetBytes(text), ToByteArray(decryptionKey)));
}

public static byte[] Decrypt(byte[] data, byte[] decryptionKey)
{
    return Transform(data, decryptionKey, false);
}

public static string Decrypt(string text, string decryptionKey)
{
    return encoding.GetString(Decrypt(Convert.FromBase64String(text), ToByteArray(decryptionKey)));
}

private static byte[] Transform(byte[] data, byte[] decryptionKey, bool encrypt)
{
    using (SymmetricAlgorithm symAlg = CreateSymmetricAlgorithm(decryptionKey))
    using (ICryptoTransform transform = encrypt ? symAlg.CreateEncryptor() : symAlg.CreateDecryptor())
    using (MemoryStream stream = new MemoryStream())
    using (CryptoStream cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Write))
    {
        cryptoStream.Write(data, 0, data.Length);
        cryptoStream.FlushFinalBlock();

        return stream.ToArray();
    }
}

private static SymmetricAlgorithm CreateSymmetricAlgorithm(byte[] decryptionKey)
{
    SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create(symmetricAlgorithmName);

    symAlg.Key = decryptionKey;
    symAlg.IV = new byte[symAlg.BlockSize / 8];

    return symAlg;
}

// ランダムな復号キーを生成します。
public static string GenerateDecryptionKey(int size)
{
    return ToHexString(GenerateRandomByteArray(size));
}

// ランダムなバイト配列を生成します。
public static byte[] GenerateRandomByteArray(int size)
{
    byte[] data = new byte[size];

    RandomNumberGenerator.Create().GetBytes(data);

    return data;
}

// 16 進数表記の文字列をバイト配列に変換します。
public static byte[] ToByteArray(string hexString)
{
    return Enumerable.Range(0, hexString.Length / 2)
        .Select(i => hexString.Substring(2 * i, 2))
        .Select(s => byte.Parse(s, NumberStyles.HexNumber))
        .ToArray();
}

// バイト配列を 16 進数表記の文字列に変換します。
public static string ToHexString(byte[] buffer)
{
    return string.Concat(buffer.Select(b => b.ToString("X2")).ToArray());
}


 
注意点
(1) 上記のコードは、比較的短いデータに対しての暗号化および復号が想定されているため、入出力の型は byte[] となっています。
     ファイルのようなデータを暗号化の対象とする場合には、
     Stream を使用したインターフェイスに変更してバッファリングを組み込む必要があります。
 
(2) decryptionKey に指定できるキーの長さは、以下のように暗号化アルゴリズムごとに決められています。
     この情報は SymmetricAlgorithm.LegalKeySizes プロパティで取得することができます。
  • Rijndael または AES の場合: 128, 192, 256 ビットのいずれか
  • TripleDES の場合: 128, 192 ビットのいずれか
バージョン情報
.NET Framework 4
 
参照
カテゴリー: .NET Framework. タグ: . 1 Comment »

SqlMembershipProvider のハッシュ アルゴリズム

前回の Silverlight Business Application テンプレートの認証機能でパスワードに対するハッシュ アルゴリズムについて少し触れましたが、
今回は SqlMembershipProvider についてより詳しく記述してみます。
 
■ 使用されるハッシュ アルゴリズムを指定する方法
.NET Framework 4 の SqlMembershipProvider が使用するハッシュ アルゴリズムは、次のロジックで決まるようです。
  1. <membership> 要素の hashAlgorithmType 属性が
      a. 指定されているとき、その値。
      b. 指定されていないとき、<membership> – <providers> – <add> 要素の passwordCompatMode 属性が
            i. 指定されていないまたは Framework20 のとき、SHA1。
            ii. Framework40 のとき、<machineKey> 要素の validation 属性の値 (既定では HMACSHA256)。
  2. 上記の結果が MD5、AES または TripleDES のとき、SHA1。
従って、Web.config で何も指定しない場合、Membership.HashAlgorithmType で取得される値が HMACSHA256 であるにもかかわらず、
SqlMembershipProvider が使用するハッシュ アルゴリズムは SHA1 となります。
 
上記で登場した要素や属性をすべて記述した Web.config がこちらです。

  <system.web>
    <machineKey validation="HMACSHA256" />
    <membership defaultProvider="Default" hashAlgorithmType="HMACSHA256">
      <providers>
        <clear />
        <add passwordCompatMode="Framework40"
          name="Default" applicationName="BusinessApplication1" connectionStringName="Business1ConnectionString"
          type="System.Web.Security.SqlMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
      </providers>
    </membership>
  </system.web>

 
passwordCompatMode 属性に指定できるのは、MembershipPasswordCompatibilityMode 列挙体の値です。
passwordCompatMode 属性は .NET Framework 4 で追加された SqlMembershipProvider 固有の属性であり、
まだあまり認知されていないと思いますが、ここに Framework40 を指定しないと validation 属性の値は反映されません。
(今のところ、MSDN ライブラリにもまだ記述がありません。検索してもヒットしません。)
 
結論として、<membership> 要素の hashAlgorithmType 属性を明示的に指定しておくのが一番わかりやすい方法であると考えられます。
逆に、ここの値を指定しておけば他の部分を指定する必要はありません。
 
また、SHA1 については脆弱性が指摘されており、より強度のあるアルゴリズムが推奨されるため、
HMACSHA256 や SHA256 などを指定することが望ましいです。
 
■ ハッシュ化のためのコード
SqlMembershipProvider は、ハッシュの強度を増すために、
ソルトと呼ばれるユーザーごとに与えられるデータと組み合わせてパスワードをハッシュ化します。
 
SqlMembershipProvider では、だいたい以下のような処理が実行されています。
SqlMembershipProvider を使わずに認証ロジックを自作したいという場合にも、これらのコードが参考になると思います。

static void Main()
{
    string hashedPassword = Hash("P@ssw0rd", "j44YfP/l4cC9OTxsBiiQ+w==");
}

private static string hashAlgorithmName = "HMACSHA256"; // 変更可能
private static Encoding encoding = Encoding.Unicode;

public static byte[] Hash(byte[] data, byte[] salt)
{
    using (HashAlgorithm hashAlgorithm = HashAlgorithm.Create(hashAlgorithmName))
    {
        if (hashAlgorithm is KeyedHashAlgorithm)
        {
            KeyedHashAlgorithm keyedHashAlgorithm = (KeyedHashAlgorithm)hashAlgorithm;

            keyedHashAlgorithm.Key = Enumerable.Range(0, keyedHashAlgorithm.Key.Length)
                 .Select(i => salt[i % salt.Length]).ToArray();

            return keyedHashAlgorithm.ComputeHash(data);
        }
        else
        {
            byte[] saltedData = salt.Concat(data).ToArray();

            return hashAlgorithm.ComputeHash(saltedData);
        }
    }
}

public static string Hash(string text, string salt)
{
    return Convert.ToBase64String(Hash(encoding.GetBytes(text), Convert.FromBase64String(salt)));
}

// ランダムなソルトを生成します。
// SqlMembershipProvider では 16 バイトです。
public static string GenerateSalt()
{
    return Convert.ToBase64String(GenerateRandomByteArray(16));
}

// ランダムなバイト配列を生成します。
public static byte[] GenerateRandomByteArray(int size)
{
    byte[] data = new byte[size];

    RandomNumberGenerator.Create().GetBytes(data);

    return data;
}


 
ちなみに、暗号アルゴリズムの名前と具象クラスのマッピングの一覧は、
CryptoConfig クラスの DefaultNameHT プロパティ (プライベートかつ静的) で見ることができます。
 
バージョン情報
.NET Framework 4
 
参照
カテゴリー: .NET Framework. タグ: . 2 Comments »