2015-09-04 44 views
3

我是Java Card的整个主题的新手,并试图查看一些代码示例以获得更好的理解。 我发现在oracle forum AES使用一个样本,但有以下部分的几个问题:Java Card中的En-/Decryption输出+相应的APDU

 private void doAES(APDU apdu) 
    { 

      byte b[] = apdu.getBuffer(); 

      short incomingLength = (short) (apdu.setIncomingAndReceive()); 
      if (incomingLength != 24) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); 

      //perform encryption and append results in APDU Buffer a[] automatically 

      cipherAES.init(aesKey, Cipher.MODE_ENCRYPT); 
      cipherAES.doFinal(b, (short) dataOffset, incomingLength, a, (short) (dataOffset + 24)); 
      cipherAES.init(aesKey, Cipher.MODE_DECRYPT); 
      cipherAES.doFinal(b, (short) (dataOffset + 24), incomingLength, a, (short) (dataOffset + 48)); 

      // Send results 
      apdu.setOutgoing(); 
      apdu.setOutgoingLength((short) 72); 
      apdu.sendBytesLong(b, (short) dataOffset, (short) 72); 
    } 

从我的理解这个代码利用从传入APDU第24个数据字节,对它们进行加密,并将它们放入字节数组a。 然后它需要接下来的24个数据字节,解密它们并将它们放入一个。

但下面的命令不会因为

使用这些输出数据
apdu.sendBytesLong(b, (short) dataOffset, (short) 72); 

采用B的输出数据......这可能是不正确的,所以请帮助我理解我哪里错了。

另外:一个简单的命令APDU用于加密一个小文本和相应的答案是什么样的?

+0

这个例子只是伤了我的眼睛。请不要使用它。在Java **级别有非常基本的错误。缓冲区处理完全关闭。它当然应该从'a'而不是'b'解密,然后发送'a'。变量'a'应该是短暂的,只有在需要时才可以填写密钥的内容。就我个人而言,我不太喜欢太阳论坛,像SO这样的适度的系统会很快地消灭这些例子(然后再说,你不应该把例子发布在蓝色之外)。 –

回答

5

实际上,来自Oracle论坛的代码并不是很好。它不遵循内存使用的基本规则,它根本不是真实世界的例子。此外,它会很慢,如果使用太频繁,甚至可能会损坏智能卡。

我想你应该通过Java卡教程首先阅读和学习APDU是什么,以及一些有关它的结构,看到了这个问题:

How to get started with Java Cards?

然后,你可以继续到Java卡加密/解密。像这样的东西可以帮助你:

public class MiniApplet extends Applet { 
    public static void install(byte[] bArray, short bOffset, byte bLength) { 
     // GP-compliant JavaCard applet registration 
     new MiniApplet().register(bArray, (short) (bOffset + 1), 
       bArray[bOffset]); 
    } 

    private final AESKey aesKey = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_128, false); 
    private final Cipher aes = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); 

    public void process(APDU apdu) { 
    // Good practice: Return 9000 on SELECT 
    if (selectingApplet()) { 
     return; 
    } 

    final byte[] buf = apdu.getBuffer(); 
    final short dataLen = apdu.setIncomingAndReceive(); 
    final byte ins = buf[ISO7816.OFFSET_INS]; 

    switch (ins) { 
    case (byte) 0x00: //KEY VALUE INIT FROM APDU 
     if (dataLen != 16) //checking key value length 
      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH) 
     aesKey.setKey(buf, ISO7816.OFFSET_CDATA); 
     break; 
    case (byte) 0x01: //DECRYPTION 
    case (byte) 0x02: //ENCRYPTION 
     if ((dataLen & 0x000F) != 0) //checking if input data is block-aligned 
      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH) 

     if (!aesKey.isInitialized()) 
      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); 
     aes.init(aesKey, (ins == 0x02) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT); 
     aes.doFinal(buf, ISO7816.OFFSET_CDATA, dataLen, buf, ISO7816.OFFSET_CDATA); 
     apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, dataLen); 
     break; 
    default: 
     // good practice: If you don't know the INStruction, say so: 
     ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 
    } 
} 

} 

注:我在我的例子初始化从输入命令键值。我的密钥存储在RAM中,这意味着在每次卡重置或另一个小程序选择后,该值都会消失。这并不一定适合您的业务案例,只需在卡上生成一个密钥并将其存储在持久内存中即可。如果是这样,您必须使用不同的密钥类型:KeyBuilder.TYPE_AES而不是KeyBuilder.TYPE_AES_TRANSIENT_DESELECT

+0

尊敬的Vojta,1-为什么您将'buf'和'dataLen'定义为'final'? 2-我们不需要检查'aesKey'是否在0x01和0x02块的开始处被初始化? – Abraham

+0

@Abraham final variable =更快的访问速度。是的,我们可能应该检查它,以及dataLen是否被分配为块大小(16)。我会改进我的答案。 – vojta

+1

@vojta更快的访问从未真正物化到这个级别。但标记'最终'的东西清楚地表明变量不会在以后改变。它始终在我的Eclipse“干净代码”功能中启用,尽管我有时会在代码示例中将其留在此处,以避免这个问题:) –