ソーセージ の メモ書き

メモこそ命の恩人だ

HMAC-SHA256 してみる

HMACSHA256 クラス

HMACSHA256 Class (System.Security.Cryptography) | Microsoft Docs

こんな感じ。

public byte[] ComputeHmacSha256(byte[] inputByteList, byte[] key)
{
    using (var hmacSha256 = new HMACSHA256(key))
    {
        return hmacSha256.ComputeHash(inputByteList);
    }
}

上記のメソッドを使用し、メッセージの MAC値 を算出してみる。
エンコードUTF-8 を使用する。

var inputByteList = Encoding.UTF8.GetBytes("メモ");
var key = Encoding.UTF8.GetBytes("1234567");
var outputByteList = ComputeHmacSha256(inputByteList, key);

foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")} ");
// 0x16 0xc5 0x18 0x40 0x63 0xee 0x9e 0xeb 0x87 0x1a 0x60 0x2d 0xb7 0x48 0x6f 0xcd 0x20 0x6d 0xdc 0x9f 0x14 0xf3 0x5a 0xba 0x5e 0x59 0xef 0x5f 0x4c 0x6a 0x51 0x92

Debug.Write($"桁数:{outputByteList.Count()}");
// 桁数:32 (256bit = 32byte)
var inputByteList = Encoding.UTF8.GetBytes("ソーセージのメモ書き");
var key = Encoding.UTF8.GetBytes("1234567");
var outputByteList = ComputeHmacSha256(inputByteList, key);

foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")} ");
// 0xbf 0xba 0x3f 0xc9 0x1d 0xc8 0x56 0xcd 0x0e 0x69 0x32 0x05 0x43 0x62 0xd5 0x14 0x86 0x7e 0x23 0x8d 0xb4 0x50 0x85 0xe5 0x51 0xf5 0x5d 0x31 0x1f 0xb9 0x94 0x69

Debug.Write($"桁数:{outputByteList.Count()}");
// 桁数:32 (256bit = 32byte)

// メッセージを変更した場合、MAC値が変わったことを確認。
var inputByteList = Encoding.UTF8.GetBytes("メモ");
var key = Encoding.UTF8.GetBytes("123");
var outputByteList = ComputeHmacSha256(inputByteList, key);

foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")} ");
// 0x9e 0xf2 0x35 0x33 0x3e 0x48 0x02 0x15 0x48 0x8e 0xdc 0x4f 0x1f 0xab 0xa8 0x7a 0xe0 0x34 0x72 0x06 0x4e 0xcf 0xe6 0x45 0x23 0xa7 0x92 0xa0 0xf2 0xfd 0xa0 0x52

Debug.Write($"桁数:{outputByteList.Count()}");
// 桁数:32 (256bit = 32byte)

// 鍵を変更した場合、MAC値が変わったことを確認。

豆知識

MAC ( Message Authentication Code : メッセージ認証符号 )

メッセージ認証符号 - Wikipedia

  • MAC とは、"メッセージ改竄" や "送信者なりすまし" を防止するための技術。
  • MAC には、共通鍵を使用する。事前に鍵を共有する必要がある。

    f:id:soseiji-memo:20190817182902p:plain

  • MAC値により、受信時に以下を行える。

    項目 内容
    完全性 メッセージの改竄を検知できる。
    真正性 共通鍵の所有者が "送信者" と "受信者" のみの前提ならば、
    送信者本人であると判断できる。
  • MAC値からでは以下が行えない。

    項目 内容
    三者証明 送信者本人であることを第三者が証明できない。
    否認防止性 以下の主張を否認できない。
    送信者 : 私はメッセージを送信してません。
    受信者 : 私はメッセージを受信してません。

    ※ 送信者 or 受信者 どちらも MAC値 を生成できるため、問題が発生する。
    ※ "電子署名" を利用すれば、上記問題は解決できる。

HMAC ( Hash-based MAC )

HMAC - Wikipedia


以上😃

エンコード / デコード してみる

エンコード ( Encode : 符号化 )

エンコード - Wikipedia

文字コードとは?~UTF-8はパソコンの世界共通語~|データ分析用語を解説 - GiXo Ltd.

  • "人間が識別できる情報" を "機械が認識できる情報" に変換すること。
  • 逆方向の変換を "デコード (Decode : 復元) " と呼ぶ。
  • エンコード方法 : ASCII, UTF-8, UTF-16, Shift-JIS etc.

Encoding クラス

Encoding Class (System.Text) | Microsoft Docs

ASCII

//【文字列 : "ABC"】

// エンコード
var encodedByteList = Encoding.ASCII.GetBytes("ABC");
foreach (var encodedByte in encodedByteList) Debug.Write($"0x{encodedByte.ToString("x2")} ");
// 0x41 0x42 0x43 
// ASCII コード表の通りにエンコードされたことを確認。

// デコード
var decodedString = Encoding.ASCII.GetString(encodedByteList);
Debug.Write(decodedString);
// ABC
// 元の文字列にデコードされたことを確認。
//【文字列 : "メモ"】( ※ ASCII コード表に含まれない文字列 )

// エンコード
var encodedByteList = Encoding.ASCII.GetBytes("メモ");
foreach (var encodedByte in encodedByteList) Debug.Write($"0x{encodedByte.ToString("x2")} ");
// 0x3f 0x3f
// 未対応の文字は "3f" でエンコードされたことを確認。

// デコード
var decodedString = Encoding.ASCII.GetString(encodedByteList);
Debug.Write(decodedString);
// ??
// 元の文字列にデコードできないことを確認。
// デコードに "3f" を入力したので "?" が出力された。ASCII コード表の通りである。

UTF-8

//【文字列 : "ABC"】

// エンコード
var encodedByteList = Encoding.UTF8.GetBytes("ABC");
foreach (var encodedByte in encodedByteList) Debug.Write($"0x{encodedByte.ToString("x2")} ");
// 0x41 0x42 0x43 
// コード表の通りにエンコードされたことを確認。

// デコード
var decodedString = Encoding.UTF8.GetString(encodedByteList);
Debug.Write(decodedString);
// ABC
// 元の文字列にデコードされたことを確認。
//【文字列 : "メモ"】

// エンコード
var encodedByteList = Encoding.UTF8.GetBytes("メモ");
foreach (var encodedByte in encodedByteList) Debug.Write($"0x{encodedByte.ToString("x2")} ");
// 0xe3 0x83 0xa1 0xe3 0x83 0xa2 
// コード表の通りにエンコードされたことを確認。

// デコード
var decodedString = Encoding.UTF8.GetString(encodedByteList);
Debug.Write(decodedString);
// メモ
// 元の文字列にデコードされたことを確認。
//【文字列 : "鷗"】

// エンコード
var encodedByteList = Encoding.UTF8.GetBytes("鷗");
foreach (var encodedByte in encodedByteList) Debug.Write($"0x{encodedByte.ToString("x2")} ");
// 0xe9 0xb7 0x97 
// コード表の通りにエンコードされたことを確認。

// デコード
var decodedString = Encoding.UTF8.GetString(encodedByteList);
Debug.Write(decodedString);
// 鷗
// 元の文字列にデコードされたことを確認。

Shift-JIS

//【文字列 : "ABC"】

// エンコード
var encodedByteList = Encoding.GetEncoding("Shift_JIS").GetBytes("ABC");
foreach (var encodedByte in encodedByteList) Debug.Write($"0x{encodedByte.ToString("x2")} ");
// 0x41 0x42 0x43 

// デコード
var decodedString = Encoding.GetEncoding("Shift_JIS").GetString(encodedByteList);
Debug.Write(decodedString);
// ABC
// 元の文字列にデコードされたことを確認。
//【文字列 : "メモ"】

// エンコード
var encodedByteList = Encoding.GetEncoding("Shift_JIS").GetBytes("メモ");
foreach (var encodedByte in encodedByteList) Debug.Write($"0x{encodedByte.ToString("x2")} ");
// 0x83 0x81 0x83 0x82
                    
// デコード
var decodedString = Encoding.GetEncoding("Shift_JIS").GetString(encodedByteList);
Debug.Write(decodedString);
// メモ
// 元の文字列にデコードされたことを確認。
//【文字列 : "鷗"】

