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    }