先谈谈net平台开发c#项目注册保护机制
now,一些共享软件往往提供给使用者的是一个功能不受限制的限时使用版,在试用期内使用者可以无限制的使用软件的全部功能(只是可能会出现提示使用者注册的窗口),试用期一过部分(或全部)功能失效,要想继续使用只能向作者索取注册码(或注册文件)完成对软件的合法注册,注册后的软件将解除一切使用限制。如果您也开发出一个有价值的作品,是否也希望为自己的软件增加一个这样的功能呢?当前对于.NET反编译的问题不在本文讨论之内,相关文章已经很多!本文我们就一起探讨软件注册功能的实现。
实现软件的注册功能方法很多,最需要考虑的就是不能轻易的让使用者破解,在这里,我就谈谈“.NET快速开发整合框架(RDIFramework.NET)”中平台注册功能的实现方法。在RDIFramework.NET中,注册功能主要方法就是对计算机唯一硬件信息进行RSA数字签名达到软件注册和保护的功能,该方法实现简单,安全性相应较高。
计算机唯一硬件信息(我们知道计算机中的关键部件如CPU,主板等在全球范围内都有一个独一无二的产品序列号,用户通过注册模块获取这些产品序列号(即传统所说的:机器吗)并将它发送给软件开发商要求进行RSA数据签名,软件开发商获得这些机器码后利用手中的私钥对这些信息进行RSA数字签名,生成的签名信息(即注册码)发回给用户,用户将收到的注册码输入注册模块的注册码框,软件即可利用公钥执行签名验证,如果输入的注册码被证明就是经过开发商数字签名的机器码,则完成注册过程。
注册功能项目结构图如下所示:
图1 注册功能项目结构
平台服务端注册码生成主界面如下所示:
图2 注册文件管理器
通过“注册文件管理器”,我们就可以根据用户提供的信息来生成软件的注册文件。
客户端的注册主要就是根据我们提供的注册文件与公钥,来验证注册文件是否为当前客户的有效注册文件,如果有效,注册成功,无效则注册失败!客户端注册功能设计参考如下所示:
图3 平台注册
用户单击“注册”按钮,成功注册提示:
图4注册成功
服务端注册码生成核心代码:
一、 生成公/私钥文件:
1 private void btnGenerateKey_Click(object sender, EventArgs e) 2 { 3 if (MessageBox.Show("确定生成生成公/私钥对吗(是/否)?", "询问信息", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) 4 == System.Windows.Forms.DialogResult.Cancel) 5 { 6 return; 7 } 8 9 RSACryptoServiceProvider crypt = new RSACryptoServiceProvider(); 10 11 string publicKey = crypt.ToXmlString(true); 12 string privateKey = crypt.ToXmlString(false); 13 crypt.Clear(); 14 15 //生成公钥 16 using (StreamWriter sw = new StreamWriter(KeyPath + "RDIFrameworkkey.key", false, UTF8Encoding.UTF8)) 17 { 18 sw.Write(SecretHelper.AESEncrypt(publicKey)); 19 sw.Flush(); 20 } 21 22 //生成私钥 23 using (StreamWriter sw = new StreamWriter(KeyPath + "RDIFrameworkPrivateKey.key", false, UTF8Encoding.UTF8)) 24 { 25 sw.Write(SecretHelper.AESEncrypt(privateKey)); 26 sw.Flush(); 27 } 28 29 MessageBox.Show("成功生成公/私钥对!","提示信息",MessageBoxButtons.OK,MessageBoxIcon.Information); 30 }
二、 生成注册文件:
1 private void btnGenerateRegisterFile_Click(object sender, EventArgs e) 2 { 3 if (string.IsNullOrEmpty(txtUserEmail.Text.Trim())) 4 { 5 MessageBox.Show("用户邮箱不能为空!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning); 6 txtUserEmail.Focus(); 7 return; 8 } 9 else 10 { 11 if (!RegexValidatorHelper.IsMatch(txtUserEmail.Text.Trim(), Pattern.EMAIL)) 12 { 13 MessageBox.Show("邮箱格式不正确!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning); 14 txtUserEmail.SelectAll(); 15 return; 16 } 17 } 18 19 if (string.IsNullOrEmpty(txtCPUSerialNo.Text.Trim())) 20 { 21 MessageBox.Show("CPU序列号不能为空!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning); 22 return; 23 } 24 25 if (!string.IsNullOrEmpty(txtUseLimited.Text.Trim())) 26 { 27 if (!RegexValidatorHelper.IsMatch(txtUseLimited.Text.Trim(), Pattern.INTEGER)) 28 { 29 MessageBox.Show("使用次数应该为数值型!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning); 30 txtUseLimited.SelectAll(); 31 return; 32 } 33 } 34 35 //读取私钥 36 StreamReader sr = new StreamReader(KeyPath + "RDIFrameworkPrivateKey.key", UTF8Encoding.UTF8); 37 string keypair = sr.ReadToEnd(); 38 sr.Close(); 39 40 //用私钥参数初始化RSACryptoServiceProvider类的实例crypt。 41 RSACryptoServiceProvider crypt = new RSACryptoServiceProvider(); 42 43 crypt.FromXmlString(SecretHelper.AESDecrypt(keypair)); 44 45 UTF8Encoding enc = new UTF8Encoding(); 46 47 string trialTime = "30";//试用次数(默认:30数,0:表示永久) 48 if (!string.IsNullOrEmpty(txtUseLimited.Text.Trim())) 49 { 50 trialTime = txtUseLimited.Text.Trim(); 51 } 52 string regInfo = txtUserEmail.Text.Trim() + ";" + txtMAC.Text.Trim() + ";" + txtCPUSerialNo.Text.Trim() + ";" + trialTime; 53 54 byte[] bytes = enc.GetBytes(regInfo);//格式:邮箱地址;MAC;CPU序列号;试用时间 55 //对用户信息加密 56 bytes = crypt.Encrypt(bytes, false); 57 58 //生成注册数据,对二进制字节进行Base64编码,但采用注册文件的形式的进修也可以不做此转化。 59 string encrytText = System.Convert.ToBase64String(bytes, 0, bytes.Length); 60 61 //将注册码写入文件 62 using (StreamWriter sw = new StreamWriter(KeyPath + "RDIFramework_reg_file.lic", false, UTF8Encoding.UTF8)) 63 { 64 sw.Write(encrytText); 65 sw.Flush(); 66 } 67 68 MessageBox.Show("注册文件:RDIFramework_reg_file.lic生成成功!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information); 69 }
三、 验证注册文件:
1 private void btnCheckRegistr_Click(object sender, EventArgs e) 2 { 3 //读取注册数据文件 4 StreamReader sr = new StreamReader(KeyPath + "RDIFramework_reg_file.lic", UTF8Encoding.UTF8); 5 string encrytText = sr.ReadToEnd(); 6 sr.Close(); 7 8 9 //读取公钥 10 StreamReader srPublickey = new StreamReader(KeyPath + "RDIFrameworkkey.key", UTF8Encoding.UTF8); 11 string publicKey = srPublickey.ReadToEnd(); 12 srPublickey.Close(); 13 14 //用公钥初化始RSACryptoServiceProvider类实例crypt。 15 RSACryptoServiceProvider crypt = new RSACryptoServiceProvider(); 16 crypt.FromXmlString(SecretHelper.AESDecrypt(publicKey)); 17 UTF8Encoding enc = new UTF8Encoding(); 18 byte[] decryptByte; 19 try 20 { 21 byte[] newBytes; 22 newBytes = System.Convert.FromBase64CharArray(encrytText.ToCharArray(), 0, encrytText.Length); 23 decryptByte = crypt.Decrypt(newBytes, false); 24 string decrypttext = enc.GetString(decryptByte); 25 // 26 //TODO:在此处添加验证逻辑 27 // 28 MessageBox.Show(decrypttext); 29 } 30 catch(Exception ex) 31 { 32 MessageBox.Show(ex.Message); 33 } 34 }
至此,软件的注册功能就完成了,当然还有其他很多方法,比如:
一、 采用加密狗的方式(最安全的方式)。
二、 在线验证注册信息(用户需能上网),这种方式也比较可靠。
Android
注册界面代码
如下:注册界面,有四个可填项: 用户名,密码,确认密码,手机号码 我就是想知道,当我填完了四项内容后,点击提交按钮,我所填的内容能保存在应用中,并在再次调到登录界面时能用刚注册的信息成功登录,这是可以实现的吧?
code:
2:配置中声明另外一个acitivity
我们先看第一步,这里是触屏处理中的一段代码:
代码如下 | |
public boolean onTouchEvent(MotionEvent event) { float pointx = event.getX(); float pointy = event.getY(); if (pointx > bp_x + 14 && pointx < bp_x + 14 + 117) { if (pointy > bp_y + 43 && pointy < bp_y + 43 + 15) { // 帐号 Intent i = new Intent();// 得到一个意图的实例 i.putExtra("count", 1);// 写出数据 i.putExtra("himi", str_zh); i.setClass(MainActivity.instance, Register.class);// 设置当前activity以及将要操作的类 MainActivity.instance.startActivity(i);// 用当前activity来启动另外一个activity } } } |
显示定义一个intent对象,Intent这个类的机制是协助交互的,详细的说明这里不多讲。
Intent中的putExtra()函数是起到两个activity之间交互交互的作用,这个方法类似 hashtable 或者hashmap中的put,第一个参数是key(索引) ,后一个参数volue(值),根据key我们可以得到对应的volue了。那么后面我也附上接受的处理。
Intent 中的setClass()函数也是传入两个参数,第一个是传入当前实例的activity对象,后面一个参数指需要打开的activity这个类!然后我们就可以利用当前activity对象来启动另外一个activity了。然后我们看下在另外一个activity是如何创建并且怎么接受数据的。
代码如下 | |
package com.himi; import android.app.Activity; /** * @author Himi **/ public class Register extends Activity { @Override Intent intent = this.getIntent(); |
以上代码可以看出,新建一个activity其实只需要继承Activity以及重写onCreate()方法即可。当然创建的还需要一步很重要的步骤,我会在第二步中会详细说明,这里我们看下是如何接受之前的activity传来数据的。
代码如下 | |
Intent intent = this.getIntent(); count = (byte) intent.getIntExtra("count", 0); |
接受也是很简明易懂,创建一个Intent 意图对象,调用来去getIntExtra函数得到之前传来的数据,根据key。当然还有getStringExtra()等等函数都是类似,只是根据你传入的数据不同选择不同函数罢了。同学们应该注意的是getIntExtra中第二个参数是什么意思,其实就是一个对于找不到key相匹配的时候会默认return 0;
那么下面介绍第二步:在配置中声明
当创建一个activity的时候我们必须在AndroidMainFeset.xml中去声明我们创建的这个类是个Activity。