// エンコード
var encodedByteList = Encoding.GetEncoding("Shift_JIS").GetBytes("鷗");
foreach (var encodedByte in encodedByteList) Debug.Write($"0x{encodedByte.ToString("x2")} ");
// 0x3f
// 未対応の文字は "3f" でエンコードされたことを確認。
                    
// デコード
var decodedString = Encoding.GetEncoding("Shift_JIS").GetString(encodedByteList);
Debug.Write(decodedString);
// ?
// 元の文字列にデコードできないことを確認。
// デコードに "3f" を入力したので "?" が出力された。ASCII コード表の通りである。


以上😃

ハッシュ関数 SHA-256 してみる

SHA256CryptoServiceProvider クラス

SHA256CryptoServiceProvider Class (System.Security.Cryptography) | Microsoft Docs

  • SHA-256 によるハッシュ値を算出できる。
  • 本クラスを使うには「using System.Security.Cryptography;」が必要。
  • SHA256Managed クラスでもハッシュ値を算出できる。

    SHA256Managed Class (System.Security.Cryptography) | Microsoft Docs

  • SHA256CryptoServiceProvider / SHA256Managed クラスの違いは以下の通り。

    項目 SHA256CryptoServiceProvider SHA256Managed
    暗号化サービスプロバイダー
    ( CSP )
    使用 未使用
    .NET Framework 3.5以上 1.0以上

    ※ 推奨は CSP 使用の "SHA256CryptoServiceProvider" である。

  • その他ハッシュ関数を扱うクラスは以下の通り。使い方は本クラスと同様。

    ハッシュ関数 クラス名
    MD5 MD5CryptoServiceProvider
    SHA1 SHA1CryptoServiceProvider
    SHA384 SHA384CryptoServiceProvider
    SHA512 SHA512CryptoServiceProvider

こんな感じ。

public byte[] ComputeSha256(byte[] inputByteList)
{
    using (var sha256 = new SHA256CryptoServiceProvider())
    {
        return sha256.ComputeHash(inputByteList);
    }
}

文字列 → ハッシュ値

上記のメソッドを使用し、文字列のハッシュ値を計算してみる。
文字列のエンコードUTF-8 を使用する。

var inputByteList = Encoding.UTF8.GetBytes("メモ");
var outputByteList = ComputeSha256(inputByteList);

foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")} ");
// 0x99 0xfa 0x5c 0x96 0xe0 0x7e 0xc4 0x9d 0x52 0xfb 0x7d 0xa8 0x8d 0x13 0xcb 0xe2 0x4a 0x79 0x1f 0xbc 0x26 0x77 0x5b 0xe2 0x54 0x97 0x08 0xd7 0x5c 0x4a 0x3f 0xa1 

Debug.Write($"桁数:{outputByteList.Count()}");
// 桁数:32 (256bit = 32byte)

// 上記ハッシュ値とフリーソフトで計算したハッシュ値が一致したことを確認。
var inputByteList = Encoding.UTF8.GetBytes("ソーセージのメモ書き");
var outputByteList = ComputeSha256(inputByteList);

foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")} ");
// 0xa5 0x28 0x4d 0xca 0x34 0x5f 0xc2 0x5e 0xb8 0x72 0x09 0xcc 0x60 0x94 0x24 0x2b 0x0d 0x63 0xb3 0x7c 0x9c 0x5f 0xf1 0xb5 0x57 0x17 0x45 0x6d 0x39 0x13 0xc8 0x2c 

Debug.Write($"桁数:{outputByteList.Count()}");
// 桁数:32 (256bit = 32byte)

// 上記ハッシュ値とフリーソフトで計算したハッシュ値が一致したことを確認。

ファイル → ハッシュ値

上記のメソッドを使用し、ファイルのハッシュ値を計算してみる。

var filePath = $@"C:\Sample.txt";
if (!File.Exists(filePath)) return;
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    // バイト配列の生成
    var inputByteList = new byte[stream.Length];
    stream.Read(inputByteList, 0, inputByteList.Length);

    // ハッシュ値の算出
    var outputByteList = ComputeSha256(inputByteList);

    foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")} ");
    // 0xae 0x2f 0x8f 0x6f 0x54 0xfc 0xb9 0x31 0x9c 0x34 0x9d 0x18 0xdf 0x3d 0xde 0x82 0x93 0x1d 0x3a 0xc3 0x98 0xf6 0x25 0x10 0x2a 0xe9 0x70 0xd7 0x54 0x3a 0x81 0xae 

    Debug.Write($"桁数:{outputByteList.Count()}");
    // 桁数:32 (256bit = 32byte)

    // 上記ハッシュ値とフリーソフトで計算したハッシュ値が一致したことを確認。
}

豆知識

暗号学的ハッシュ関数 ( Hash : 細かく刻む )

暗号学的ハッシュ関数 - Wikipedia

  • 任意の長さの入力を固定長の出力に変換する関数である。
  • 入力が同じ場合、出力するハッシュ値は変化しない。
  • ハッシュ値は "メッセージダイジェスト" とも呼ばれる。
  • 要求される特性は以下の通り。

    特性 内容
    原像計算困難性 ハッシュ値から元のメッセージを算出することが困難であること。
    第二原像計算困難性
    (弱衝突耐性)
    メッセージAとハッシュ値Aのペアを把握した状態で、
    ハッシュ値Aに一致するメッセージBの探索は困難であること。
    衝突発見困難性
    (強衝突耐性)
    ハッシュ値が衝突するメッセージの探索は困難であること。

    f:id:soseiji-memo:20190816170834p:plain

SHA ( Secure Hash Algorithm )

Secure Hash Algorithm - Wikipedia

  • 暗号学的ハッシュ関数のシリーズ名。
  • SHA-1、SHA-2、SHA-3 に大別される。
  • 現在 SHA-2 以上が推奨。

SHA-2

SHA-2 - Wikipedia


以上😃

RSA 暗号 してみる

RSACryptoServiceProvider クラス

RSACryptoServiceProvider Class (System.Security.Cryptography) | Microsoft Docs

  • RSA 暗号を行える。
  • 本クラスを使うには「using System.Security.Cryptography;」が必要。

鍵ペア生成

こんな感じ。

public (string, string) CreateKeys()
{
    using (var rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize: 2048))
    {
        // 公開鍵・秘密鍵の生成 ( 鍵長 : 2048 bit )
        var publicKey = rsaCryptoServiceProvider.ToXmlString(includePrivateParameters: false);
        var privateKey = rsaCryptoServiceProvider.ToXmlString(includePrivateParameters: true);
        return (publicKey, privateKey);
    }
}

上記のメソッドを使用してみる。

(var publicKey, var privateKey) = CreateKeys();

Debug.WriteLine(publicKey);
// <RSAKeyValue><Modulus>tdDG+8RYhHzp6Bo+oSNix/sSrejKC/tc7uz4TrcTuvfv6zYGxKrF8wac/hsuW9CBc5pUaT2A2D4olB7On6mbKBVb/wwKn59zHVmpBytMAXN7MwelqwFms5QK1vm0SbN8izcnH2mIO+2PD3r/I5hd3UY16O8EgteCvCBQ3FTMK2ztT2McweYnqtZmODHvrzxVcvjGkePkhJ6017qG3xnwy15NtT+Ns7dJ9p/jj8LNo7OQwEJpr7pBTTAdILTkCUG26gqAElCt6+egTkrOuOoKT/yqphuiMlJ1Hp2iwPX61xvgEK6y/br6LQ5J03joIogoijt/i2zUYwXUDnebB61bWQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>

