.NET Framework 1.1用HMACサンプル
会社で作ったシステムのAPIを別システムから呼び出してもらう際、HMAC-SHA256で署名してもらおうと思ったら「うちはレガシーシステムで.NET1.1使ってるからHMACSHA256クラスがないぞゴルァ!」って怒られた。
ただ、.NET1.1にはSHA256自体はあるので、HMACの部分だけC#で作ってサンプルとして提供した。
今のご時世、AmazonさんのAPIを呼び出すのにもHMAC-SHA256が必要なので、レガシーシステムを保守している場合は必要になるかもしれない。
実際にはVS2005を使って書いており.NET1.1ではビルドしてないので、どこかでうっかりビルドエラーがでるかもしれない。でも、本体部分はそのまま.NET1.1で使えるはず。
ちゃんとテストしてないのでご利用は自己責任で。
using System; using System.Security.Cryptography; namespace OreOreHMACSampleForDotNet11 { public class Hmac { public static readonly int BLOCK_SIZE = 64; public static byte[] ProcessHmac(byte[] srcText, byte[] srcKey, HashAlgorithm algorithm) { // HMACMD5とHMACSHA1用のテスト:http://www.ipa.go.jp/security/rfc/RFC2202JA.html // 処理の内容:http://www.ipa.go.jp/security/rfc/RFC2104JA.html if (srcKey.Length > BLOCK_SIZE) { srcKey = algorithm.ComputeHash(srcKey); } byte[] key = Padding(srcKey, (char)0x00, BLOCK_SIZE); byte[] ipad = Padding(new byte[0], (char)0x36, BLOCK_SIZE); byte[] opad = Padding(new byte[0], (char)0x5C, BLOCK_SIZE); return algorithm.ComputeHash(Append(Xor(key, opad), algorithm.ComputeHash(Append(Xor(key, ipad), srcText)))); } private static byte[] Padding(byte[] src, char c, int blockSize) { int length = src.Length; byte[] result = new byte[blockSize]; Array.Copy(src, result, length); for (int i = length; i < blockSize; i++) { result[i] = (byte)c; } return result; } private static byte[] Xor(byte[] x, byte[] y) { int length = Math.Min(x.Length, y.Length); byte[] result = new byte[length]; for (int i = 0; i < length; i++) { result[i] = (byte)(x[i] ^ y[i]); } return result; } private static byte[] Append(byte[] x, byte[] y) { byte[] result = new byte[x.Length + y.Length]; Array.Copy(x, result, x.Length); Array.Copy(y, 0, result, x.Length, y.Length); return result; } } }
おまけとして「http://www.ipa.go.jp/security/rfc/RFC2202JA.html」にあったテストのSHA1部分のサンプルも記載。
ここでSHA1ManagerとしているところをSHA256ManagerとすればAmazon APIの署名でも使えるはず(テストコード自体はSHA1のハッシュ値なので注意)。
public class Program { public static void Main(string[] args) { // HMAC自体のテスト testHmac(); } private static void testHmac() { // 参考:http://www.ipa.go.jp/security/rfc/RFC2202JA.html byte[] text = null; byte[] key = null; byte[] expect = null; text = Encoding.UTF8.GetBytes("Hi There"); key = HexStringToBytes("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); expect = HexStringToBytes("b617318655057264e28bc0b6fb378c8ef146be00"); ExecuteSample(text, key, expect); text = Encoding.UTF8.GetBytes("what do ya want for nothing?"); key = Encoding.UTF8.GetBytes("Jefe"); expect = HexStringToBytes("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"); ExecuteSample(text, key, expect); text = HexStringToBytes("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"); key = HexStringToBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); expect = HexStringToBytes("125d7342b9ac11cd91a39af48aa17b4f63f175d3"); ExecuteSample(text, key, expect); text = HexStringToBytes("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"); key = HexStringToBytes("0102030405060708090a0b0c0d0e0f10111213141516171819"); expect = HexStringToBytes("4c9007f4026250c6bc8414f9bf50c86c2d7235da"); ExecuteSample(text, key, expect); text = Encoding.UTF8.GetBytes("Test With Truncation"); key = HexStringToBytes("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"); expect = HexStringToBytes("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"); ExecuteSample(text, key, expect); text = Encoding.UTF8.GetBytes("Test Using Larger Than Block-Size Key - Hash Key First"); key = HexStringToBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); expect = HexStringToBytes("aa4ae5e15272d00e95705637ce8a3b55ed402112"); ExecuteSample(text, key, expect); text = Encoding.UTF8.GetBytes("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"); key = HexStringToBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); expect = HexStringToBytes("e8e99d0f45237d786d6bbaa7965c7808bbff1a91"); ExecuteSample(text, key, expect); } private static void ExecuteSample(byte[] text, byte[] key, byte[] expect) { byte[] actual = ProcessHmacSha1(text, key); Debug.WriteLine("text [" + BytesToHexString(text) + "]"); Debug.WriteLine("key [" + BytesToHexString(key) + "]"); Debug.WriteLine("sig expect [" + BytesToHexString(expect) + "]"); Debug.WriteLine("sig actual [" + BytesToHexString(actual) + "]"); for (int i = 0; i < expect.Length; i++) { if (actual[i] != expect[i]) { Debug.WriteLine("NG!"); return; } } Debug.WriteLine("OK!"); } private static byte[] ProcessHmacSha1(byte[] srcText, byte[] srcKey) { HashAlgorithm algorithm = new SHA1Managed(); return Hmac.ProcessHmac(srcText, srcKey, algorithm); } private static string BytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.Length; i++) { sb.Append(bytes[i].ToString("x2")); } return sb.ToString(); } private static byte[] HexStringToBytes(string byteString) { int length = byteString.Length; if (length % 2 == 1) { byteString = "0" + byteString; length++; } ArrayList data = new ArrayList(); for (int i = 0; i < length - 1; i = i + 2) { string buf = byteString.Substring(i, 2); data.Add(Convert.ToByte(buf, 16)); } return (byte[])data.ToArray(typeof(byte)); } }
※なお、上記コードの「BytesToHexString」「HexStringToBytes」は「http://wawatete.ddo.jp/exec/program/cs/binary_hexandbyte.html」から拝借したものを.NET1.1用に非ジェネリクス化したもの。