/* * GidsApplet: A Java Card implementation of the GIDS (Generic Identity * Device Specification) specification * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642100%28v=vs.85%29.aspx * Copyright (C) 2016 Vincent Le Toux(vincent.letoux@mysmartlogon.com) * * It has been based on the IsoApplet * Copyright (C) 2014 Philip Wendland (wendlandphilip@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package com.mysmartlogon.gidsApplet; import javacard.framework.Applet; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.APDU; import javacard.framework.JCSystem; import javacard.framework.SystemException; import javacard.framework.Util; import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.PrivateKey; import javacard.security.PublicKey; import javacard.security.RSAPrivateCrtKey; import javacard.security.RSAPublicKey; import javacardx.crypto.Cipher; import javacard.security.CryptoException; /** * \brief The GidsApplet class. * * */ public class GidsApplet extends Applet { /* API Version */ public static final byte API_VERSION_MAJOR = (byte) 0x00; public static final byte API_VERSION_MINOR = (byte) 0x06; /* Card-specific configuration */ public static final boolean DEF_PRIVATE_KEY_IMPORT_ALLOWED = true; /* ISO constants not in the "ISO7816" interface */ // File system related INS: public static final byte INS_CREATE_FILE = (byte) 0xE0; public static final byte INS_UPDATE_BINARY = (byte) 0xD6; public static final byte INS_READ_BINARY = (byte) 0xB0; public static final byte INS_DELETE_FILE = (byte) 0xE4; // Other INS: public static final byte INS_VERIFY = (byte) 0x20; public static final byte INS_CHANGE_REFERENCE_DATA = (byte) 0x24; public static final byte INS_GENERATE_ASYMMETRIC_KEYPAIR = (byte) 0x47; public static final byte INS_RESET_RETRY_COUNTER = (byte) 0x2C; public static final byte INS_MANAGE_SECURITY_ENVIRONMENT = (byte) 0x22; public static final byte INS_PERFORM_SECURITY_OPERATION = (byte) 0x2A; public static final byte INS_GET_RESPONSE = (byte) 0xC0; public static final byte INS_PUT_DATA = (byte) 0xDB; public static final byte INS_GET_CHALLENGE = (byte) 0x84; public static final byte INS_GENERAL_AUTHENTICATE = (byte) 0x87; public static final byte INS_GET_DATA = (byte) 0xCB; public static final byte INS_ACTIVATE_FILE = (byte) 0x44; public static final byte INS_TERMINATE_DF = (byte) 0xE6; private GidsPINManager pinManager = null; /* Member variables: */ private GidsFileSystem fs = null; private byte[] currentAlgorithmRef; private Object[] currentKey; private TransmitManager transmitManager = null; private Cipher rsaPkcs1Cipher = null; private Cipher rsaOaepCipher = null; private Cipher rsaRawCipher = null; /** * \brief Installs this applet. * * \param bArray * the array containing installation parameters * \param bOffset * the starting offset in bArray * \param bLength * the length in bytes of the parameter data in bArray */ public static void install(byte[] bArray, short bOffset, byte bLength) { new GidsApplet(); } /** * \brief Only this class's install method should create the applet object. */ protected GidsApplet() { // by default the pin manager is in "initialization mode" pinManager = new GidsPINManager(); transmitManager = new TransmitManager(); currentAlgorithmRef = JCSystem.makeTransientByteArray((short)1, JCSystem.CLEAR_ON_DESELECT); currentKey = JCSystem.makeTransientObjectArray((short)1, JCSystem.CLEAR_ON_DESELECT); rsaPkcs1Cipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false); try { rsaOaepCipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1_OAEP, false); } catch (CryptoException e) { if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) { rsaOaepCipher = null; } else { throw e; } } rsaRawCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); byte mechanisms = (byte) 0xC0; fs = new GidsFileSystem(pinManager, transmitManager, (short) 0x3F00, // FCP new byte[] { (byte)0x62, (byte)0x08, (byte)0x82, (byte)0x01, (byte)0x38, // File descriptor byte. (byte)0x8C, (byte)0x03, (byte)0x03, (byte)0x30, (byte)0x30,// security attribute }, // FCI new byte[] { 0x61, 0X12, 0x4F, 0x0B, (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x97, (byte) 0x42, (byte) 0x54, (byte) 0x46, (byte) 0x59, 0x02, 0x01, // AID 0x73, 0x03, 0x40, 0x01, mechanisms, // cryptographic mechanism }, // FMD new byte[] { (byte)0x64, (byte)0x09, (byte)0x5F, (byte)0x2F, (byte) 0x01, (byte) 0x60, // pin usage policy (byte)0x7F, (byte)0x65, 0x02, (byte) 0x80, 0x00 } ); // FCI / FMD / FCP are hard coded register(); } /** * \brief This method is called whenever the applet is being deselected. */ public void deselect() { pinManager.DeauthenticateAllPin(); } /** * \brief Processes an incoming APDU. * * \see APDU. * * \param apdu The incoming APDU. */ public void process(APDU apdu) { byte buffer[] = apdu.getBuffer(); byte ins = buffer[ISO7816.OFFSET_INS]; // No secure messaging at the moment if((buffer[ISO7816.OFFSET_CLA] & 0x0C) != 0) { ISOException.throwIt(ISO7816.SW_SECURE_MESSAGING_NOT_SUPPORTED); } transmitManager.processChainInitialization(apdu); if((buffer[ISO7816.OFFSET_CLA] & 0xE0) == 0) { switch (ins) { case INS_ACTIVATE_FILE: fs.processActivateFile(apdu); break; case INS_CREATE_FILE: fs.processCreateFile(apdu); break; case INS_CHANGE_REFERENCE_DATA: pinManager.processChangeReferenceData(apdu); break; case INS_DELETE_FILE: fs.processDeleteFile(apdu); break; case INS_GENERAL_AUTHENTICATE: pinManager.processGeneralAuthenticate(apdu); break; case INS_GENERATE_ASYMMETRIC_KEYPAIR: processGenerateAsymmetricKeypair(apdu); break; case INS_GET_DATA: processGetData(apdu); break; case INS_GET_RESPONSE: transmitManager.processGetResponse(apdu); break; case INS_MANAGE_SECURITY_ENVIRONMENT: processManageSecurityEnvironment(apdu); break; case INS_PERFORM_SECURITY_OPERATION: processPerformSecurityOperation(apdu); break; case INS_PUT_DATA: processPutData(apdu); break; case INS_RESET_RETRY_COUNTER: pinManager.processResetRetryCounter(apdu); break; case ISO7816.INS_SELECT: fs.processSelectFile(apdu, selectingApplet()); break; case INS_TERMINATE_DF: processTerminateDF(apdu); break; case INS_VERIFY: pinManager.processVerify(apdu); break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } // switch } else { ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); } } private void processTerminateDF(APDU apdu) { byte[] buf = apdu.getBuffer(); byte p1 = buf[ISO7816.OFFSET_P1]; byte p2 = buf[ISO7816.OFFSET_P2]; if (p1 != (byte) 0x00 || p2 != (byte) 0x00 ) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } fs.CheckPermission(pinManager, File.ACL_OP_DF_TERMINATE); // kill me fs.setState(File.STATE_TERMINATED); } /** * \brief Process the GET DATA apdu (INS = CA) * * This APDU can be used to request the following data: * P1P2 = 0x1001: Applet version and features * * \param apdu The apdu to process. */ private void processGetData(APDU apdu) throws ISOException { byte[] buf = apdu.getBuffer(); byte p1 = buf[ISO7816.OFFSET_P1]; byte p2 = buf[ISO7816.OFFSET_P2]; short lc; if (p1 == 0x3F && p2 == (byte) 0xFF) { // get Applet information // Bytes received must be Lc. lc = apdu.setIncomingAndReceive(); // check for public key request // typically 00 CB 3F FF 0A 70 08 84 01 **81** A5 03 7F 49 80 00 (*keyref*) if (lc == (short) 10 && buf[5] == (byte) 0x70) { CRTKeyFile file = null; byte keyID = buf[9]; try { file = fs.findKeyCRT(keyID); } catch (NotFoundException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } file.CheckPermission(pinManager, File.ACL_OP_KEY_GETPUBLICKEY); PublicKey pk = file.GetKey().getPublic(); // Return pubkey. See ISO7816-8 table 3. try { sendPublicKey(apdu, pk); } catch (InvalidArgumentsException e) { ISOException.throwIt(ISO7816.SW_UNKNOWN); } catch (NotEnoughSpaceException e) { ISOException.throwIt(ISO7816.SW_FILE_FULL); } } else if (lc == (short) 04 && buf[5] == (byte) 0x5C && buf[6] == (byte) 0x02) { short id = Util.makeShort(buf[7], buf[8]); if (id == (short) 0x7F71 || id == (short) 0x7F72 || id == (short) 0x7F73 ) { pinManager.returnPINStatus(apdu, id); } else { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } else { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } else if (p1 == 0x2F && p2 == (byte) 0x01) { // EF.ATR lc = apdu.setIncomingAndReceive(); // check for EF.ATR request // 00 CB 2F 01 02 5C 00 00 if (lc == (short) 2 && buf[5] == (byte) 0x5C && buf[6] == (byte) 0x00) { // 43 01 F4 47 03 08 01 80 46 0C 4D 79 53 6D 61 72 74 4C 6F 67 6F 6E buf[0] = (byte) 0x43; buf[1] = (byte) 0x01; buf[2] = (byte) 0xF4; buf[3] = (byte) 0x47; buf[4] = (byte) 0x03; buf[5] = (byte) 0x08; buf[6] = (byte) 0x01; buf[7] = (byte) 0x80; buf[8] = (byte) 0x46; buf[9] = (byte) 0x0C; buf[10] = (byte) 0x4D; buf[11] = (byte) 0x79; buf[12] = (byte) 0x53; buf[13] = (byte) 0x6D; buf[14] = (byte) 0x61; buf[15] = (byte) 0x72; buf[16] = (byte) 0x74; buf[17] = (byte) 0x4C; buf[18] = (byte) 0x6F; buf[19] = (byte) 0x67; buf[20] = (byte) 0x6F; buf[21] = (byte) 0x6E; apdu.setOutgoing(); apdu.setOutgoingLength((short)22); apdu.sendBytes((short) 0, (short) 22); } else { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } else { // read BER TLV DO fs.processGetData(apdu); } } /** * \brief Process the GENERATE ASYMMETRIC KEY PAIR apdu (INS = 46). * * A MANAGE SECURITY ENVIRONMENT must have succeeded earlier to set parameters for key * generation. * * \param apdu The apdu. * * \throw ISOException SW_WRONG_LENGTH, SW_INCORRECT_P1P2, SW_CONDITIONS_NOT_SATISFIED, * SW_SECURITY_STATUS_NOT_SATISFIED. */ public void processGenerateAsymmetricKeypair(APDU apdu) throws ISOException { byte[] buf = apdu.getBuffer(); byte p1 = buf[ISO7816.OFFSET_P1]; byte p2 = buf[ISO7816.OFFSET_P2]; short lc, pos, len, innerOffset, innerLength; byte algID=0, keyID=0; CRTKeyFile file = null; KeyPair kp = null; // Check INS: We only support INS=D6 at the moment. if (p1 != (byte) 0x00 || p2 != (byte) 0x00 ) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } // Bytes received must be Lc. lc = apdu.setIncomingAndReceive(); // TLV structure consistency check. if( ! UtilTLV.isTLVconsistent(buf, ISO7816.OFFSET_CDATA, lc)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } // length and length-field of outer FCI tag consistency check. try { innerLength = UtilTLV.decodeLengthField(buf, (short)(ISO7816.OFFSET_CDATA+1)); if(innerLength != (short)(lc-1-UtilTLV.getLengthFieldLength(buf, (short)(ISO7816.OFFSET_CDATA+1)))) { throw InvalidArgumentsException.getInstance(); } // Let innerOffset point to the first inner TLV entry. innerOffset = (short) (ISO7816.OFFSET_CDATA + 1 + UtilTLV.getLengthFieldLength(buf, (short)(ISO7816.OFFSET_CDATA+1))); // Now we check for the consistency of the lower level TLV entries. if( ! UtilTLV.isTLVconsistent(buf, innerOffset, innerLength) ) { throw InvalidArgumentsException.getInstance(); } pos = UtilTLV.findTag(buf, innerOffset, innerLength, (byte) 0x83); len = UtilTLV.decodeLengthField(buf, (short)(pos+1)); if (len != (short) 1) { throw InvalidArgumentsException.getInstance(); } keyID = buf[(short)(pos+2)]; pos = UtilTLV.findTag(buf, innerOffset, innerLength, (byte) 0x80); len = UtilTLV.decodeLengthField(buf, (short)(pos+1)); if (len != (short) 1) { throw InvalidArgumentsException.getInstance(); } algID = buf[(short)(pos+2)]; } catch (InvalidArgumentsException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } catch (NotFoundException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try { file = fs.findKeyCRT(keyID); } catch (NotFoundException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } file.CheckPermission(pinManager, File.ACL_OP_KEY_GENERATE_ASYMETRIC); try { switch(algID) { case (byte)0x06: kp = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_1024); break; case (byte)0x07: kp = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_2048); break; default: ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); break; } kp.genKeyPair(); // special Feitian workaround for A40CR and A22CR cards RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) kp.getPrivate(); short pLen = priKey.getP(buf, (short) 0); priKey.setP(buf, (short) 0, pLen); short qLen = priKey.getQ(buf, (short) 0); priKey.setQ(buf, (short) 0, qLen); // end of workaround } catch(CryptoException e) { if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) { ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } ISOException.throwIt(ISO7816.SW_UNKNOWN); } catch(SystemException e) { if(e.getReason() == SystemException.NO_RESOURCE) { ISOException.throwIt(ISO7816.SW_FILE_FULL); } ISOException.throwIt(ISO7816.SW_UNKNOWN); } file.SaveKey(kp); // Return pubkey. See ISO7816-8 table 3. try { sendPublicKey(apdu, kp.getPublic()); } catch (InvalidArgumentsException e) { ISOException.throwIt(ISO7816.SW_UNKNOWN); } catch (NotEnoughSpaceException e) { ISOException.throwIt(ISO7816.SW_FILE_FULL); } } private void sendPublicKey(APDU apdu, PublicKey publicKey) throws InvalidArgumentsException, NotEnoughSpaceException { if (publicKey instanceof RSAPublicKey) { sendRSAPublicKey(apdu, (RSAPublicKey) publicKey); } } /** * \brief Encode a 2048 bit RSAPublicKey according to ISO7816-8 table 3 and send it as a response, * using an extended APDU. * * \see ISO7816-8 table 3. * * \param apdu The apdu to answer. setOutgoing() must not be called already. * * \param key The RSAPublicKey to send. * Can be null for the secound part if there is no support for extended apdus. */ private void sendRSAPublicKey(APDU apdu, RSAPublicKey key) { short pos = 0; short size = key.getSize(); byte[] ram_buf = transmitManager.GetRamBuffer(); transmitManager.ClearRamBuffer(); ram_buf[pos++] = (byte) 0x7F; // Interindustry template for nesting one set of public key data objects. ram_buf[pos++] = (byte) 0x49; // " if (size < (short) 2048) { ram_buf[pos++] = (byte) 0x81; // Length field: 2 Bytes. ram_buf[pos++] = (byte) ((size / 8) + 8); } else { ram_buf[pos++] = (byte) 0x82; // Length field: 3 Bytes. Util.setShort(ram_buf, pos, (short)((size / 8) + 9)); pos += 2; } ram_buf[pos++] = (byte) 0x81; // RSA public key modulus tag. if (size < (short) 2048) { ram_buf[pos++] = (byte) 0x81; // Length field: 2 Bytes. ram_buf[pos++] = (byte) (size / 8); } else { ram_buf[pos++] = (byte) 0x82; // Length field: 3 Bytes. Util.setShort(ram_buf, pos, (short)(size / 8)); pos += 2; } pos += key.getModulus(ram_buf, pos); ram_buf[pos++] = (byte) 0x82; // RSA public key exponent tag. ram_buf[pos++] = (byte) 0x03; // Length: 3 Bytes. pos += key.getExponent(ram_buf, pos); transmitManager.sendDataFromRamBuffer(apdu, (short)0, pos); } /** * \brief Process the MANAGE SECURITY ENVIRONMENT apdu (INS = 22). * * \attention Only SET is supported. RESTORE will reset the security environment. * The security environment will be cleared upon deselection of the applet. * STOREing and ERASEing of security environments is not supported. * * \param apdu The apdu. * * \throw ISOException SW_SECURITY_STATUS_NOT_SATISFIED, SW_WRONG_LENGTH, SW_DATA_INVALID, * SW_INCORRECT_P1P2, SW_FUNC_NOT_SUPPORTED, SW_COMMAND_NOT_ALLOWED. */ public void processManageSecurityEnvironment(APDU apdu) throws ISOException { byte[] buf = apdu.getBuffer(); byte p1 = buf[ISO7816.OFFSET_P1]; byte p2 = buf[ISO7816.OFFSET_P2]; short lc; short pos = 0; byte algRef = 0; byte privKeyRef = -1; CRTKeyFile crt = null; // Bytes received must be Lc. lc = apdu.setIncomingAndReceive(); // TLV structure consistency check. if( ! UtilTLV.isTLVconsistent(buf, ISO7816.OFFSET_CDATA, lc)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } /* Extract data: */ switch(p1) { case (byte) 0x81: // SET Verification, encipherment, external authentication and key agreement. case (byte) 0xC1: // Private key reference (Index in keys[]-array). try { pos = UtilTLV.findTag(buf, ISO7816.OFFSET_CDATA, (byte) lc, (byte) 0x83); } catch (Exception e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if(buf[++pos] != (byte) 0x01 ) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } privKeyRef = buf[++pos]; algRef = (byte) 0x02; break; case (byte) 0x41: // SET Computation, decipherment, internal authentication and key agreement. // Algorithm reference. try { pos = UtilTLV.findTag(buf, ISO7816.OFFSET_CDATA, (byte) lc, (byte) 0x80); } catch (NotFoundException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } catch (InvalidArgumentsException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if(buf[++pos] != (byte) 0x01) { // Length must be 1. ISOException.throwIt(ISO7816.SW_DATA_INVALID); } // Set the current algorithm reference. algRef = buf[++pos]; // Private key reference (Index in keys[]-array). try { pos = UtilTLV.findTag(buf, ISO7816.OFFSET_CDATA, (byte) lc, (byte) 0x84); } catch (Exception e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if(buf[++pos] != (byte) 0x01) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } privKeyRef = buf[++pos]; break; case (byte) 0xF3: // RESTORE // Set sec env constants to default values. algRef = 0; privKeyRef = -1; break; case (byte) 0xF4: // ERASE case (byte) 0xF2: // STORE default: ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } if(privKeyRef == -1) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try { crt = fs.findKeyCRT(privKeyRef); } catch (NotFoundException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try { crt.CheckUsage(p2, algRef); } catch (NotFoundException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } crt.CheckPermission(pinManager, File.ACL_OP_KEY_MANAGE_SEC_ENV); if (p1 == (byte) 0xc1 || p1 == (byte) 0x81) { pinManager.SetKeyReference(crt); } else { pinManager.SetKeyReference(null); } // Finally, update the security environment. currentAlgorithmRef[0] = algRef; currentKey[0] = crt; } /** * \brief Process the PERFORM SECURITY OPERATION apdu (INS=2A). * * This operation is used for cryptographic operations * (Computation of digital signatures, decrypting.). * * \param apdu The PERFORM SECURITY OPERATION apdu. * * \throw ISOException SW_SECURITY_STATUS_NOT_SATISFIED, SW_INCORRECT_P1P2 and * the ones from computeDigitalSignature() and decipher(). */ private void processPerformSecurityOperation(APDU apdu) throws ISOException { byte[] buf = apdu.getBuffer(); byte p1 = buf[ISO7816.OFFSET_P1]; byte p2 = buf[ISO7816.OFFSET_P2]; if(p1 == (byte) 0x9E && p2 == (byte) 0x9A) { computeDigitalSignature(apdu); } else if(p1 == (byte) 0x80 && p2 == (byte) 0x86) { decipher(apdu); } else { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } } /** * \brief Decipher the data from the apdu using the private key referenced by * an earlier MANAGE SECURITY ENVIRONMENT apdu. * * \param apdu The PERFORM SECURITY OPERATION apdu with P1=80 and P2=86. * * \throw ISOException SW_CONDITIONS_NOT_SATISFIED, SW_WRONG_LENGTH and * SW_WRONG_DATA */ private void decipher(APDU apdu) { byte[] buf = apdu.getBuffer(); short offset_cdata; short lc; short decLen = -1; byte[] ram_buf = transmitManager.GetRamBuffer(); Cipher cipher = null; lc = transmitManager.doChainingOrExtAPDU(apdu); offset_cdata = 0; // Padding indicator should be "No further indication". if(buf[offset_cdata] != (byte) 0x00) { ISOException.throwIt(ISO7816.SW_WRONG_DATA); } switch((byte) (currentAlgorithmRef[0] & 0xF0)) { case (byte) 0x80: cipher = rsaOaepCipher; break; case (byte) 0x40: cipher = rsaPkcs1Cipher; break; case (byte) 0x00: cipher = rsaRawCipher; break; default: ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } // Get the key - it must be an RSA private key, // checks have been done in MANAGE SECURITY ENVIRONMENT. CRTKeyFile key = (CRTKeyFile) currentKey[0]; PrivateKey theKey = key.GetKey().getPrivate(); // Check the length of the cipher. // Note: The first byte of the data field is the padding indicator // and therefor not part of the ciphertext. if(lc != (short)(theKey.getSize() / 8)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } cipher.init(theKey, Cipher.MODE_DECRYPT); try { decLen = cipher.doFinal(ram_buf, (short) 0, lc, buf, (short) 0); } catch(CryptoException e) { ISOException.throwIt(ISO7816.SW_WRONG_DATA); } // We have to send at most 256 bytes. A short APDU can handle that - only one send operation neccessary. apdu.setOutgoingAndSend((short)0, decLen); } /** * \brief Compute a digital signature of the data from the apdu * using the private key referenced by an earlier * MANAGE SECURITY ENVIRONMENT apdu. * * \attention The apdu should contain a hash, not raw data for RSA keys. * PKCS1 padding will be applied if neccessary. * * \param apdu The PERFORM SECURITY OPERATION apdu with P1=9E and P2=9A. * * \throw ISOException SW_CONDITIONS_NOT_SATISFIED, SW_WRONG_LENGTH * and SW_UNKNOWN. */ private void computeDigitalSignature(APDU apdu) throws ISOException { byte[] buf = apdu.getBuffer(); short lc, le; short sigLen = 0; PrivateKey rsaKey = null; byte[] ram_buf = transmitManager.GetRamBuffer(); CRTKeyFile key = (CRTKeyFile) currentKey[0]; switch((byte) (currentAlgorithmRef[0] & 0xF0)) { case (byte) 0x10: // padding made off card -> raw encryption to be performed lc = transmitManager.doChainingOrExtAPDU(apdu); // RSA signature operation. rsaKey = key.GetKey().getPrivate(); rsaRawCipher.init(rsaKey, Cipher.MODE_ENCRYPT); sigLen = rsaRawCipher.doFinal(ram_buf, (short) 0, lc, ram_buf, (short)0); // A single short APDU can handle 256 bytes - only one send operation neccessary. le = apdu.setOutgoing(); if(le > 0 && le < sigLen) { ISOException.throwIt(ISO7816.SW_CORRECT_LENGTH_00); } apdu.setOutgoingLength(sigLen); apdu.sendBytesLong(ram_buf, (short) 0, sigLen); break; case (byte) 0x50: // rsa padding made by the card, only the hash is provided // Receive. // Bytes received must be Lc. lc = apdu.setIncomingAndReceive(); // RSA signature operation. rsaKey = key.GetKey().getPrivate(); if(lc > (short) 247) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } rsaPkcs1Cipher.init(rsaKey, Cipher.MODE_ENCRYPT); sigLen = rsaPkcs1Cipher.doFinal(buf, ISO7816.OFFSET_CDATA, lc, ram_buf, (short)0); /*if(sigLen != 256) { ISOException.throwIt(ISO7816.SW_UNKNOWN); }*/ // A single short APDU can handle 256 bytes - only one send operation neccessary. le = apdu.setOutgoing(); if(le > 0 && le < sigLen) { ISOException.throwIt(ISO7816.SW_CORRECT_LENGTH_00); } apdu.setOutgoingLength(sigLen); apdu.sendBytesLong(ram_buf, (short) 0, sigLen); break; default: // Wrong/unknown algorithm. ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } } /** * \brief Process the PUT DATA apdu (INS=DB). * * PUT DATA is currently used for private key import. * * \throw ISOException SW_SECURITY_STATUS_NOT_SATISFIED, SW_INCORRECT_P1P2 */ private void processPutData(APDU apdu) throws ISOException { byte[] buf = apdu.getBuffer(); byte p1 = buf[ISO7816.OFFSET_P1]; byte p2 = buf[ISO7816.OFFSET_P2]; if(p1 == (byte) 0x3F && p2 == (byte) 0xFF) { importPrivateKey(apdu); } else { fs.processPutData(apdu); } } /** * \brief Upload and import a usable private key. * * A preceeding MANAGE SECURITY ENVIRONMENT is necessary (like with key-generation). * The format of the data (of the apdu) must be BER-TLV, * Tag 7F48 ("T-L pair to indicate a private key data object") for RSA or tag 0xC1 * for EC keys, containing the point Q. * * For RSA, the data to be submitted is quite large. It is required that command chaining is * used for the submission of the private key. One chunk of the chain (one apdu) must contain * exactly one tag (0x92 - 0x96). The first apdu of the chain must contain the outer tag (7F48). * * \throw ISOException SW_SECURITY_STATUS_NOT_SATISFIED, SW_DATA_INVALID, SW_WRONG_LENGTH. */ private void importPrivateKey(APDU apdu) throws ISOException { short recvLen; short len = 0, pos = 0; short innerPos = 0, innerLen = 0; byte[] flash_buf = null; byte privKeyRef = -1; CRTKeyFile crt = null; if( ! DEF_PRIVATE_KEY_IMPORT_ALLOWED) { ISOException.throwIt(ErrorCode.SW_COMMAND_NOT_ALLOWED_GENERAL); } try { // flash buffer is allocated in the next instruction recvLen = transmitManager.doChainingOrExtAPDUFlash(apdu); // if these 2 lines are reversed, flash_buf can be null flash_buf = transmitManager.GetFlashBuffer(); try { innerPos = UtilTLV.findTag(flash_buf, (short) 0, recvLen, (byte) 0x70); innerLen = UtilTLV.decodeLengthField(flash_buf, (short)(innerPos+1)); innerPos += 1 + UtilTLV.getLengthFieldLength(flash_buf, (short)(innerPos+1)); } catch (Exception e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try { pos = UtilTLV.findTag(flash_buf, innerPos, innerLen, (byte) 0x84); len = UtilTLV.decodeLengthField(flash_buf, (short)(innerPos+1)); if (len != 1) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } privKeyRef = flash_buf[(short) (pos+2)]; } catch (Exception e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try { pos = UtilTLV.findTag(flash_buf, innerPos, innerLen, (byte) 0xA5); len = UtilTLV.decodeLengthField(flash_buf, (short)(innerPos+1)); } catch (Exception e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if(privKeyRef == -1) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try { crt = fs.findKeyCRT(privKeyRef); } catch (NotFoundException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } crt.CheckPermission(pinManager, File.ACL_OP_KEY_PUTKEY); try { crt.importKey(flash_buf, pos, len); } catch (InvalidArgumentsException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } // clear ressource and avoid leaking a private key in flash (if the private key is deleted after) transmitManager.ClearFlashBuffer(); } catch(ISOException e) { if (e.getReason() != ISO7816.SW_NO_ERROR) { // clear ressource and avoid leaking a private key in flash (if the private key is deleted after) transmitManager.ClearFlashBuffer(); } throw e; } } } // class GidsApplet