Debug.WriteLine(privateKey);
// <RSAKeyValue><Modulus>tdDG+8RYhHzp6Bo+oSNix/sSrejKC/tc7uz4TrcTuvfv6zYGxKrF8wac/hsuW9CBc5pUaT2A2D4olB7On6mbKBVb/wwKn59zHVmpBytMAXN7MwelqwFms5QK1vm0SbN8izcnH2mIO+2PD3r/I5hd3UY16O8EgteCvCBQ3FTMK2ztT2McweYnqtZmODHvrzxVcvjGkePkhJ6017qG3xnwy15NtT+Ns7dJ9p/jj8LNo7OQwEJpr7pBTTAdILTkCUG26gqAElCt6+egTkrOuOoKT/yqphuiMlJ1Hp2iwPX61xvgEK6y/br6LQ5J03joIogoijt/i2zUYwXUDnebB61bWQ==</Modulus><Exponent>AQAB</Exponent><P>6r7fteem/qKL3Cx6OnxFkmPS7xrCYrQk9fZc8LJBJeGmsU79RIoYy07vlfUr5tqOuWZvTdDErrxBuxm7e/5ntcudG6CsUupCdZ21q+Ch1TJl8/gpDIAex0rNAKBz8jalP8l0TfZErWGdnLWhmrVFzLndgUbtqbZOT/GApSk2PNM=</P><Q>xkcMBHPhBtcueomV+SAxAQDKzpRomeETrRwvl+keoZ5EHIyyPL2d6oVppiKgyOYg7lXFuMFjgHk/APFI8JGWpBGAgAQQ/xIqqMsK+wK4hgu15RecUwQxrvKZQDxandcKFfYLD1XHoJLpcsJJQ8fYxPro2AIsl95mGJXlXblgO6M=</Q><DP>RB+udnDKsrzjT7sMoaps2kXj8gXXAaP70fF0GVnxtBefM3eT4m8dMOswdllXK0CkCMPkazW5zmfNv4mBMkOrP0PezSS8vbdEIDPrg/zFmkRs9/c9mmYYCIf5pEPsQg9/t1o0SWe9mH9qqQ7A3k/a+MW7tYeq8wR4B5UzWzrxhYc=</DP><DQ>tGsdokeXMVRINLRRRTlnndQMtuEQNUU7VD/bhs1XVBThQWE9JkwcD7SKJ+RGjcfpmzVtDuraEaNCEyd7CXxQqMZMOTB6Dr+HFcR8H1+gbEjnoKhVVKzyUJDrpinFAu8TfFxAQBn75j8IMaZcaFLHCqaNjVCdJyv1ltV+ukPeLyU=</DQ><InverseQ>VtB6s7dgWQWj9pGBulzujTOQU0qkY+/x0CfaxLJCmWNIrhxu2OauhiJV3h9r7XnkxocxXOCZ9ukCd56zWuNMk/19+QZXqm67HkblAspuwVwhcFKXnFOsDpnpE7/9L+Ax0H2GczXdc1rJL0vn6QiDXxNBldsHfbIqlCnUWpT/YcQ=</InverseQ><D>C9zXXs3syJgt2sr7kx/v7gRfr6Ekrat04NHtUZV4YBaVPM+FmYda6wRfceTsHWl5SMWlBEjTA0jq9N8dtCUoznC2U+nBN7D7ncqf0KvuhZm3TBkPohyxSr46BtS8AqMHWT37+3yzOtXBI81uoC/sz+co1U3jCXZuPUAOhpGx0JNql5CGhw9VQ8cE7ASvdLoacz1VUxWlbGVIe4kIsfqPfzDGArf3V4SiJapeeuFACfPTXERTVzaUn579wqCE6xCOHa3HKa8XrIRdvVBKURDS52O3yQtPxnKy4pbueChqO0IttCZWfQVcj0Ojdx3JW1WMVdkS6fjhQzzkuydWufTIUQ==</D></RSAKeyValue>

暗号化

こんな感じ。

public byte[] EncryptRsa(byte[] inputByteList, string publicKey)
{
    using (var rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize: 2048))
    {
        // 公開鍵の設定
        rsaCryptoServiceProvider.FromXmlString(publicKey);
        
        // 暗号化 (OAEP使用)
        return rsaCryptoServiceProvider.Encrypt(inputByteList, fOAEP: true);
    }
}

上記のメソッドを使用してみる。公開鍵は "鍵ペア生成" で用意したものを使用する。

// 文字列 "メモ" を暗号化する。
var inputByteList = Encoding.UTF8.GetBytes("メモ");
var publicKey = "<RSAKeyValue><Modulus>tdDG+8RYhHzp6Bo+oSNix/sSrejKC/tc7uz4TrcTuvfv6zYGxKrF8wac/hsuW9CBc5pUaT2A2D4olB7On6mbKBVb/wwKn59zHVmpBytMAXN7MwelqwFms5QK1vm0SbN8izcnH2mIO+2PD3r/I5hd3UY16O8EgteCvCBQ3FTMK2ztT2McweYnqtZmODHvrzxVcvjGkePkhJ6017qG3xnwy15NtT+Ns7dJ9p/jj8LNo7OQwEJpr7pBTTAdILTkCUG26gqAElCt6+egTkrOuOoKT/yqphuiMlJ1Hp2iwPX61xvgEK6y/br6LQ5J03joIogoijt/i2zUYwXUDnebB61bWQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var outputByteList = EncryptRsa(inputByteList, publicKey);

foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")}, ");
// 0x30, 0xa4, 0xee, 0x4c, 0x58, 0xee, 0x18, 0x47, 0x95, 0xc3, 0xa9, 0x02, 0x40, 0x9e, 0xf8, 0xbd, 0xab, 0x3b, 0xe6, 0x40, 0x6d, 0xc6, 0x5b, 0x6d, 0x55, 0xa6, 0x2a, 0x32, 0x86, 0x93, 0xcb, 0x17, 0xda, 0x9f, 0x3a, 0xbe, 0xa8, 0xb4, 0xbc, 0xb9, 0x97, 0x7b, 0x50, 0xd9, 0xff, 0x59, 0x5e, 0xd6, 0x7a, 0xc2, 0x1b, 0x63, 0xd2, 0xac, 0xec, 0xea, 0xdb, 0xff, 0xa8, 0x5f, 0x1c, 0xff, 0x06, 0xc7, 0x1c, 0xa9, 0xa5, 0x03, 0xa9, 0x96, 0xaf, 0xab, 0x98, 0x68, 0x5c, 0xff, 0x62, 0x9f, 0x51, 0xfb, 0x19, 0xad, 0x3c, 0xa7, 0xcc, 0x2b, 0x17, 0x3a, 0xe5, 0x35, 0xec, 0xad, 0xef, 0x32, 0x27, 0x2e, 0x89, 0x36, 0xee, 0xcb, 0x0c, 0x96, 0xc8, 0x11, 0x76, 0xb4, 0x35, 0xbe, 0x58, 0xe6, 0x3d, 0xdc, 0xed, 0xd7, 0x11, 0x99, 0x37, 0x98, 0xbc, 0xaf, 0x95, 0xf5, 0xf7, 0x00, 0xd2, 0x7b, 0x02, 0xd9, 0x33, 0x72, 0xdb, 0xf0, 0xbc, 0x43, 0x35, 0xff, 0xfe, 0xd2, 0x06, 0x9b, 0x67, 0x4e, 0xd0, 0xde, 0xa3, 0x8a, 0xf4, 0x43, 0x3f, 0xd7, 0x47, 0x54, 0x2f, 0xde, 0xcd, 0xca, 0x5e, 0x13, 0xe3, 0x4a, 0xd9, 0x03, 0x57, 0x97, 0x09, 0x31, 0xf5, 0x50, 0x72, 0xb0, 0xe6, 0x6f, 0x39, 0x55, 0x6b, 0xa9, 0x62, 0xa7, 0xa9, 0xf5, 0x5e, 0x0f, 0x57, 0xe5, 0x5f, 0xab, 0xbd, 0xe1, 0x41, 0xdc, 0x77, 0x93, 0x7e, 0xe7, 0x38, 0x77, 0xb2, 0x6e, 0x50, 0x9d, 0xa1, 0x3f, 0x5b, 0x5c, 0xf5, 0x0b, 0xd4, 0x7b, 0xa5, 0x42, 0x6b, 0x83, 0x25, 0xf1, 0xae, 0xde, 0x93, 0xcb, 0xad, 0x4f, 0x62, 0x0b, 0x8d, 0x44, 0x97, 0x0e, 0x26, 0x39, 0xeb, 0x7b, 0x7f, 0x2d, 0x29, 0x65, 0x64, 0xd3, 0x70, 0x35, 0xfc, 0x8d, 0xa4, 0xb7, 0x17, 0xd7, 0x00, 0xd0, 0x7b, 0x77, 0x9e, 0xb4, 0x43, 0xd7, 0x4b, 0xdc, 0x2e, 0xdc, 

