Unity实用功能之程序加密(三)- 获取设备硬件信息

1,460 阅读5分钟

这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战

概述

前两篇文章分别介绍了使用时间和限制使用次数进行加密,其实加密的方式还有很多种,今天主要介绍下通过获取计算机的硬件信息进行加密,但是这种方法加密效果不是很好,这个要分很多种情况。首先一起来看一下优缺点

优缺点

优点:

  • 在用户使用过该软件之后,不能够将其复制到别的设备上使用,仅可以一台机器使用 缺点:
  • 一个没有运行过新打包出来的程序可以随意复制,没法做到控制 如果程序是联网的还好说,可以达成一个安装包进行安装次数统计等限制,离线版的就没有办法了。如果有哪位小伙伴有更好的方法欢迎留言交流讨论
    还有一种方法是提前知道用户电脑的MAC地址或设备唯一标识,然后直接写入到程序中或者说存到本地进行读取。

思路分析

目前就作者知道的是Unity可以获取电脑的MAC地址设备唯一标识符,这两个信息是每个电脑都不可能一样的,可以用来进行加密。 首先在Unity在StreamAssets下创建一个txt,写入一条加密数据,内容可以自己定,程序启动之后,对数据进行解密,看是否和规定的数据一样,相同说明程序是第一次运行,获取本机的MAC地址,并通过加密算法写入本地。如果不相同,说明数据已经写入过,解密,和本地MAC地址进行对比,相同,则继续,否则程序不可用。
也可以直接将本地的MAC地址或设备唯一标识直接写入程序中,这样就可以固定一台机器使用(前提是要获取用户电脑信息)

功能实现

数据加密

首先我们来看一下数据是如何加密的,然后我们在进行下一步。这里我们使用的是RijndaelManaged加密解码。需要导入命名空间 using System.Security.Cryptography;,需要注意的是,该加密方式需要使用一个32位的key(自己设定就可以了,可以在程序中设置为只读的),数据加密和解密的时候key必须相同,否则会出现数据解析结果不同或出现错误的情况,主要代码如下

 /// <summary> 
 /// 加密数据 
 /// </summary>
 public static string Encrypt(string Text, string sKey)
 {
     byte[] keyArray = UTF8Encoding.UTF8.GetBytes(sKey);
     RijndaelManaged encryption = new RijndaelManaged();
     encryption.Key = keyArray;
     encryption.Mode = CipherMode.ECB;
     encryption.Padding = PaddingMode.PKCS7;
     ICryptoTransform cTransform = encryption.CreateEncryptor();
     byte[] _EncryptArray = UTF8Encoding.UTF8.GetBytes(Text);
     byte[] resultArray = cTransform.TransformFinalBlock(_EncryptArray, 0, _EncryptArray.Length);
     return Convert.ToBase64String(resultArray, 0, resultArray.Length);
 }
/// <summary> 
/// 解密数据 
/// </summary> 
/// <param name="Text">要解密的内容</param> 
/// <param name="sKey">key,必须为32位,要和加密时的key相同</param> 
/// <returns></returns> 
public static string Decrypt(string Text, string sKey)
{
    try
    {
        byte[] keyArray = UTF8Encoding.UTF8.GetBytes(sKey);
        RijndaelManaged decipher = new RijndaelManaged();
        decipher.Key = keyArray;
        decipher.Mode = CipherMode.ECB;
        decipher.Padding = PaddingMode.PKCS7;
        ICryptoTransform cTransform = decipher.CreateDecryptor();
        byte[] _EncryptArray = Convert.FromBase64String(Text);
        byte[] resultArray = cTransform.TransformFinalBlock(_EncryptArray, 0, _EncryptArray.Length);
        return UTF8Encoding.UTF8.GetString(resultArray);
    }
    catch
    {
        return "解密错误";
    }
}

通过调用上面的两个代码,我们可在Unity中测试一下

 //需要加密的字符串
 string encrypt = string.Empty;
 //加密后的字符串
 string desEncrypt = string.Empty;
 //key,加密和解密都需要用到
 readonly string key = "45621488631456644633443346324661";

 // Start is called before the first frame update
 void Start()
 {       
     encrypt = "我是测试数据123";
     desEncrypt = DESEncrypt.Encrypt(encrypt, key);
     Debug.Log(encrypt + ">>加密结果为>>" + desEncrypt);
     Debug.Log(desEncrypt + ">>解密结果为>>" + DESEncrypt.Decrypt(desEncrypt, key));
 }

运行结果

image.png

