DES Crypto applet在收到任何命令

时间:2015-05-06 05:49:44

标签: java cryptography javacard

在下面,您将看到一个简单的Java Card小程序,该小程序用于使用不同的DES3DES算法加密和解密数据。

这些是支持的命令:

  1. 00 C0 00 00 | KeyLength | KeyValue :设置DES / 3DES密钥。 (DES为8字节,2Key / 3Key 3DES算法为16/24字节)

  2. 00 C1 XX YY | DataLength | DataValue :用于DES加密/解密

  3. 00 C2 XX YY | DataLength | DataValue :适用于2Key 3DES加密/解密
  4. 00 C3 XX YY | DataLength | DataValue :适用于3Key 3DES加密/解密
  5.   

    XX = 0x00:DES_CBC_ISO9797_M1

         

    XX = 0x01:DES_CBC_ISO9797_M2

         

    XX = 0x02:DES_CBC_NOPAD

         

    XX = 0x03:DES_CBC_PKCS5

         

    XX = 0x04:DES_ECB_ISO9797_M1

         

    XX = 0x05:DES_ECB_ISO9797_M2

         

    XX = 0x06:DES_ECB_NOPAD

         

    XX = 0x07:DES_ECB_PKCS5

         

    YY = 0x00:加密

         

    YY = 0x01:解密

    该计划:

    package cryptoPack;
    
    import javacard.framework.APDU;
    import javacard.framework.Applet;
    import javacard.framework.ISO7816;
    import javacard.framework.ISOException;
    import javacard.framework.JCSystem;
    import javacard.framework.Util;
    import javacard.security.DESKey;
    import javacard.security.KeyBuilder;
    import javacardx.crypto.Cipher;
    
    public class CryptoDES extends Applet {
    
        // Array for the encryption/decryption key
        private byte[] TheDES_Key = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x00 };
    
        // Defining required Keys
        DESKey MyDES1Key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,
                KeyBuilder.LENGTH_DES, false);
        DESKey MyDES2Key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,
                KeyBuilder.LENGTH_DES3_2KEY, false);
        DESKey MyDES3Key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,
                KeyBuilder.LENGTH_DES3_3KEY, false);
    
        // Defining required cipher
        Cipher MyCipher;
    
        // Defining switch case variables for supported instructions
        final byte SetKey = (byte) 0xC0;
        final byte OneKeyDES = (byte) 0xC1;
        final byte TwoKeyDES = (byte) 0xC2;
        final byte ThreeKeyDES = (byte) 0xC3;
    
        // Defining switch case variables for cipher algorithms
        final byte DES_CBC_ISO9797_M1 = (byte) 0x00;
        final byte DES_CBC_ISO9797_M2 = (byte) 0x01;
        final byte DES_CBC_NOPAD = (byte) 0x02;
        final byte DES_CBC_PKCS5 = (byte) 0x03;
        final byte DES_ECB_ISO9797_M1 = (byte) 0x04;
        final byte DES_ECB_ISO9797_M2 = (byte) 0x05;
        final byte DES_ECB_NOPAD = (byte) 0x06;
        final byte DES_ECB_PKCS5 = (byte) 0x07;
    
        // Defining Proprietary Status Words
        final short KeyInNotSetGood = 0x6440;
    
        // A flag to be sure that the configured key has the same length that the
        // algorithm needs.
        byte ConfiguredKeyLength = 0;
    
        private CryptoDES() {
    
        }
    
        public static void install(byte bArray[], short bOffset, byte bLength)
                throws ISOException {
            new CryptoDES().register();
        }
    
        public void process(APDU apdu) throws ISOException {
    
            // Assigning 0 to "ConfiguredKeyLength" to force the user to use ...
            // ... "SetKey" command, after applet selection.
            if (selectingApplet()) {
                ConfiguredKeyLength = 0;
                return;
            }
    
            byte[] buffer = apdu.getBuffer();
    
            // Checking the CLA field in the APDU command.
            if (buffer[ISO7816.OFFSET_CLA] != 0) {
                ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
            }
    
            // Checking the P1 and P2 fields in the APDU command.
            if (buffer[ISO7816.OFFSET_P1] > 7 || buffer[ISO7816.OFFSET_P2] > 1) {
                ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
            }
    
            // Analyzing the command.
            try {
    
                switch (buffer[ISO7816.OFFSET_INS]) {
    
                case SetKey:
                    SetCryptoKeyAndInitCipher(apdu);
                    break;
    
                case OneKeyDES:
                    OneKeyDESCrypto(apdu);
                    DoEncryptDecrypt(apdu);
                    break;
    
                case TwoKeyDES:
                    TwoKeyDESCrypto(apdu);
                    DoEncryptDecrypt(apdu);
                    break;
    
                case (byte) ThreeKeyDES:
                    ThreeKeyDESCrypto(apdu);
                    DoEncryptDecrypt(apdu);
                    break;
    
                default:
                    ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
    
                }
    
            } catch (Exception e) {
                if (e instanceof CryptoException) {
                    ISOException.throwIt(((CryptoException) e).getReason());
                }
                ISOException.throwIt(ISO7816.SW_UNKNOWN);
    
            }
    
        }
    
        public void SetCryptoKeyAndInitCipher(APDU apdu) throws ISOException {
            byte[] buffer = apdu.getBuffer();
            // Key must has a length of 8, 16 or 24 bytes
            if (buffer[ISO7816.OFFSET_LC] == 8 || buffer[ISO7816.OFFSET_LC] == 16
                    || buffer[ISO7816.OFFSET_LC] == 24) {
                Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, TheDES_Key,
                        (short) 0, ISO7816.OFFSET_LC);
    
                ConfiguredKeyLength = buffer[ISO7816.OFFSET_LC];
    
            } else {
                ISOException.throwIt(ISO7816.SW_DATA_INVALID);
            }
    
            switch (buffer[ISO7816.OFFSET_P1]) {
            case DES_CBC_ISO9797_M1:
                MyCipher.getInstance(Cipher.ALG_DES_CBC_ISO9797_M1, false);
                break;
            case DES_CBC_ISO9797_M2:
                MyCipher.getInstance(Cipher.ALG_DES_CBC_ISO9797_M2, false);
                break;
            case DES_CBC_NOPAD:
                MyCipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);
                break;
            case DES_CBC_PKCS5:
                MyCipher.getInstance(Cipher.ALG_DES_CBC_PKCS5, false);
                break;
            case DES_ECB_ISO9797_M1:
                MyCipher.getInstance(Cipher.ALG_DES_ECB_ISO9797_M1, false);
                break;
            case DES_ECB_ISO9797_M2:
                MyCipher.getInstance(Cipher.ALG_DES_ECB_ISO9797_M2, false);
                break;
            case DES_ECB_NOPAD:
                MyCipher.getInstance(Cipher.ALG_DES_ECB_NOPAD, false);
                break;
            case DES_ECB_PKCS5:
                MyCipher.getInstance(Cipher.ALG_DES_ECB_PKCS5, false);
                break;
    
            }
    
        }
    
        public void OneKeyDESCrypto(APDU apdu) throws ISOException {
            byte[] buffer = apdu.getBuffer();
            // Check to see if the configured key is the required key for this ...
            // ... algorithm or not
            if (ConfiguredKeyLength != 8) {
                ISOException.throwIt(KeyInNotSetGood);
            }
            MyDES1Key.setKey(TheDES_Key, (short) 0);
    
            if (buffer[ISO7816.OFFSET_P2] == 1) {
                MyCipher.init(MyDES1Key, Cipher.MODE_ENCRYPT);
            } else {
                MyCipher.init(MyDES1Key, Cipher.MODE_DECRYPT);
    
            }
    
        }
    
        public void TwoKeyDESCrypto(APDU apdu) throws ISOException {
            byte[] buffer = apdu.getBuffer();
            // Check to see if the configured key is the required key for this ...
            // ... algorithm or not
    
            if (ConfiguredKeyLength != 16) {
                ISOException.throwIt(KeyInNotSetGood);
            }
            MyDES2Key.setKey(TheDES_Key, (short) 0);
    
            if (buffer[ISO7816.OFFSET_P2] == 1) {
                MyCipher.init(MyDES2Key, Cipher.MODE_ENCRYPT);
            } else {
                MyCipher.init(MyDES2Key, Cipher.MODE_DECRYPT);
    
            }
    
        }
    
        public void ThreeKeyDESCrypto(APDU apdu) throws ISOException {
            byte[] buffer = apdu.getBuffer();
            // Check to see if the configured key is the required key for this ...
            // ... algorithm or not
            if (ConfiguredKeyLength != 24) {
                ISOException.throwIt(KeyInNotSetGood);
            }
    
            MyDES3Key.setKey(TheDES_Key, (short) 0);
    
            if (buffer[ISO7816.OFFSET_P2] == 1) {
                MyCipher.init(MyDES3Key, Cipher.MODE_ENCRYPT);
            } else {
                MyCipher.init(MyDES3Key, Cipher.MODE_DECRYPT);
    
            }
    
        }
    
        public void DoEncryptDecrypt(APDU apdu) {
            byte[] buffer = apdu.getBuffer();
    
            byte[] CipheredData = JCSystem.makeTransientByteArray((short) 32,
                    JCSystem.CLEAR_ON_DESELECT);
    
            short datalen = apdu.setIncomingAndReceive();
            if ((datalen % 8) != 0) {
                ISOException.throwIt(ISO7816.SW_DATA_INVALID);
            }
    
            MyCipher.doFinal(buffer, (short) 0, datalen, CipheredData, (short) 0);
            Util.arrayCopyNonAtomic(CipheredData, (short) 0, buffer, (short) 0,
                    datalen);
            apdu.setOutgoingAndSend((short) 0, datalen);
        }
    
    }
    

    运行时输出:

    OSC: opensc-tool.exe -s 00a404000b0102030405060708090000 -s 00c10000081122334455667788 -s 00c20000081011121314151617
    
    Using reader with a card: ACS CCID USB Reader 0
    
    //Selecting the applet
    Sending: 00 A4 04 00 0B 01 02 03 04 05 06 07 08 09 00 00
    Received (SW1=0x90, SW2=0x00)
    
    //Assign 1122334455667788 as the crypto key
    Sending: 00 C1 00 00 08 11 22 33 44 55 66 77 88
    Received (SW1=0x6F, SW2=0x00)
    
    //Request to "encrypt" "1011121314151617" with "DES_CBC_ISO9797_M1"
    Sending: 00 C2 00 00 08 10 11 12 13 14 15 16 17
    Received (SW1=0x6F, SW2=0x00)
    

    问题:

    1. 专业程序员如何编写上述程序以使其更高效,更安全? (任何改进:声明的范围和变量的定义,变量的类型等)
    2. 为什么applet在接收到任何支持的APDU命令时返回错误?
    3. 更新:

      2.1:我的IDE(Eclipse),警告应该以静态方式访问Cipher类型的静态方法getInstance(byte,boolean) 这是什么意思?我为什么要这样做?

      2.2:在上面的程序中,我认为输入数据的长度小于32字节,也是8字节的倍数。如何使长度变量?一种解决方案是使用new关键字,但我认为这是最糟糕的解决方案。有什么建议吗?