Debug.Write($"桁数:{outputByteList.Count()}");
// 桁数:256

復号

こんな感じ。

public byte[] DecryptRsa(byte[] inputByteList, string privateKey)
{
    using (var rsaCryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize: 2048))
    {
        // 秘密鍵の設定
        rsaCryptoServiceProvider.FromXmlString(privateKey);

        // 復号 (OAEP使用)
        return rsaCryptoServiceProvider.Decrypt(inputByteList, fOAEP: true);
    }
}

上記のメソッドを使用してみる。
暗号化した結果を入力する。秘密鍵は "鍵ペア生成" で用意したものを使用する。

// 文字列 "メモ" に復号する。
var inputByteList = new byte[] { 0x30, 0xa4, 0xee, 0x4c, 0x58, 0xee, 0x18, 0x47, 0x95, 0xc3, 0xa9, 0x02, 0x40, 0x9e, 0xf8, 0xbd, 0xab, 0x3b, 0xe6, 0x40, 0x6d, 0xc6, 0x5b, 0x6d, 0x55, 0xa6, 0x2a, 0x32, 0x86, 0x93, 0xcb, 0x17, 0xda, 0x9f, 0x3a, 0xbe, 0xa8, 0xb4, 0xbc, 0xb9, 0x97, 0x7b, 0x50, 0xd9, 0xff, 0x59, 0x5e, 0xd6, 0x7a, 0xc2, 0x1b, 0x63, 0xd2, 0xac, 0xec, 0xea, 0xdb, 0xff, 0xa8, 0x5f, 0x1c, 0xff, 0x06, 0xc7, 0x1c, 0xa9, 0xa5, 0x03, 0xa9, 0x96, 0xaf, 0xab, 0x98, 0x68, 0x5c, 0xff, 0x62, 0x9f, 0x51, 0xfb, 0x19, 0xad, 0x3c, 0xa7, 0xcc, 0x2b, 0x17, 0x3a, 0xe5, 0x35, 0xec, 0xad, 0xef, 0x32, 0x27, 0x2e, 0x89, 0x36, 0xee, 0xcb, 0x0c, 0x96, 0xc8, 0x11, 0x76, 0xb4, 0x35, 0xbe, 0x58, 0xe6, 0x3d, 0xdc, 0xed, 0xd7, 0x11, 0x99, 0x37, 0x98, 0xbc, 0xaf, 0x95, 0xf5, 0xf7, 0x00, 0xd2, 0x7b, 0x02, 0xd9, 0x33, 0x72, 0xdb, 0xf0, 0xbc, 0x43, 0x35, 0xff, 0xfe, 0xd2, 0x06, 0x9b, 0x67, 0x4e, 0xd0, 0xde, 0xa3, 0x8a, 0xf4, 0x43, 0x3f, 0xd7, 0x47, 0x54, 0x2f, 0xde, 0xcd, 0xca, 0x5e, 0x13, 0xe3, 0x4a, 0xd9, 0x03, 0x57, 0x97, 0x09, 0x31, 0xf5, 0x50, 0x72, 0xb0, 0xe6, 0x6f, 0x39, 0x55, 0x6b, 0xa9, 0x62, 0xa7, 0xa9, 0xf5, 0x5e, 0x0f, 0x57, 0xe5, 0x5f, 0xab, 0xbd, 0xe1, 0x41, 0xdc, 0x77, 0x93, 0x7e, 0xe7, 0x38, 0x77, 0xb2, 0x6e, 0x50, 0x9d, 0xa1, 0x3f, 0x5b, 0x5c, 0xf5, 0x0b, 0xd4, 0x7b, 0xa5, 0x42, 0x6b, 0x83, 0x25, 0xf1, 0xae, 0xde, 0x93, 0xcb, 0xad, 0x4f, 0x62, 0x0b, 0x8d, 0x44, 0x97, 0x0e, 0x26, 0x39, 0xeb, 0x7b, 0x7f, 0x2d, 0x29, 0x65, 0x64, 0xd3, 0x70, 0x35, 0xfc, 0x8d, 0xa4, 0xb7, 0x17, 0xd7, 0x00, 0xd0, 0x7b, 0x77, 0x9e, 0xb4, 0x43, 0xd7, 0x4b, 0xdc, 0x2e, 0xdc };
var privateKey = "<RSAKeyValue><Modulus>tdDG+8RYhHzp6Bo+oSNix/sSrejKC/tc7uz4TrcTuvfv6zYGxKrF8wac/hsuW9CBc5pUaT2A2D4olB7On6mbKBVb/wwKn59zHVmpBytMAXN7MwelqwFms5QK1vm0SbN8izcnH2mIO+2PD3r/I5hd3UY16O8EgteCvCBQ3FTMK2ztT2McweYnqtZmODHvrzxVcvjGkePkhJ6017qG3xnwy15NtT+Ns7dJ9p/jj8LNo7OQwEJpr7pBTTAdILTkCUG26gqAElCt6+egTkrOuOoKT/yqphuiMlJ1Hp2iwPX61xvgEK6y/br6LQ5J03joIogoijt/i2zUYwXUDnebB61bWQ==</Modulus><Exponent>AQAB</Exponent><P>6r7fteem/qKL3Cx6OnxFkmPS7xrCYrQk9fZc8LJBJeGmsU79RIoYy07vlfUr5tqOuWZvTdDErrxBuxm7e/5ntcudG6CsUupCdZ21q+Ch1TJl8/gpDIAex0rNAKBz8jalP8l0TfZErWGdnLWhmrVFzLndgUbtqbZOT/GApSk2PNM=</P><Q>xkcMBHPhBtcueomV+SAxAQDKzpRomeETrRwvl+keoZ5EHIyyPL2d6oVppiKgyOYg7lXFuMFjgHk/APFI8JGWpBGAgAQQ/xIqqMsK+wK4hgu15RecUwQxrvKZQDxandcKFfYLD1XHoJLpcsJJQ8fYxPro2AIsl95mGJXlXblgO6M=</Q><DP>RB+udnDKsrzjT7sMoaps2kXj8gXXAaP70fF0GVnxtBefM3eT4m8dMOswdllXK0CkCMPkazW5zmfNv4mBMkOrP0PezSS8vbdEIDPrg/zFmkRs9/c9mmYYCIf5pEPsQg9/t1o0SWe9mH9qqQ7A3k/a+MW7tYeq8wR4B5UzWzrxhYc=</DP><DQ>tGsdokeXMVRINLRRRTlnndQMtuEQNUU7VD/bhs1XVBThQWE9JkwcD7SKJ+RGjcfpmzVtDuraEaNCEyd7CXxQqMZMOTB6Dr+HFcR8H1+gbEjnoKhVVKzyUJDrpinFAu8TfFxAQBn75j8IMaZcaFLHCqaNjVCdJyv1ltV+ukPeLyU=</DQ><InverseQ>VtB6s7dgWQWj9pGBulzujTOQU0qkY+/x0CfaxLJCmWNIrhxu2OauhiJV3h9r7XnkxocxXOCZ9ukCd56zWuNMk/19+QZXqm67HkblAspuwVwhcFKXnFOsDpnpE7/9L+Ax0H2GczXdc1rJL0vn6QiDXxNBldsHfbIqlCnUWpT/YcQ=</InverseQ><D>C9zXXs3syJgt2sr7kx/v7gRfr6Ekrat04NHtUZV4YBaVPM+FmYda6wRfceTsHWl5SMWlBEjTA0jq9N8dtCUoznC2U+nBN7D7ncqf0KvuhZm3TBkPohyxSr46BtS8AqMHWT37+3yzOtXBI81uoC/sz+co1U3jCXZuPUAOhpGx0JNql5CGhw9VQ8cE7ASvdLoacz1VUxWlbGVIe4kIsfqPfzDGArf3V4SiJapeeuFACfPTXERTVzaUn579wqCE6xCOHa3HKa8XrIRdvVBKURDS52O3yQtPxnKy4pbueChqO0IttCZWfQVcj0Ojdx3JW1WMVdkS6fjhQzzkuydWufTIUQ==</D></RSAKeyValue>";
var outputByteList = DecryptRsa(inputByteList, privateKey);
Debug.Write(Encoding.UTF8.GetString(outputByteList));
// メモ

豆知識

公開鍵暗号