获取MAC地址

首先我们需要在脚本中引用System.Net.NetworkInformation程序集,然后使用NetworkInterface中的GetAllNetworkInterfaces()可以获取所有的网络相关信息,其中GetPhysicalAddress()方法就可以拿到设备的物理地址即Mac地址。要获取所有信息之后再遍历找到MAC地址。具体代码如下:

/// <summary>
/// 获取MAC地址(物理地址)
/// </summary>
/// <returns></returns>
private static string GetMacAddress()
{
    string physicalAddress = "";
    NetworkInterface[] allInfo = NetworkInterface.GetAllNetworkInterfaces();
    foreach (NetworkInterface adaper in allInfo)
    {
        if (adaper.Description == "en0")
        {
            physicalAddress = adaper.GetPhysicalAddress().ToString();
            break;
        }
        else
        {
            physicalAddress = adaper.GetPhysicalAddress().ToString();
            if (physicalAddress != "")
            {
                break;
            }
        }
    }
    return physicalAddress;
}

运行结果:

image.png

获取设备唯一标识

在Unity中有一个类SystemInfo,是专门用来获取设备的信息的,具体后期文章中会有介绍,内容有点多。本篇想要获取设备的唯一标识,只需要一句话即可,如下

SystemInfo.deviceUniqueIdentifier;

他返回的就是设备的唯一标识

image.png 运行结果就是

image.png

验证

首先我们创建一个txt文本放到StreamingAssets下,然后用程序生成一条加密的初始化判断语句,写入TXT中。

 //程序第一运行
    readonly string firstPlayStr = "这是程序的第一次运行!";
    // Start is called before the first frame update
    void Start()
    {
        //这里加密一条数据用来初始化第一次
        Debug.Log(DESEncrypt.Encrypt(firstPlayStr, key));
    }

将打印的信息复制到文本中。(手动复制,不是程序复制,没打包之前的操作)
然后读取文本,解密,对比数据。这里使用的是设备唯一标识。
读取文本,先看本地是否有文件,有就读,没有直接退出程序

    /// <summary>
    /// 读取数据
    /// </summary>
    /// <returns></returns>
    private string ReadTxt()
    {
        string str = string.Empty;
        //如果有数据就读
        if (File.Exists(path))
        {
            StreamReader sr = new StreamReader(path);
            str = sr.ReadToEnd();
            sr.Close();
        }
        else
        {
            //没有数据直接退出
#if UNITY_EDITOR
           UnityEditor.EditorApplication.isPlaying = false;
#else
             Application.Quit();
#endif
        }
        return str;
    }

然后和读取的数据的解密结果进行对比,结果等于程序第一次运行,则获取设备唯一标识,并加密,然后写入到txt中,如果结果不是程序第一次运行,也不和设备唯一标识相同,退出程序

        //读取数据
        path = Application.streamingAssetsPath + "/info.txt";
        string readString = DESEncrypt.Decrypt(ReadTxt(), key);
        //解密后的数据是程序第一次运行的数据
        if (readString == firstPlayStr)
        {
            //写入唯一标识的加密数据
            WriteTxt(new string[] { DESEncrypt.Encrypt(SystemInfo.deviceUniqueIdentifier, key) });
        }
        else
        {
            //和本机设备标识对比
            if (SystemInfo.deviceUniqueIdentifier.Equals(readString))
            {
                //验证通过
            }
            else
            {
                //验证失败
#if UNITY_EDITOR
                UnityEditor.EditorApplication.isPlaying = false;
#else
             Application.Quit();
#endif
            }
        }

写入数据

    /// <summary>
    /// 写入数据
    /// </summary>
    /// <param name="contents"></param>
    public void WriteTxt(string[] contents)
    {
        if (!File.Exists(path))
        {
            //文件不存在,退出程序
#if UNITY_EDITOR
            UnityEditor.EditorApplication.isPlaying = false;
#else
             Application.Quit();
#endif
        }
        else
        {
            StreamWriter sw = new StreamWriter(path);
            foreach (var item in contents)
            {
                sw.WriteLine(item);
            }
            sw.Close();
        }
    }

源代码分享

这个就不上效果图了,直接上源代码吧,一共有两个,感兴趣的可以下载下来看看 GitHub下载地址:点击这里跳转下载

写在最后

所有分享的内容均为作者在日常开发过程中使用过的各种小功能点,分享出来也变相的回顾一下,如有写的不好的地方还请多多指教。欢迎大家相互学习进步。本片文章就先写到这里,希望对你能够有所帮助