1 个答案:

答案 0 :(得分:1)

<强> Q1

StackOverflow并不是一个用于同行代码审查的网站。尽管如此,还是有一些明显的观察结果:

  • 遵循Java代码约定:myCipher而不是MyCipherKEY_IN_NOT_SET_GOOD而不是KeyInNotSetGood等(请参阅https://google-styleguide.googlecode.com/svn/trunk/javaguide.html)。这些规则可以大大提高代码的可读性。
  • 绝不要以非静态方式使用静态方法(使用Cipher.getInstance(...)代替myCipher.getInstance(...))。 (static标记 - https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
  • 使用try-catch块覆盖process方法的全部内容,并处理您捕获的所有异常。然后根据异常的类型和原因设置状态字。否则你只得到6F00,几乎没有任何信息。
  • ConfiguredKeyLength必须存储在RAM中。你的方法会很快破坏卡(每个SELECT重写EEPROM单元)。

<强> Q2

首先,有一个非常常见的错误:

Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, TheDES_Key,
                    (short) 0, ISO7816.OFFSET_LC);

而不是

 Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, TheDES_Key,
                    (short) 0, buffer[ISO7816.OFFSET_LC]);

(是你在一周前在StackOverflow告诉我的那个人,顺便说一下?)

第二个问题:你不打电话

apdu.setIncomingAndReceive();

在触摸APDU缓冲区的数据部分之前。这可能会造成很多麻烦。

第三个问题:以不正确的方式创建Cipher实例。写:

MyCipher = Cipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);

而不是

MyCipher.getInstance(Cipher.ALG_DES_CBC_NOPAD, false);

这就是为什么你得到NullPointerException - MyCipher始终保持null