公開鍵暗号 - Wikipedia

RSA 暗号

RSA暗号 - Wikipedia

  • Rivest-Shamir-Adleman の略。( 3人の頭文字 )
  • 公開鍵暗号方式である。( 鍵長 : 512 / 1024 / 2048 / 3072 bit )
  • 鍵長の推奨は現状 2048 bit 以上である。
  • 鍵長が長いほど、安全性が高まるが、処理速度は低下する。

OAEP

Optimal Asymmetric Encryption Padding - Wikipedia

  • Optimal Asymmetric Encryption Padding の略。
  • パディング手法の一つ。
  • RSA 暗号と併用した場合、RSA-OAEP と呼ぶ。


以上😃

AES 暗号 してみる

AesManaged クラス

AesManaged Class (System.Security.Cryptography) | Microsoft Docs

暗号化

こんな感じ。

public byte[] EncryptAes(byte[] inputByteList)
{
    using (var aesManaged = new AesManaged())
    {
        // AES初期設定
        InitializeAesManaged(aesManaged);
        
        // 暗号化
        var encryptor = aesManaged.CreateEncryptor();
        return encryptor.TransformFinalBlock(inputByteList, 0, inputByteList.Length);
    }
}
private void InitializeAesManaged(AesManaged aesManaged)
{
    // --------------------
    // 共通鍵の設定
    // --------------------
    // 鍵長
    aesManaged.KeySize = 256;
    // 共通鍵 (※ 256bit = 32byte)
    aesManaged.Key = Encoding.UTF8.GetBytes("12345678901234567890123456789012");

    // --------------------
    // 暗号モードの設定
    // --------------------
    // 暗号モード 
    aesManaged.Mode = CipherMode.CBC;
    // ブロック長
    aesManaged.BlockSize = 128;
    // 初期ベクトル (※ 128bit = 16byte)
    aesManaged.IV = Encoding.UTF8.GetBytes("1234567890123456");
    // パディング
    aesManaged.Padding = PaddingMode.PKCS7;
}

上記のメソッドを使用してみる。
ちなみに、出力桁数はブロック長 (128bit = 16byte) の倍数になる。

// 文字列 "メモ" を暗号化する。
var inputByteList = Encoding.UTF8.GetBytes("メモ");
var outputByteList = EncryptAes(inputByteList);

foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")} ");
// 0x43 0x26 0x5c 0x2f 0xe7 0x0a 0x67 0x29 0x62 0xca 0x5b 0x88 0x1c 0x70 0x92 0x97

Debug.Write($"桁数:{outputByteList.Count()}");
// 桁数:16
// 文字列 "ソーセージのメモ書き" を暗号化する。
var inputByteList = Encoding.UTF8.GetBytes("ソーセージのメモ書き");
var outputByteList = EncryptAes(inputByteList);

foreach (var outputByte in outputByteList) Debug.Write($"0x{outputByte.ToString("x2")} ");
// 0x10 0xf2 0x49 0x5c 0xad 0x1b 0x45 0xbf 0x55 0xce 0x4c 0x9a 0x46 0xf4 0xc7 0x01 0x6a 0xf6 0xce 0xe3 0xb5 0xf6 0xf5 0xd3 0x25 0x3a 0x2e 0x18 0x61 0x95 0xd6 0x24

Debug.Write($"桁数:{outputByteList.Count()}");
// 桁数:32

復号

こんな感じ。

public byte[] DecryptAes(byte[] byteList)
{
    using (var aesManaged = new AesManaged())
    {
        // AES初期設定 (※ 暗号化と同じメソッド。設定を合わせること。)
        InitializeAesManaged(aesManaged);

        // 復号
        var decryptor = aesManaged.CreateDecryptor();
        return decryptor.TransformFinalBlock(byteList, 0, byteList.Length);
    }
}

上記のメソッドを使用してみる。暗号化の結果を入力してみる。

// 文字列 "メモ" に復号する。
var inputByteList = new byte[] { 0x43, 0x26, 0x5c, 0x2f, 0xe7, 0x0a, 0x67, 0x29, 0x62, 0xca, 0x5b, 0x88, 0x1c, 0x70, 0x92, 0x97 };
var outputByteList = DecryptAes(inputByteList);
Debug.Write(Encoding.UTF8.GetString(outputByteList));
// メモ
// 文字列 "ソーセージのメモ書き" に復号する。
var inputByteList = new byte[] { 0x10, 0xf2, 0x49, 0x5c, 0xad, 0x1b, 0x45, 0xbf, 0x55, 0xce, 0x4c, 0x9a, 0x46, 0xf4, 0xc7, 0x01, 0x6a, 0xf6, 0xce, 0xe3, 0xb5, 0xf6, 0xf5, 0xd3, 0x25, 0x3a, 0x2e, 0x18, 0x61, 0x95, 0xd6, 0x24 };
var outputByteList = DecryptAes(inputByteList);
Debug.Write(Encoding.UTF8.GetString(outputByteList));
// ソーセージのメモ書き

豆知識

共通鍵暗号

共通鍵暗号 - Wikipedia

  • 暗号化と復号に同一の鍵を用いる暗号方式。
  • 鍵は第三者に知られないよう秘密にする必要がある。
  • 公開鍵暗号と比較した場合

    項目 内容
    利点 処理が高速である。
    欠点 鍵の受け渡しに注意を要する。
  • 暗号方式は以下の2つに大別される。

    名称 内容
    ストリーム暗号 1bit or 1byte 毎に順次暗号化する。 RC4, MUGI
    ブロック暗号 平文メッセージをブロック長毎に分割し、暗号化する。 AES, DES, Camellia
  • ブロック暗号のモードは以下の通り。( 一部紹介 )

    名称 内容
    ECB ( Electronic Codebook ) 平文ブロックごとに暗号化する。(※非推奨)
    CBC ( Cipher Block Chaining ) 平文ブロックを組合わせて暗号化する。後述参照。
  • CBC の手順

    手順 内容
    初期ベクトルと平文ブロックとの EXOR を行う。
    上記結果と次の平文ブロックとの EXOR を行う。
    手順②を繰り返す。
    最終ブロックに余りが発生した場合、パディングする。
    PKCS#5, PKCS#7 etc.

AES 暗号

Advanced Encryption Standard - Wikipedia

  • Advanced Encryption Standard の略。
  • 共通鍵暗号方式である。( 鍵長 : 128 / 192 / 256 bit )
  • 鍵長の推奨は現状 128 bit 以上である。
  • 鍵長が長いほど、安全性が高まるが、処理速度は低下する。
  • ブロック暗号である。 ( ブロック長 : 128 bit 固定 )
  • ラウンド数は鍵長毎に異なる。

    鍵長 ラウンド数
    128 bit 10回
    192 bit 12回
    256 bit 14回
  • 鍵長により AES の表記が異なる。

    鍵長 表記
    128 bit AES-128
    192 bit AES-192
    256 bit AES-256
  • 上記に加えて、ブロックの暗号モードも表記に付与される。


以上😃

Base64 してみる

Base64

Base64 - Wikipedia

  • "バイナリデータ" を "テキストデータ" に変換する手法の一つ。
  • 生体情報 etc. のバイナリデータをテキストデータとして出力できる。
  • テキストデータには、64種類の印字可能な英数字と記号を用いる。
  • 変換後は、データ量が 4/3(約133%)増加する。
  • 読み方は "ベースろくじゅうよん"。

エンコード方法

試しに "メモ" という言葉に対して Base64 を適用してみる。
( 元データがバイナリ形式の場合、手順2から開始する。)

  • 1.バイナリ形式に変換する。

    項目 内容
    変換前 メモ
    変換中 E383A1 E383A2 ( エンコード : UTF-8 )
    変換後 1110 0011 1000 0011 1010 0001 1110 0011 1000 0011 1010 0010
  • 2.6bit 毎に分割する。( 6bit でない箇所は "0" でパディング )

    項目 内容
    変換前 1110 0011 1000 0011 1010 0001 1110 0011 1000 0011 1010 0010
    変換後 111000 111000 001110 100001 111000 111000 001110 100010
  • 3.変換表を用いて4文字毎に分割する。( 4文字でない箇所は "=" でパディング )

    項目 内容
    変換前 111000 111000 001110 100001 111000 111000 001110 100010
    変換後 44Oh 44Oi
  • 4.各文字を連結させれば、エンコード完了。

    項目 内容
    変換前 44Oh 44Oi
    変換後 44Oh44Oi

