    001    /* Copyright  (c) 2002 Graz University of Technology. All rights reserved.
    002     *
    003     * Redistribution and use in  source and binary forms, with or without
    004     * modification, are permitted  provided that the following conditions are met:
    005     *
    006     * 1. Redistributions of  source code must retain the above copyright notice,
    007     *    this list of conditions and the following disclaimer.
    008     *
    009     * 2. Redistributions in  binary form must reproduce the above copyright notice,
    010     *    this list of conditions and the following disclaimer in the documentation
    011     *    and/or other materials provided with the distribution.
    012     *
    013     * 3. The end-user documentation included with the redistribution, if any, must
    014     *    include the following acknowledgment:
    015     *
    016     *    "This product includes software developed by IAIK of Graz University of
    017     *     Technology."
    018     *
    019     *    Alternately, this acknowledgment may appear in the software itself, if
    020     *    and wherever such third-party acknowledgments normally appear.
    021     *
    022     * 4. The names "Graz University of Technology" and "IAIK of Graz University of
    023     *    Technology" must not be used to endorse or promote products derived from
    024     *    this software without prior written permission.
    025     *
    026     * 5. Products derived from this software may not be called
    027     *    "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior
    028     *    written permission of Graz University of Technology.
    029     *
    030     *  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
    031     *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    032     *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    033     *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE
    034     *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
    035     *  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    036     *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
    037     *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    038     *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    039     *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    040     *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    041     *  POSSIBILITY  OF SUCH DAMAGE.
    042     */
    043
    044    package demo.pkcs.pkcs11;
    045
    046    import java.io.BufferedReader;
    047    import java.io.ByteArrayOutputStream;
    048    import java.io.FileInputStream;
    049    import java.io.FileOutputStream;
    050    import java.io.InputStream;
    051    import java.io.InputStreamReader;
    052    import java.io.PrintWriter;
    053    import java.util.Arrays;
    054    import java.util.HashMap;
    055    import java.util.Iterator;
    056    import java.util.List;
    057    import java.util.Map;
    058    import java.util.Vector;
    059
    060    import iaik.pkcs.pkcs11.Info;
    061    import iaik.pkcs.pkcs11.Mechanism;
    062    import iaik.pkcs.pkcs11.MechanismInfo;
    063    import iaik.pkcs.pkcs11.Module;
    064    import iaik.pkcs.pkcs11.Session;
    065    import iaik.pkcs.pkcs11.Slot;
    066    import iaik.pkcs.pkcs11.Token;
    067    import iaik.pkcs.pkcs11.TokenInfo;
    068    import iaik.pkcs.pkcs11.objects.DES3SecretKey;
    069    import iaik.pkcs.pkcs11.objects.Object;
    070    import iaik.pkcs.pkcs11.parameters.InitializationVectorParameters;
    071
    072
    073
    074    /**
    075     * This demo program uses a PKCS#11 module to encrypt a given file using
    076     * Triple DES.
    077     *
    078     * @author <a href="mailto:Karl.Scheibelhofer@iaik.at"> Karl Scheibelhofer </a>
    079     * @version 0.1
    080     * @invariants
    081     */
    082    public class TripleDESEncrypt {
    083
    084      static PrintWriter output_;
    085
    086      static BufferedReader input_;
    087
    088      static {
    089        try {
    090          //output_ = new PrintWriter(new FileWriter("GetInfo_output.txt"), true);
    091          output_ = new PrintWriter(System.out, true);
    092          input_ = new BufferedReader(new InputStreamReader(System.in));
    093        } catch (Throwable thr) {
    094          thr.printStackTrace();
    095          output_ = new PrintWriter(System.out, true);
    096          input_ = new BufferedReader(new InputStreamReader(System.in));
    097        }
    098      }
    099
    100      public static void main(String[] args) {
    101        if (args.length != 3) {
    102          printUsage();
    103          System.exit(1);
    104        }
    105
    106        try {
    107            // Security.addProvider(new IAIK());
    108
    109            output_.println("################################################################################");
    110            output_.println("load and initialize module: " + args[0]);
    111            Module pkcs11Module = Module.getInstance(args[0]);
    112            pkcs11Module.initialize(null);
    113
    114            Info info = pkcs11Module.getInfo();
    115            output_.println(info);
    116            output_.println("################################################################################");
    117
    118
    119            output_.println("################################################################################");
    120            output_.println("getting list of all tokens");
    121            Slot[] slotsWithToken = pkcs11Module.getSlotList(Module.SlotRequirement.TOKEN_PRESENT);
    122            Token[] tokens = new Token[slotsWithToken.length];
    123            Map tokenIDtoToken = new HashMap(tokens.length);
    124
    125            for (int i = 0; i < slotsWithToken.length; i++) {
    126              output_.println("________________________________________________________________________________");
    127              tokens[i] = slotsWithToken[i].getToken();
    128              TokenInfo tokenInfo = tokens[i].getTokenInfo();
    129              long tokenID = tokens[i].getTokenID();
    130              tokenIDtoToken.put(new Long(tokenID), tokens[i]);
    131              output_.println("Token ID: " + tokenID);
    132              output_.println(tokenInfo);
    133              output_.println("________________________________________________________________________________");
    134            }
    135            output_.println("################################################################################");
    136
    137            output_.println("################################################################################");
    138            Token token = null;
    139            Long selectedTokenID = null;
    140            if (tokens.length == 0) {
    141              output_.println("There is no slot with a present token.");
    142              output_.flush();
    143              System.exit(0);
    144            } else if (tokens.length == 1) {
    145              output_.println("Taking token with ID: " + tokens[0].getTokenID());
    146              selectedTokenID = new Long(tokens[0].getTokenID());
    147              token = tokens[0];
    148            } else {
    149              boolean gotTokenID = false;
    150              while (!gotTokenID) {
    151                output_.print("Enter the ID of the token to use or 'x' to exit: ");
    152                output_.flush();
    153                String tokenIDstring = input_.readLine();
    154                if (tokenIDstring.equalsIgnoreCase("x")) {
    155                  output_.flush();
    156                  System.exit(0);
    157                }
    158                try {
    159                  selectedTokenID = new Long(tokenIDstring);
    160                  token = (Token) tokenIDtoToken.get(selectedTokenID);
    161                  if (token != null) {
    162                    gotTokenID = true;
    163                  } else {
    164                    output_.println("A token with the entered ID \"" + tokenIDstring + "\" does not exist. Try again.");
    165                  }
    166                } catch (NumberFormatException ex) {
    167                  output_.println("The entered ID \"" + tokenIDstring + "\" is invalid. Try again.");
    168                }
    169              }
    170            }
    171
    172            Session session =
    173                token.openSession(Token.SessionType.SERIAL_SESSION, Token.SessionReadWriteBehavior.RO_SESSION, null, null);
    174
    175            TokenInfo tokenInfo = token.getTokenInfo();
    176            if (tokenInfo.isLoginRequired()) {
    177              if (tokenInfo.isProtectedAuthenticationPath()) {
    178                output_.print("Please enter the user PIN at the PIN-pad of your reader.");
    179                session.login(Session.UserType.USER, null); // the token prompts the PIN by other means; e.g. PIN-pad
    180              } else {
    181                output_.print("Enter user-PIN and press [return key]: ");
    182                output_.flush();
    183                String userPINString = input_.readLine();
    184                session.login(Session.UserType.USER, userPINString.toCharArray());
    185              }
    186            }
    187            output_.println("################################################################################");
    188
    189            List supportedMechanisms = Arrays.asList(token.getMechanismList());
    190            MechanismInfo des3CbcMechanismInfo = null;
    191            if (!supportedMechanisms.contains(Mechanism.DES3_CBC_PAD)) {
    192              output_.print("This token does not support Tripple DES!");
    193              output_.flush();
    194              System.exit(0);
    195            } else {
    196              des3CbcMechanismInfo = token.getMechanismInfo(Mechanism.DES3_CBC_PAD);
    197              if (!des3CbcMechanismInfo.isEncrypt()) {
    198                output_.print("This token does not support Tripple DES for encryption!");
    199                output_.flush();
    200                System.exit(0);
    201              }
    202            }
    203
    204            output_.println("################################################################################");
    205            output_.println("searching for Tripple DES encryption keys");
    206
    207            List encryptionKeyList = new Vector(4);
    208
    209            // first we search for secret keys that we can use for encryption
    210            DES3SecretKey secretEncryptionKeyTemplate = new DES3SecretKey();
    211            secretEncryptionKeyTemplate.getEncrypt().setBooleanValue(Boolean.TRUE);
    212
    213            session.findObjectsInit(secretEncryptionKeyTemplate);
    214            Object[] secretEncryptionKeys = session.findObjects(1);
    215
    216            while (secretEncryptionKeys.length > 0) {
    217              encryptionKeyList.add(secretEncryptionKeys[0]);
    218              secretEncryptionKeys = session.findObjects(1);
    219            }
    220            session.findObjectsFinal();
    221
    222            DES3SecretKey selectedEncryptionKey = null;
    223            if (encryptionKeyList.size() == 0) {
    224              if (supportedMechanisms.contains(Mechanism.DES3_KEY_GEN)) {
    225                output_.println("Found NO Tripple DES key that can be used for encryption.");
    226                output_.print("Do you want to generate a temporal session key? (y/n) ");
    227                output_.flush();
    228
    229                String mechanismNameString = input_.readLine();
    230                if (mechanismNameString.equalsIgnoreCase("y")) {
    231                  Mechanism keyGenerationMechanism = (Mechanism) Mechanism.DES3_KEY_GEN.clone();
    232
    233                  DES3SecretKey secretKeyTemplate = new DES3SecretKey();
    234                  secretKeyTemplate.getEncrypt().setBooleanValue(Boolean.TRUE);
    235                  secretKeyTemplate.getDecrypt().setBooleanValue(Boolean.TRUE);
    236                  // we only have a read-only session, thus we only create a session object
    237                  secretKeyTemplate.getToken().setBooleanValue(Boolean.FALSE);
    238
    239                  selectedEncryptionKey = (DES3SecretKey) session.generateKey(keyGenerationMechanism, secretKeyTemplate);
    240                } else {
    241                  output_.flush();
    242                  System.exit(0);
    243                }
    244              } else {
    245                output_.println("Found NO Tripple DES key that can be used for encryption.");
    246                output_.println("This token does not support generation of Tripple DES keys.");
    247                output_.flush();
    248                System.exit(0);
    249              }
    250            } else {
    251              output_.println("found these Tripple DES encryption keys:");
    252              Map objectHandleToObjectMap = new HashMap(encryptionKeyList.size());
    253              Iterator encryptionKeyListIterator = encryptionKeyList.iterator();
    254              while (encryptionKeyListIterator.hasNext()) {
    255                Object encryptionKey = (Object) encryptionKeyListIterator.next();
    256                long objectHandle = encryptionKey.getObjectHandle();
    257                objectHandleToObjectMap.put(new Long(objectHandle), encryptionKey);
    258                output_.println("________________________________________________________________________________");
    259                output_.println("Object with handle: " + objectHandle);
    260                output_.println(encryptionKey);
    261                output_.println("________________________________________________________________________________");
    262              }
    263
    264              boolean gotObjectHandle = false;
    265              Long selectedObjectHandle;
    266              while (!gotObjectHandle) {
    267                output_.print("Enter the handle of the key to use for encryption or 'x' to exit: ");
    268                output_.flush();
    269                String objectHandleString = input_.readLine();
    270                if (objectHandleString.equalsIgnoreCase("x")) {
    271                  System.exit(0);
    272                }
    273                try {
    274                  selectedObjectHandle = new Long(objectHandleString);
    275                  selectedEncryptionKey = (DES3SecretKey) objectHandleToObjectMap.get(selectedObjectHandle);
    276                  if (selectedEncryptionKey != null) {
    277                    gotObjectHandle = true;
    278                  } else {
    279                    output_.println("An object with the handle \"" + objectHandleString + "\" does not exist. Try again.");
    280                  }
    281                } catch (NumberFormatException ex) {
    282                  output_.println("The entered handle \"" + objectHandleString + "\" is invalid. Try again.");
    283                }
    284              }
    285            }
    286
    287            output_.println("################################################################################");
    288
    289            output_.println("################################################################################");
    290            output_.println("encrypting data from file: " + args[1]);
    291
    292            InputStream dataInputStream = new FileInputStream(args[1]);
    293
    294            /* we buffer all data in memory that we can use encrypt(byte[]) instad of several
    295             * subsequent encryptUpdate(byte[]) calls, because many tokens do not support this
    296             */
    297
    298            byte[] dataBuffer = new byte[1024];
    299            int bytesRead;
    300            ByteArrayOutputStream streamBuffer = new ByteArrayOutputStream();
    301
    302            // feed in all data from the input stream
    303            while ((bytesRead = dataInputStream.read(dataBuffer)) >= 0) {
    304              streamBuffer.write(dataBuffer, 0, bytesRead);
    305            }
    306            Arrays.fill(dataBuffer, (byte) 0); // ensure that no data is left in the memory
    307            streamBuffer.flush();
    308            streamBuffer.close();
    309            dataInputStream.close();
    310            byte[] rawData = streamBuffer.toByteArray();
    311
    312            Mechanism selectedMechanism = (Mechanism) Mechanism.DES3_CBC_PAD.clone();
    313
    314            byte[] encryptInitializationVector = { 0, 0, 0, 0, 0, 0, 0, 0}; // use random value
    315            InitializationVectorParameters encryptInitializationVectorParameters =
    316                new InitializationVectorParameters(encryptInitializationVector);
    317            selectedMechanism.setParameters(encryptInitializationVectorParameters);
    318
    319            output_.print("encrypting the data... ");
    320
    321            // initialize for encryption
    322            session.encryptInit(selectedMechanism, selectedEncryptionKey);
    323
    324            byte[] encryptedData = session.encrypt(rawData);
    325
    326            output_.println("finished");
    327
    328            output_.print("writing encrypted data to file \"" + args[2] + "\"...");
    329
    330            FileOutputStream outputStream = new FileOutputStream(args[2]);
    331            outputStream.write(encryptedData);
    332            outputStream.flush();
    333            outputStream.close();
    334
    335            output_.println("finished");
    336
    337            output_.println("################################################################################");
    338
    339            output_.println("################################################################################");
    340
    341            if (!des3CbcMechanismInfo.isDecrypt()) {
    342                output_.print("This token does not support Tripple DES for decryption!");
    343            } else {
    344              if (!selectedEncryptionKey.getDecrypt().getBooleanValue().booleanValue()) {
    345                output_.print("The selected key cannot be used for decryption!");
    346              } else {
    347                output_.println("decrypting data from file: " + args[2]);
    348
    349                // we alread have the data in the encryptedData array
    350
    351                // use same mechanism and IV as before
    352                selectedMechanism = (Mechanism) Mechanism.DES3_CBC_PAD.clone();
    353                selectedMechanism.setParameters(encryptInitializationVectorParameters);
    354
    355                output_.print("decrypting the data... ");
    356
    357                // initialize for encryption
    358                session.decryptInit(selectedMechanism, selectedEncryptionKey);
    359
    360                byte[] decryptedData = session.decrypt(encryptedData);
    361
    362                output_.println("finished");
    363
    364                // compare initial data and decrypted data
    365                boolean equal = false;
    366                if (rawData.length != decryptedData.length) {
    367                  equal = false;
    368                } else {
    369                  equal = true;
    370                  for (int i=0; i < rawData.length; i++) {
    371                    if (rawData[i] != decryptedData[i]) {
    372                      equal = false;
    373                      break;
    374                    }
    375                  }
    376                }
    377
    378                output_.println("decryption " + ((equal) ? "successful" : "FAILED"));
    379
    380                output_.println("finished");
    381              }
    382            }
    383
    384            output_.println("################################################################################");
    385
    386            session.closeSession();
    387            pkcs11Module.finalize(null);
    388
    389        } catch (Throwable thr) {
    390          thr.printStackTrace();
    391        }
    392      }
    393
    394      public static void printUsage() {
    395        output_.println("Usage: Encrypt <PKCS#11 module> <file to be encrypted> <encrypted file>");
    396        output_.println(" e.g.: Encrypt pk2priv.dll data.dat data.enc");
    397        output_.println("The given DLL must be in the search path of the system.");
    398      }
    399
    400    }