HDDのシリアル番号を利用した簡易ライセンスチェッカ(C#)

ソフトウェア起動時のライセンスチェックの一例として紹介します。

# 概要
ハードディスクのシリアル番号を取得し、ライセンスファイルと一致しているか確認することで、ソフトウェアを別のPCにコピーして使用することを抑制します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204268/570dcfea-39ee-f948-a70a-8a342342dbf5.png)

# 注意事項
- .NetFramework環境です
- 「簡単に突破できる」とか玄人的な話は無しとします。
- 単純な一致チェックではなくライセンスファイルをゴニョゴニョしてから一致比較すれば、突破難易度は上がると思います

# HDDのシリアル番号とは？
コマンドプロンプトで　`vol`と打つと取得できるアイツのことです。
C#ではこんな感じで取得できます。
```cs
//Cドライブボリュームのシリアル番号のHEX文字を取得
System.Management.ManagementObject _mo = new System.Management.ManagementObject("Win32_LogicalDisk=\"C:\"");
this.VolumeSerial = (string)_mo.Properties["VolumeSerialNumber"].Value;
```
# ライセンスファイル
ライセンスファイルは、バイナリエディタで作成したファイルとします。
テキストエディタで作成した場合エンコード指定の場合分け等が面倒なのでバイナリとしました。


# コード
ライセンスチェッカークラスです。　ライセンスファイルへのパスを引数としてインスタンスしてもらい、HDDのシリアルと一致しているとフラグが立つだけの単純なものです。

```cs
using System;
using System.Reflection;
using System.IO;
using LicenseCheck.Messages;
using System.Text;

namespace LicenceCheck {

    /// <summary>
    /// ライセンスチェッカー
    /// ライセンスファイルをとハードディスクのシリアルの一致判定
    /// </summary>
    public class LicenceChecker {

        /// <summary>
        /// モジュールのバージョン番号を返す
        /// </summary>
        /// <returns></returns>
        public string Version { get; } = "";

        /// <summary>
        /// ボリュームのシリアル番号文字列
        /// </summary>        
        public string VolumeSerial { get; } = "";

        /// <summary>
        /// ライセンスByteアレイ
        /// </summary>        
        private byte[] _licenseData = null;

        /// <summary>
        /// ライセンスHEX文字列
        /// </summary>
        public string LicenseHexStr { get; } = "";
        /// <summary>
        /// ライセンスファイルが健全ですよフラグ
        /// </summary>
        public bool IsFine { get; } = false;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="LicenseFitePath">ライセンスファイルへのパス</param>
        /// <exception cref="Exception"></exception>
        public LicenceChecker(string LicenseFitePath) {
            //アセンブリ情報からバージョン番号を取得
            Assembly _asmb = Assembly.GetExecutingAssembly();
            this.Version = _asmb.GetName().Version.ToString();
            
            //Cドライブボリュームのシリアル番号のHEX文字を取得
            System.Management.ManagementObject _mo = new System.Management.ManagementObject("Win32_LogicalDisk=\"C:\"");
            this.VolumeSerial = (string)_mo.Properties["VolumeSerialNumber"].Value;

            //ライセンスファイルをByte配列として読み込む
            try {
                using(FileStream _fs = new FileStream(LicenseFitePath,FileMode.Open,FileAccess.Read)) {
                    this._licenseData = new byte[_fs.Length];//リサイズ
                    _fs.Read(this._licenseData,0,(int)_fs.Length);//Byteとして読み込む
                }
            } catch(Exception ex) {
                //ライセンスが読めなかった例外を投げる
                throw new Exception(ResourceMsg.LicenseFileOpenError + ex.Message);
            }

             //ライセンスのバイト配列をHEX文字に変換
             StringBuilder hexBuilder = new StringBuilder(this._licenseData.Length * 2);
             foreach(byte _bin in this._licenseData) {
                 hexBuilder.Append(_bin.ToString("X2"));
             }
             this.LicenseHexStr =  hexBuilder.ToString();
            
            //一致していたら正常フラグを立てる
            if(this.LicenseHexStr == this.VolumeSerial) {
                this.IsFine = true;
            }
        }

    }
}
```

# テストコード

```cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using LicenceCheck;

namespace UnitTest {

    [TestClass]
    public class UnitTest1 {
        [TestMethod]
        public void TestLicenseChecker() {
            string LicenceFilePath= "C:/**********/Lisense.dat";
            //ライセンスチェッカのインスタンス
            LicenceChecker LC = new LicenceChecker(LicenceFilePath);
            //参考までに読みこんだ値をプリント
            Console.WriteLine("version = " + LC.Version);
            Console.WriteLine("VolumeSerial = " + LC.VolumeSerial);
            Console.WriteLine("LicenseHexStr = " + LC.LicenseHexStr);
            //一致フラグが立っていればOK
            Assert.AreEqual(true, LC.IsFine);
        }
    }
}


```