C# での使用

ToBase64String メソッド

Convert.ToBase64String Method (System) | Microsoft Docs

  • バイト配列を Base64 形式の文字列にエンコードする。
  • Convert クラスの静的メソッド。

こんな感じ。

var byteList = Encoding.UTF8.GetBytes("メモ");
var convertedString = Convert.ToBase64String(byteList);
Debug.Write(convertedString);
// 44Oh44Oi

// 予想通りの結果で出力されたことを確認。

FromBase64String メソッド

Convert.FromBase64String(String) Method (System) | Microsoft Docs

  • Base64 形式の文字列をバイト配列にデコードする。
  • Convert クラスの静的メソッド。

こんな感じ。

var byteList = Convert.FromBase64String("44Oh44Oi");
foreach (var byteData in byteList) Debug.Write($"0x{byteData.ToString("x2")} ");
// 0xe3 0x83 0xa1 0xe3 0x83 0xa2

var convertedString = Encoding.UTF8.GetString(byteList);
Debug.Write(convertedString);
// メモ

// 予想通りの結果で出力されたことを確認。
// Base64 に対応していないデータを入力
var byteList = Convert.FromBase64String("!!!");
// System.FormatException: 入力は有効な Base-64 文字列ではありません。


以上😃

リスクマネジメント 調べてみる

リスク ( Risk : 危険度 )

リスク - Wikipedia

  • 資産 ( 人、物、金、情報 ) に悪影響を与える可能性である。
    資産の "脆弱性" が "脅威" に狙われる。

    f:id:soseiji-memo:20190815070102p:plain

  • リスクの大きさは "影響度" と "頻度" の組合わせにより決まる。
    ( リスクの大きさ = リスクレベル )

  • リスクの分類は以下の通り。

    名称 内容
    純粋リスク 損失のみを発生させるリスク。 災害、事故
    投機的リスク 損失 or 利益 が生じるリスク。 環境、経済、政治制度の変化

リスクマネジメント ( Management : 管理 )

リスクマネジメント - Wikipedia

  • リスクを管理し、その影響を "回避" or "低減" する対策を考えること。
  • リスクマネジメントは "リスクアセスメント" と "リスク対応" から成る。

    f:id:soseiji-memo:20190815071021p:plain

リスクアセスメント ( Assessment : 査定 )

リスクアセスメント - Wikipedia

  • "リスク特定"、"リスク分析"、"リスク評価" のプロセス。
  • 内容は以下の通り。

    名称 内容
    リスク特定 資産に対するリスクを把握する。
    リスク分析 リスクレベルを決定する。
    リスク評価 リスクを許容可能か判定する。

リスク対応

リスクコントロール ( Control : 制御 )

  • リスクが具現化する前に、予防策をとってコントロールする。
  • 対応方法は以下の通り。

    名称 内容
    リスク回避 リスク自体をなくす。 事業撤退
    リスク低減 リスクの影響度や頻度を低減する。 システム導入、教育

リスクファイナンス ( Finance : 財務 )

  • リスクが具体化した後に備えて、資金面の対応策を図る。
  • 対応方法は以下の通り。

    名称 内容
    リスク移転 リスクを他者に移転する。 保険加入
    リスク保有 リスクを自社で負担する。 積立金


以上😃

情報セキュリティ 調べてみる

情報

経営資源 - Wikipedia

  • 情報とは、経営資源の一つ。( 人、物、金、情報 )
  • 例 : 顧客情報、技術情報、販売情報 etc.

セキュリティ ( Security : 安全 )

セキュリティ - Wikipedia

  • セキュリティは、悪意を持った人的な脅威に対する安全。
    • 脅威の例 : 侵入、盗難、改竄 etc.
  • 対して、セーフティ ( Safety : 安全 ) は偶発的な脅威に対する安全。
    • 脅威の例 : 地震、火事、水害 etc.

情報セキュリティ

情報セキュリティ - Wikipedia

f:id:soseiji-memo:20190811094447p:plain

通常の定義 ( JIS Q 27002 )

  • 情報の "機密性"、"完全性"、"可用性" を維持すること。
  • CIA : 下記要素の頭字語。
    • 機密性 ( Confidentiality ) : 情報は権限のある人のみ使用できる。
    • 完全性 ( Integrity ) : 情報は改竄されない。
    • 可用性 ( Availability ) : 情報はいつでも使用できる。

拡張した定義

  • CIA に加え、"真正性"、"責任追跡性"、"否認防止性"、"信頼性"を維持すること。
    • 真正性 : 情報のなりすましがない。
    • 責任追跡性 : 情報の利用事実を追跡できる。
    • 否認防止性 : 情報の利用事実の否認を防止できる。
    • 信頼性 : 情報は意図した通り使用できる。
  • CIA は情報セキュリティの必須特性であり、上記の4要素は任意特性である。


以上😃

ID / コード / ナンバー の違い 調べてみる

ID ( Identification : 識別 )

IDとは - IT用語辞典 e-Words

  • 対象を一意に識別するための符号。
  • 例:ユーザーID etc.

コード ( Code : 符号、規約 )

コードとは - IT用語辞典 e-Words

  • 規約に基づいて、対象を一意に識別するための符号。
  • 例:文字コードソースコード銀行コード etc.
  • 規約がある点が、ID とは異なる箇所。
    • *桁目は、○な意味。
    • *~*は、○な意味。etc.

ナンバー ( Number : 番号 )

ナンバーとは - コトバンク

  • 対象を一意に識別するための符号。
  • 対象の順番を表す用途にも使用する。
  • 例:マイナンバー、電話番号、郵便番号、暗証番号、ページ番号 etc.

補足

符号

符号(ふごう)とは - コトバンク

  • 情報の表現に用いる際の基本となる記号。

記号

記号(きごう)とは - コトバンク

  • ある特定の機能や内容を、わかりやすく表現するための図形や符号。


以上😃

SQL Server でバイナリデータの 登録 / 表示 してみる

以下のテーブルで試してみる。

SampleTable

項目名 データ型
SampleData varbinary(max)

バイナリデータの登録

  • 2通りの登録方法がある。
    • 1. 数値のまま登録する。4バイトの数値データを登録できる。
    • 2. "0x" を付与して登録する。16進数形式のデータを登録できる。

1つ目の方法を試してみる。

INSERT INTO SampleTable (SampleData) VALUES (0);
-- 0x00000000 を登録できた。

INSERT INTO SampleTable (SampleData) VALUES (1);
-- 0x00000001 を登録できた。

INSERT INTO SampleTable (SampleData) VALUES (10);
-- 0x0000000a を登録できた。

2つ目の方法を試してみる。

INSERT INTO SampleTable (SampleData) VALUES (0x0);
-- 0x00 を登録できた。

INSERT INTO SampleTable (SampleData) VALUES (0x1);
-- 0x01 を登録できた。

INSERT INTO SampleTable (SampleData) VALUES (0x10);
-- 0x10 を登録できた。

INSERT INTO SampleTable (SampleData) VALUES (0xAB);
-- 0xab を登録できた。

バイナリデータの表示

  • master.dbo.fn_varbintohexstr メソッドを使用する。
  • varbinary のデータを文字列 (16進数形式) に変換できる。

こんな感じ。

SELECT master.dbo.fn_varbintohexstr(SampleData) FROM SampleTable
-- 上記登録データを表示できた。

補足

バイナリデータ (Binary : 2進数)

バイナリ - Wikipedia

  • バイナリデータは "機械" が読んで理解できる形式。テキストデータは "人" が読んで理解できる形式。
  • 用途:画像、音声、圧縮ファイル etc.

使用 DDL

USE SampleDb

/* テーブル削除 */
IF ObJECt_ID('[SampleTable]') IS NOT NULL DROP TABLE [SampleTable];

/* テーブル追加 */
CREATE TABLE [SampleTable]
(
    [SampleData] varbinary(max)
);


以上😃

ItemsControl してみる

ItemsControl クラス

