标签:
很多时候我们并不需要自己去实现一个较为复杂的算法,而只需要知道怎么去调用现有的实现。API调用,在C/C++是用include+函数的形式,java其实也无非就是import+类(方法、变量)的形式,其他语言的也差不多,例如web里面的前端框架,所谓框架其实就是一堆别人已经写好的代码,你拿去用,然后继续在上面填充自己的代码,说到底其实就是代码复用。而在java这里,因为以.class文件的形式封装了实现,我们看到的接口:一堆.class文件里面方法的具体实现被隐藏了,只能看到个函数原型,所以调用javacard API的难度在于你得去看别人写的API注释(全英,学好英语的作用有木有),去了解每个函数干什么的,然后你才知道要实现自己的功能需要用到它的哪些函数以及调用顺序是怎样的,一般在API(库)的目录下面都会有使用文档(html形式的),用eclipse直接翻.class文件的话会有一堆html标签符看着眼疼。
话说不多,上代码:
Des.java(调用DES算法API的主要文件):
package helloWorld; import javacard.framework.JCSystem; import javacard.security.DESKey; import javacard.security.Key; import javacard.security.KeyBuilder; import javacardx.crypto.Cipher; public class Des { private Cipher DESEngine; private Key myKey; private byte[] temp; private RandGenerator rand; public Des() { //必须先初始化(获得实例instance才能init否则报错) DESEngine = Cipher.getInstance(Cipher.ALG_DES_CBC_ISO9797_M1, false); //buildKey创建的是未初始化的key,密钥值需用setKey函数手动赋值 myKey = KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES, false); //设置暂存变量temp temp = JCSystem.makeTransientByteArray((short)64, JCSystem.CLEAR_ON_DESELECT); //给rand对象也分配空间,不然无法执行RandGenerator类的代码!! rand = new RandGenerator(); //****** 1 *******首先自动生成个密钥 //产生64bit随机数 temp = rand.GenrateSecureRand((short)64); //Util.arrayFillNonAtomic(temp1, (short)16, (short)48, (byte)0x11); //设置密钥--拿随机数当密钥 ((DESKey)myKey).setKey(temp, (short)0); //****** 2 *******初始化加密密钥和加密模式 DESEngine.init(myKey, Cipher.MODE_ENCRYPT); } public final void GetCipher(byte[] inBuf, short inOffset, short inLength, byte[] outBuf, short outOffset) { //****** 3 *******传入密文进行加密并得到密文 //需要特别注意doFinal加密之后的结果估计有64位以上,所以outBuf在前面的定义时也要new出足够的大小,否则又no precise... DESEngine.doFinal(inBuf, inOffset, inLength, outBuf, outOffset); } }
接着就是生成随机数的RandGenerator.java文件,调用了RandomData API:
package helloWorld; import javacard.framework.JCSystem; import javacard.security.RandomData; public class RandGenerator { private byte[] temp; //随机数的值 private RandomData random; private byte size; //随机数长度 //构造函数 public RandGenerator() { size = (byte)4; temp = JCSystem.makeTransientByteArray((short)4, JCSystem.CLEAR_ON_DESELECT); //类当中有getInstance的都要先调用这个函数获取对象实例才能使用其他方法,不然6F00 random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); } //产生length长度的随机数并返回 public final byte[] GenrateSecureRand(short length) { temp = new byte[length]; //生成4bit的随机数 random.generateData(temp, (short)0, (short)length); return temp; } //返回随机数长度 public final byte GetRandSize() { return size; } }
package helloWorld; //import Hello; import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; public class Hello extends Applet { //下面这些都是未分配空间的实例化!需要后面自己使用new关键字或者用getInstance函数分配空间! private Des des = new Des(); public static void install(byte[] bArray, short bOffset, byte bLength) { // GP-compliant JavaCard applet registration new Hello().register(bArray, (short) (bOffset + 1), bArray[bOffset]); } public void process(APDU apdu) { // Good practice: Return 9000 on SELECT if (selectingApplet()) { return; } //将缓冲区与数组buf建立映射绑定 byte[] buf = apdu.getBuffer(); short lc = apdu.setIncomingAndReceive();//读取data并返回data长度lc byte[] data = new byte[lc]; Util.arrayCopyNonAtomic(buf, ISO7816.OFFSET_CDATA, data, (short)0, lc); //byte[] src = {'h','e','l','l','o',',','w','o','r','l','d'};//11 bytes byte[] cipher = new byte[128]; byte ins = buf[ISO7816.OFFSET_INS]; switch (ins) { case (byte) 0x00://INS == 0x00 表明要用DES加密 //****** 3 *******传入密文进行加密并得到密文 des.GetCipher(data, (short)0, lc, cipher, (short)0); Util.arrayCopyNonAtomic(cipher, (short)0, buf, (short)ISO7816.OFFSET_CDATA, lc); apdu.setOutgoingAndSend((short)5, lc); break;//一定要有break否则会继续进入switch循环 case (byte) 0x01: break; default: // good practice: If you don't know the INStruction, say so: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } }
然后用JCOP Shell工具执行测试。
测试脚本(des.jcsh):
/select 554433221100 /send 00000000041234567804 /send 00000000048765432104
执行脚本看到结果:
Applet返回0x04长度的加密后的密文。
最后记录几个自己在写代码的过程中遇到的一些需要注意的事项。
1:因为涉及到了多个类,所以会有在另一个类(文件)中建立类的对象,但是千万记得要给这个对象分配空间!类如果有有getInstance函数的就调用这个函数进行对象的实例化,如果没有的就用new关键字来分配空间给对象!之前自己就出现了没分配空间给对象的情况,结果测试时返回6F00,然后调试的时候一直是跳不进去Des.java的,因为根本没分配空间给这个对象,也就无从谈跳过去执行Des.java里面的代码了。这个在面向对象编程尤其需要注意。
2:暂存对象(包含变量/数组)与永久对象。用JCSystem.makeTransientByteArray(第一个参数表示数组长度,第二个参数表示声明周期(一般用CLEAR_ON_DESELECT也就是该AID的Applet被deselect的时候被回收))函数可以声明一个固定长度的暂存数组。为什么要用到暂存对象呢?因为javacard毕竟那么小,它所拥有的存储空间(无论是内存或者是ROM(包括那个啥EEPROM))都是很有限的,ROM一般是用来存卡片操作系统(COS,一般用汇编写成)和虚拟机(必须下层有操作系统作为基础,自己并不是操作系统)的,永久对象会存放在EEPROM空间当中,而暂存对象(数组)存放在RAM空间中,自然读写速度就快很多,所以对于需要更新次数频繁的数据,并且暂存即可的,声明为暂存对象可提高程序效率。同时,EEPROM中创建的对象,对这些对象的操作必须具有原子性(例如javacard的事务[transaction]处理机制),而RAM里面对象的处理自然不具有原子性。
3:阅读API里面的函数时容易发现有些在前面带“@deprecated”字眼,@override表示覆盖父类的方法,而前面这个表示不建议使用的方法(即将被淘汰或怎样的),一般这个方法都会被删除线划掉。
4:然后说一个跟des加密不相关的,是跟公钥密码系统如RSA算法相关的,des这些对称密码系统只需要一个密钥,而公钥系统需要公钥私钥,所以需要用一个KeyPair对象去存储私钥和公钥,而不能用PrivateKey对象和PublicKey对象就算了,需要用KeyPair对象囊括住这两个对象,后面会继续写RSA算法API调用的blog。
5:switch相当于一个循环,在每一个case后面都必须要加一个break关键字,否则执行完一次case之后会继续跑去循环,下一次的话case的值是不确定的,所以会跳去执行default语句里面的代码,所以如果执行的某个case里面没有break,程序后面还会执行default里面的语句。
6:提到对象的初始化(分配空间),注意这个操作一般放在类的构造函数当中,如果是Applet主文件,就在install()的时候完成这些操作。
7:最后抽取几个上面的关键函数出来提一提:
DESEngine = Cipher.getInstance(Cipher.ALG_DES_CBC_ISO9797_M1, false);
myKey = KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES, false);
((DESKey)myKey).setKey(temp, (short)0);
密钥设置好了,然后就是将这个密钥扔到DES引擎对象里面去,用下面这个函数:
DESEngine.init(myKey, Cipher.MODE_ENCRYPT);
最后就可以传入数据进行加密得到密文了:
DESEngine.doFinal(inBuf, inOffset, inLength, outBuf, outOffset);
标签:
原文地址:http://blog.csdn.net/lv_victor/article/details/51933462