ConoHa WINGを使ってライセンスサーバーを構築してみた

# はじめに
この記事は「[ConoHa Advent Calendar 2022](https://qiita.com/advent-calendar/2022/conoha)」の22日目の記事です。今回は、ConoHa WINGを使って簡単なソフトウェアのライセンスサーバーを構築してみようというお話です。

# 今回のお話
私は今年から新しい挑戦として、.NET Framework と C# を使ったアプリケーション開発に取り組んでみました。1年たって簡単なアプリケーションを作ることができるようになったのですが、アプリケーションが作れるようになると気になるのがソフトウェアのライセンスです。つまり、製作者が意図しないユーザーには使ってほしくないというというわけです。
そこで、格安で、清楚かわいくて、WEBシステムとデータベースを使うことができるConoHa WINGを使って、簡単なライセンスシステムを構築してみることにしました。

※動作確認とか仕組みとかそういうのを確認して学習することが目的なので、セキュリティとかあまり意識していません。なので、間違っても実環境に投入しないでくださいね♪

# 技術的なお話
## ライセンスサーバーを作ろう
ConoHa WINGで適当なサイトを登録して、ライセンスキーと登録する端末のIDを格納するためのデータベースをあらかじめ作っておきます。データベースのユーザー名とパスワードを忘れずに保管しておきましょう。
ライセンスサーバーはPHPで構築します。今回はとてもシンプルなもので、開発するクライアントソフトウェアからライセンスキーと端末のIDをPOSTされてくるのを受け取るだけのシンプルなものです。

では、ライセンスキーと端末のIDを登録するためのPHPフォームをつくります。

以下のコードで、クライアントソフトウェアからライセンスキーと端末のIDを受け取りデータベースに登録します。ライセンスキーはあらかじめデータベースに格納してあるものを照合して、クライアントソフトウェアから受け取ったライセンスキーと一致していれば端末のIDを登録できます。登録が完了すると値として、「1」を返してきます。失敗すると「0」を返してくる仕様で、クライアントソフトウェア側のエラー判定に使用しています。

当然、あらかじめ登録されていないライセンスキーを入力してもエラーになりますので、まぐれ当たりがなければ、基本的にはライセンスキーを持っている人しか認証できません。

また、このライセンスサーバーは一度登録すると、データベース側で端末のIDを削除しない限り、再登録することができませんので、最初に登録した端末以外は起動できないという仕様になります。

```php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body>
<?php
try{
    $pdo = new PDO(
        'mysql:host=データベースサーバーのアドレス;dbname=データベース名;charset=utf8',
        '接続用ユーザー名',
        '接続用パスワード'
    );
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}catch(PDOException $Exception){
    die('接続エラーA' .$Exception->getMessage());
}
try{
    $reglkey = htmlspecialchars($_GET["reglkey"]);
    $regcpuid = htmlspecialchars($_GET["regcpuid"]);
    $sql = 'UPDATE license SET MACADDR = :regcpuid, FLAG = 1 WHERE LKEY = :reglkey AND FLAG = 0';
    $stmh = $pdo->prepare($sql);
    $params = array(':reglkey' => $reglkey, 'regcpuid' => $regcpuid);
    $stmh->execute($params);
    $count = $stmh -> rowCount();
    echo $count.'';
}catch(PDOException $Exception){
    die('接続エラーB' .$Exception->getMessage());
}

$pdo = null;

?>
</body>
</html>
```

## クライアントソフトウェア側のライセンス認証機能を作ろう
クライアントソフトウェアは.NET Framework と C# を使って開発します。ライセンスサーバー側にライセンスキーと端末のIDを投げるだけのシンプルなものです。
keyinput.Text と名付けてあるクライアントソフトウェアのUIに入っているライセンスキーと cpuid[0] に格納されている端末のIDをライセンスサーバーに投げています。そして、投げた後にライセンスサーバーから戻ってきた値によってライセンス認証が成功したか、失敗したか判定しています。

※このままのコードでは動かないので、適宜必要なパッケージやおまじないを書いています。

```c#
WebClient wc = new WebClient();
NameValueCollection nvc = new NameValueCollection();
nvc.Add("reglkey", keyinput.Text);
nvc.Add("regcpuid", cpuid[0]);
wc.QueryString = nvc;
byte[] resGet = wc.DownloadData("ライセンスサーバーのURL");
wc.Dispose();

string resGetData = System.Text.Encoding.UTF8.GetString(resGet);
var resGetData2 = Regex.Replace(resGetData, "<[^>]*?>", "");
resGetData2 = resGetData2.Replace("\n", "");
resGetData2 = resGetData2.Replace(" ", "");

if(resGetData2 == "1")
{
     MessageBox.Show("正常にライセンス認証が完了しました");
     Environment.Exit(100);

}
```
なお、クライアントソフトウェアのUIはこんな感じでとってもシンプルです。ライセンスキーを入力して、「認証する」を押せば、ライセンスが完了します。
![2022-12-22_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/ff5b8738-7b0a-68d4-de6f-50794f72d8eb.png)
このテキストボックスが、コード内の keyinput.Text と一致しています。

これで簡易ライセンスサーバー完成です！

# まとめ

今回は、ConoHa Wingを使って、簡単ではありますが、ライセンスサーバーを構築しました。
仕様として、製作者が意図したユーザーにはデータベースにあらかじめ登録されたライセンスキーを提供し、そのライセンスキーを使ってユーザーの端末のIDをライセンスサーバーに登録することでライセンスシステムを構築することができました。

ただし、今回はセキュリティを一切意識して作っていません。実際に運用するには、ライセンスサーバーのPHPにもひと工夫もふた工夫も必要でしょうし、ライセンスキーのまぐれ当たりも防ぐような構築が必要です。また、クライアントソフトウェア側からライセンスサーバーのURLが見えないように、アルゴリズムが分からないようにコードの難読化する必要があると思います。
私の今後の目標はそのあたりのセキュリティを考えていくことかなと思っています。

また、今回はConoHa Wing のベーシックプランで、[WINGパック](https://www.conoha.jp/conoha/wing/wingpack/)を使いました。私はお得な24ヶ月契約です！　そして、独自ドメインも2つまで永久無料なので、今回のライセンスサーバーのドメインもそれを使いました！
コントロールパネルのUIもとっても簡単なので気に入っています。動作も軽いのでストレスなしです。
当然モードは、清楚かわいい、このはモードです♪(≧∇≦*)！！