ItemsControl Class (System.Windows.Controls) | Microsoft Docs

  • コレクションの要素を並べて表示できる。
  • 使用方法
    • ItemsPanel プロパティにパネルを指定する。
    • ItemTemplate プロパティに要素の表示内容を指定する。

こんな感じ。

public class SampleClass
{
    public int Id { get; set; } = 0;
    public string Name { get; set; } = string.Empty;
}
public ObservableCollection<SampleClass> SampleList { get; set; } = new ObservableCollection<SampleClass>()
{
    new SampleClass() { Id = 1, Name= "suzuki" },
    new SampleClass() { Id = 2, Name= "yamada" },
};
<ItemsControl ItemsSource="{Binding SampleList}">

    <!-- パネルの設定 -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <!-- 表示内容の設定 -->
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <WrapPanel Margin="10">
                <TextBlock Text="{Binding Id}" />
                <TextBlock Text=":" />
                <TextBlock Text="{Binding Name}" />
            </WrapPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
                
</ItemsControl>


以上😃

Chart で グラフ表示 してみる

Chart クラス

Chart Class (System.Windows.Forms.DataVisualization.Charting) | Microsoft Docs

こんな感じ。

<!-- ユーザーコントロール(Chartコントロールをラップ) -->
<UserControl x:Class="WpfApp1.ChartUserControl"
             ...
             xmlns:visualization="clr-namespace:System.Windows.Forms.DataVisualization.Charting;assembly=System.Windows.Forms.DataVisualization">

    <WindowsFormsHost>
        <!-- Chartコントロール -->
        <visualization:Chart x:Name="Chart1" GetToolTipText="Chart1_GetToolTipText" />
    </WindowsFormsHost>
</UserControl>
// コードビハインド(コンストラクタ)
public ChartUserControl()
{
    InitializeComponent();

    //-------------------
    // 表示設定
    //-------------------
    // 初期化
    this.Chart1.ChartAreas.Clear();
    this.Chart1.Series.Clear();
    this.Chart1.Titles.Clear();
    this.Chart1.Legends.Clear();

    // フォント設定
    var chartFont = new System.Drawing.Font("Meiryo UI", 14.0F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);

    // 表示エリア設定
    var chartArea = new ChartArea
    {
        BackColor = System.Drawing.Color.LightGray,
        BackGradientStyle = GradientStyle.TopBottom,
    };
    chartArea.AxisX.Title = "時間";   // X軸タイトル
    chartArea.AxisY.Title = "発生回数"; // Y軸タイトル
    chartArea.AxisY.TextOrientation = TextOrientation.Stacked;  // Y軸タイトルテキスト配置:スタック
    chartArea.AxisX.TitleFont = chartFont;  // X軸フォント
    chartArea.AxisY.TitleFont = chartFont;  // Y軸タイトル
    chartArea.AxisX.Minimum = 0;   // X軸最小値
    chartArea.AxisX.Maximum = 23;   // X軸最大値
    chartArea.AxisX.Interval = 1.0; // X軸間隔
    chartArea.AxisX.MajorGrid.Enabled = false;  // X軸罫線:なし
    chartArea.AxisY.MajorGrid.LineColor = System.Drawing.Color.Gray;    // Y軸罫線色
    chartArea.AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dot;   // Y軸罫線スタイル:ドット
    chartArea.Area3DStyle.Enable3D = false; // 3D効果:なし
    this.Chart1.ChartAreas.Add(chartArea);

    // 座標データ設定
    var series = new Series
    {
        Name = "ログ1",    // 凡例タイトル
        ChartType = SeriesChartType.Line,    // グラフタイプ:線グラフ
        MarkerStyle = MarkerStyle.Circle,    // マーカー:丸
        Color = System.Drawing.Color.Blue,  // グラフ色:青
        Font = chartFont,
    };
    this.Chart1.Series.Add(series);

    // タイトル設定(不要ならコメントアウト)
    var title = new Title("ログデータ");
    this.Chart1.Titles.Add(title);

    // 凡例設定(不要ならコメントアウト)
    var legend = new Legend();
    this.Chart1.Legends.Add(legend);
}
// コードビハインド(座標データをバインディング可能にする。)
public List<Point> PointList
{
    get { return GetValue(PointListProperty) as List<Point>; }
    set { SetValue(PointListProperty, value); }
}
public static readonly DependencyProperty PointListProperty = DependencyProperty.Register(
        "PointList",
        typeof(List<Point>),
        typeof(ChartUserControl),
        new PropertyMetadata(null, OnPointListChanged)
    );
private static void OnPointListChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    if (!(sender is ChartUserControl control)) return;
    if (!(e.NewValue is List<Point> pointList)) return;
    if (pointList.Count == 0) return;

    // 座標データ設定
    var tempSeries = control.Chart1.Series.First();
    tempSeries.Points.Clear();
    foreach (var point in pointList) tempSeries.Points.AddXY(point.X, point.Y);
}
// コードビハインド(ツールチップ表示用)
private void Chart1_GetToolTipText(object sender, System.Windows.Forms.DataVisualization.Charting.ToolTipEventArgs e)
{
    if (e.HitTestResult.ChartElementType != ChartElementType.DataPoint) return;
    var pointIndex = e.HitTestResult.PointIndex;
    var point = e.HitTestResult.Series.Points[pointIndex];
    e.Text = $"{point.XValue}時台 -> {point.YValues[0]}回";
}


上記のユーザーコントロールを使用し、ログデータをグラフ表示してみる。
ViewModel で以下を実行する。

// ユーザーコントロールにバインディングするプロパティ
public List<Point> PointList
{
    get { return pointList; }
    set { SetProperty(ref pointList, value); }
}
private List<Point> pointList = new List<Point>();
// サンプルデータ
var logDataList = new[]
{
    new { Time = new DateTime(2019,1,1,8,0,0), Message = "データ1"},
    new { Time = new DateTime(2019,1,1,8,10,0), Message = "データ2"},
    new { Time = new DateTime(2019,1,1,8,20,0), Message = "データ3"},
    new { Time = new DateTime(2019,1,1,9,0,0), Message = "データ4"},
    new { Time = new DateTime(2019,1,1,9,30,0), Message = "データ5"},
    new { Time = new DateTime(2019,1,1,10,0,0), Message = "データ6"},
};

//  時間毎のログ発生回数を集計 (0時~23時)
var timeCountList = Enumerable.Repeat(0, 24).ToList();
foreach (var logData in logDataList) timeCountList[logData.Time.Hour]++;

// 座標データ設定
var tempPointList = new List<Point>();
foreach (var (count, hour) in timeCountList.Select((count, hour) => (count, hour)))
{
    tempPointList.Add(new Point(hour, count));
}
                    
// グラフ反映
PointList.Clear();
PointList = tempPointList;

View はこんな感じ。

<!-- ViewModel の処理でグラフ表示できたことを確認。 -->
<local:ChartUserControl PointList="{Binding PointList}" />

ChartType 列挙型

SeriesChartType Enum (System.Web.UI.DataVisualization.Charting) | Microsoft Docs

一部をメモする。詳細は上記サイトを参照。

列挙値 内容
Line 折れ線グラフ
Column 縦棒グラフ
Bar 横棒グラフ
Area 面グラフ


以上😃

WindowsFormsHost してみる

WindowsFormsHost クラス

WindowsFormsHost Class (System.Windows.Forms.Integration) | Microsoft Docs

  • WPFXAML に、Form のコントロールを配置するための要素。
  • Form にしかないコントロールを扱いたい場合に使用する。
  • 使用時は、プロジェクトの参照に以下を追加する。
    • WindowsFormsIntegration ( WindowsFormsHost の使用に必要 )
    • System.Windows.Forms ( Form コントロールの使用に必要 )

こんな感じ。

<Window x:Class="WpfApp1.MainWindow"
        ...
        xmlns:form="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms">

    <StackPanel>
        <WindowsFormsHost>
             <!-- MaskedTextBox コントロールが表示されたことを確認 -->
            <form:MaskedTextBox x:Name="MaskedTextBox1" Mask="0000/00/00"/>
        </WindowsFormsHost>
    </StackPanel>
</Window>

WindowsFormsHost 使用時の注意事項

Form にしか存在しないコントロール ( WPF 比較 )

