WEB开发网
开发学院软件开发Java 保护自己的MIDlet程序之三:SNProtector 阅读

保护自己的MIDlet程序之三:SNProtector

 2007-12-23 12:32:41 来源:WEB开发网   
核心提示:有了前面的基础,现在就开始实现SNPRotector,保护自己的MIDlet程序之三:SNProtector,这回我们对SNProtector又有哪些要求和目标呢?1,和前面的OnceProtector和TimeProtector一样要使用简单;2,只能象征性的意思一下,如何有更好的加密算法?这个话题就太大了:)有意见

有了前面的基础,现在就开始实现SNPRotector。
这回我们对SNProtector又有哪些要求和目标呢?
1。和前面的OnceProtector和TimeProtector一样要使用简单;
2。用户可以在程序里输入用户名和序列号并验证;
3。允许用户试用;
4。允许验证后继续运行程序。

好了,有了目标之后我们如何管理注册码呢?
放在资源里?放在程序里?这两种方法都不方便批量的分发。
哪里呢?我选择jad里面:)

分发的程序的jad里面有两个用户定义字段:
User: cakec
SN: 8180076728b161326ae2cc61b783b451
他们有什么用?看下去就明白了。

大家是不是又想到一个问题,如何可以像前面的两个Protector一样使用简便
只需要判断一下check,又能在用户输入注册码后再继续运行呢?
因为check判断和其它的startApp的初始工作在一个函数里面,难道有什么办法
让函数停止执行?然而如果主进程停止了执行,我又怎么显示输入注册码的界面,
响应用户的输入呢?思考10秒钟然后看我的方案:)

看看你跟我想的是不是一样:利用midlet的生命周期。
大家知道midlet每次pause之后再次切换的时候系统会调用startApp函数,
此时让check通过不就可以了。

ok,下面来看实现:
首先是注册码输入界面SNInputUI:

package vmlinux.app;

import javax.microedition.lcdui.*;

public class SNInputUI extends Form {

   TextField tfUser_;
   TextField tfSN_;
  
   public SNInputUI()
   {
     super(StringManager.get("SNInputUI.Title"));
     tfUser_=new TextField(StringManager.get("SNInputUI.User"),"",20,TextField.ANY);
     tfSN_=new TextField(StringManager.get("SNInputUI.SN"),"",60,TextField.ANY);
     this.append(tfUser_);
     this.append(tfSN_);
   }
  
   public String getUser()
   {
     return tfUser_.getString();
   }
   public String getSN()
   {
     return tfSN_.getString();
   }
}

这个没有难度,不说了。
然后是SNProtector:

我们需要使用新的UI来获取注册信息,所以构造函数是这样:

   public SNProtector(MIDlet app)
   {
     super(app);
     ui_=new SNInputUI();
     cmdDone_=new Command(StringManager.get("SNProtector.Done"),Command.SCREEN,1);
     cmdTry_=new Command(StringManager.get("SNProtector.Try"),Command.SCREEN,2);
     ui_.addCommand(cmdDone_);
     ui_.addCommand(cmdTry_);
   }

由于我们已经设置了基类Protector为CommandListener,所以需要改造一下基类:
修改Protector的commandAction并增加doCommand函数

   public void commandAction(Command c, Displayable d) {

   if(c==cmdOK_)
       app_.notifyDestroyed();
     else
       doCommand(c);
   }


   protected void doCommand(Command c)
   {
    
   }

然后在SNProtector重写doCommand:

   protected void doCommand(Command c)
   {
     if(c==cmdDone_)
     {
         。。。
     }
     else if(c==cmdTry_)
     {
          。。。
     }
   }

当用户选择注册的时候,要做的工作就是把用户的输入保存起来。
如果用户选择了试用,就需要一个标志,说明用户要试用。
做完这些工作我们还需要提醒用户暂停程序,然后再恢复程序
以便达到我们重新调用startApp的目的。

所以完整的doCommand就是这样:

   protected void doCommand(Command c)
   {
     if(c==cmdDone_)
     {
       SNInputUI input=((SNInputUI)ui_);
       if(input.getUser().equals(app_.getAppProperty("User")))
       {
         try
         {
           RecordStore.deleteRecordStore(SNRMS);
         }
         catch(Exception ex)
         {
           //ignore
         }
         try
         {
           RecordStore rs=RecordStore.openRecordStore(SNRMS,true);
           byte[] buf=input.getSN().getBytes();
           rs.addRecord(buf,0,buf.length);
           rs.closeRecordStore();
         }
         catch(Exception ex)
         {
           System.out.println(ex);
         }
         showAlert();
       }
     }
     else if(c==cmdTry_)
     {
       tryit=true;
       showAlert();
     }
   }


现在已经把用户输入的注册码保存了起来,在决定命运的check函数里就需要
把保存的信息读出来然后对比判断是否有效,这个就是check函数:

   public boolean check()
   {
     if(tryit)
     {
       tryit=false;
       return true;
     }
    
     boolean r=true;
     try
     {
       RecordStore rs=RecordStore.openRecordStore(SNRMS,true);
       RecordEnumeration e=rs.enumerateRecords(null,null,false);
       if(e.hasNextElement())
       {
         r=checkCode(generate(app_.getAppProperty("User"),new String(e.nextRecord())));
       }
       else
       {
         r=false;
       }
       rs.closeRecordStore();
     }
     catch(Exception ex)
     {
       r=false;
     }
     if(!r)
     {
       showUI();
     }

   return r;
   }

最后就是sn系统的核心checkCode和generate函数。
generate根据用户名和输入的注册码生成注册序列号
然后checkCode比较生成的注册序列号和验证码得出结论。
大家通过开头的注册码是不是已经知道了,我在这个例子中
简单的使用了md5加密来实现这个过程,下面就是实现:

   boolean checkCode(byte[] x)
   {
     boolean r=true;
     byte[] s=getCode();
     if(x!=null && s!=null && x.length==s.length)
     {
       for(int i=0;i<x.length;++i)
         if(x[i]!=s[i])
         {
           r=false;
           break;
         }
     }
     else
       r=false;
     return r;
   }
  
   byte[] getCode()
   {
     String sn=app_.getAppProperty("SN");
     if(sn==null)
       return null;
     else
     {
       //deserialize sn from text
       return sn.getBytes();
     }
   }
  
   public byte[] generate(String user,String code)
   {
     String u=app_.getAppProperty("User");
     if(u==null !u.equals(user))
       return null;
     else
     {
       //compute inner-code from user and code
       Md5 md5=new Md5(user+"\r\n"+code+"\r\nvmlinux snprotector");
       try
       {
         md5.processString();
         return md5.getStringDigest().getBytes();
       }
       catch(Exception ex)
       {
         System.out.println(ex);
         return null;
       }
     }
   }


怎么样,这就是我的SNProtector的工作过程。
使用方法:在startApp开始加入if(!new SNProtector(this).check())return;

最后你除了要把开头的两条内容加入jad文件还需要告诉用户一个密码:123456
这个就是cakec的注册密码。

这个SNProtector距离实际应用还有相当距离,因为加密方法太简单,随便生成密码
代入公式把生成的md5码放到SN里面即可,只能象征性的意思一下。如何有更好的加密算法?
这个话题就太大了:)

有意见或建议请联系vmlinuxx@Gmail.com,共同学习,共同提高:)

(出处:http://www.cncms.com)


Tags:保护 自己 MIDlet

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接