Windows フォーム コントロールおよび同等の WPF コントロール | Microsoft Docs

  • 以下例
    • Chart
    • ColorDialog
    • FolderBrowserDialog
    • FontDialog
    • MaskedTextBox
    • NotifyIcon
    • PageSetupDialog


以上😃

Ping してみる

Ping コマンド

ping - Wikipedia

  • ネットワークの疎通確認 etc. を行うためのコマンド。
  • ICMP (Internet Control Message Protocol) を内部で使用する。
  • 仕組み
    • 送信側:ICMP echo request を送信する。
    • 受信側:ICMP echo reply を返す。
    • 送信側:応答時間を表示する。

Ping クラス

Ping Class (System.Net.NetworkInformation) | Microsoft Docs

IPStatus Enum (System.Net.NetworkInformation) | Microsoft Docs

  • Ping コマンドを送信するクラス。
  • Ping クラスの Send メソッドに IP アドレスを指定して実行する。
  • 応答結果は、IPStatus 列挙値に格納される。
  • Ping クラスを使用するには「using System.Net.NetworkInformation;」が必要。

こんな感じ。

using (var ping = new Ping())
{
    var ipAddress = "192.168.11.1";
    foreach (var count in Enumerable.Range(1,4))
    {
        var pingReply = ping.Send(ipAddress);
        switch (pingReply.Status)
        {
            case IPStatus.Success:
                Debug.WriteLine($"送信成功 <アドレス:{pingReply.Address}, バイト数:{pingReply.Buffer.Length}, 時間:{pingReply.RoundtripTime}, TTL:{pingReply.Options.Ttl}>");
                break;
            default:
                Debug.WriteLine($"送信失敗 <ステータス:{pingReply.Status}>");
                break;

        }
    }
}

// 送信成功 < アドレス:192.168.11.1, バイト数: 32, 時間: 1, TTL: 128 >
// 送信成功 < アドレス:192.168.11.1, バイト数: 32, 時間: 0, TTL: 128 >
// 送信成功 < アドレス:192.168.11.1, バイト数: 32, 時間: 0, TTL: 128 >
// 送信成功 < アドレス:192.168.11.1, バイト数: 32, 時間: 0, TTL: 128 >


以上😃

CsvHelper してみる

CsvHelper

https://github.com/JoshClose/CsvHelper

  • CSV ファイルの 読込 / 書込 を行うためのライブラリ。
  • 作成者 : Josh Close
  • ライセンス : Apache 2.0 or MS-PL
  • ダウンロード数:12,300,000 件 ( 2019/7 時点 )
  • 使用方法
    • Nuget より "CsvHelper" をインストールする。
    • 使用時は「using CsvHelper;」が必要。

書込 / 読込処理

準備

  • 以下の2つのクラスを用意する。
  • マッピング方法
    • ClassMap クラスを継承する。
    • Index メソッドでカラムの順序を定義する。( 0 スタート )

こんな感じ。

// データ格納用クラス
public class SampleData
{
    public string StringField { get; set; }
    public int IntField { get; set; }
    public byte ByteField { get; set; }
    public bool BoolField { get; set; }
    public DateTime DateTimeField { get; set; }
}
// マッピング用クラス
public class SampleDataMapper : ClassMap<SampleData>
{
    private SampleClassMapper()
    {
        Map(x => x.StringField).Index(0);
        Map(x => x.IntField).Index(1);
        Map(x => x.ByteField).Index(2);
        Map(x => x.BoolField).Index(3);
        Map(x => x.DateTimeField).Index(4);
    }
}

書込処理

  • CsvWriter クラスで書き込みを行う。
  • 区切り文字を含む場合、自動で引用符が付与される。
  • Configuration プロパティを通して書込設定を行う。

    プロパティ 内容 備考
    HasHeaderRecord ヘッダーの有無

こんな感じ。

// 書込用データ
var sampleDataList = new List<SampleData>()
{
    new SampleData(){ StringField = "あああ", IntField = 1, ByteField = 1, BoolField = true, DateTimeField = new DateTime(2019, 1, 1, 1, 0, 0) },
    new SampleData(){ StringField = "い,い,い", IntField = 2, ByteField = 2, BoolField = false, DateTimeField = new DateTime(2019, 1, 2, 2, 0, 0) },
    new SampleData(){ StringField = " ううう ", IntField = 3, ByteField = 3, BoolField = true, DateTimeField = new DateTime(2019, 1, 3, 3, 0, 0) },
};

var filePath = @"C:\Sample\Sample.csv";
using (var streamWriter = new StreamWriter(filePath, append:false, encoding: Encoding.UTF8))
using (var csvWriter = new CsvWriter(streamWriter))
{
    // 書込設定
    var configuration = csvWriter.Configuration;
    configuration.HasHeaderRecord = true;  // ヘッダーあり
    csvWriter.Configuration.RegisterClassMap<SampleDataMapper>();

    // 処理開始
    csvWriter.WriteRecords(sampleDataList);
}

出力ファイルはこんな感じ。

StringField,IntField,ByteField,BoolField,DateTimeField
あああ,1,1,True,2019/01/01 1:00:00
"い,い,い",2,2,False,2019/01/02 2:00:00
" ううう ",3,3,True,2019/01/03 3:00:00

読込処理

  • CsvReader クラスで読み込みを行う。
  • フィールドのデータを引用符 (") で囲むと、区切り文字を文字列として扱える。
  • フィールドが少ない場合、例外が発生する。多い場合は無視されるだけ。
  • キャストに失敗する場合、例外が発生する。( 例外発生の仕方は Convert クラスを使用してる風 )

    int 型へのキャスト してみる - ソーセージ の メモ書き

    bool 型へのキャスト してみる - ソーセージ の メモ書き

  • Configuration プロパティを通して読込設定を行う。

    プロパティ 内容 備考
    HasHeaderRecord ヘッダーの有無 既定 : true
    TrimOptions トリムの有無 TrimOptions.Trim にした場合、前後の半角スペースが削除される。
    複数連続の半角スペースも削除できる。
    全角スペースは削除されない。
    IgnoreBlankLines 空白レコードの無視の有無 既定 : true

こんな感じ。

var filePath = @"C:\Sample\Sample.csv";
if (!File.Exists(filePath)) return;
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
using (var csvReader = new CsvReader(streamReader))
{
    // 読込設定
    var configuration = csvReader.Configuration;
    configuration.HasHeaderRecord = false;
    configuration.IgnoreBlankLines = true;
    configuration.TrimOptions = TrimOptions.Trim;
    configuration.RegisterClassMap<SampleClassMapper>();

    // 処理開始 (遅延読込のため、この時点では読み込まれない)
    var recordList = csvReader.GetRecords<SampleClass>();

    var index = 0;
    try
    {
        foreach (var record in recordList)
        {
            Debug.WriteLine($"{index} : {record.StringField}_{record.IntField}_{record.ByteField}_{record.BoolField}_{record.DateTimeField}");
            index++;
        }
    }
    catch (CsvHelper.MissingFieldException ex)
    {
        // 例外要因 : フィールド数不足
        Debug.WriteLine($"error record index:{index}");
        Debug.WriteLine($"error field index:{ex.ReadingContext.CurrentIndex}");
        Debug.WriteLine($"{ex.ToString()}");
    }
    catch (CsvHelper.TypeConversion.TypeConverterException ex)
    {
        // 例外要因 : キャストエラー(int,bool)
        Debug.WriteLine($"error record index:{index}");
        Debug.WriteLine($"error field index:{ex.ReadingContext.CurrentIndex}");
        Debug.WriteLine($"error text:{ex.Text}");
        Debug.WriteLine($"{ex.ToString()}");
    }
    catch (CsvHelper.ReaderException ex)
    {
        // 例外要因 : キャストエラー(DateTime)
        Debug.WriteLine($"error record index:{index}");
        Debug.WriteLine($"error field index:{ex.ReadingContext.CurrentIndex}");
        Debug.WriteLine($"{ex.ToString()}");
    }
    catch (CsvHelper.BadDataException ex)
    {
        // 例外要因 : 区切り文字エラー
        Debug.WriteLine($"error record index:{index}");
        Debug.WriteLine($"error field index:{ex.ReadingContext.CurrentIndex}");
        Debug.WriteLine($"{ex.ToString()}");
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"error record index:{index}");
        Debug.WriteLine($"{ex.ToString()}");
    }
}


以上😃