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.IOException;
048    import java.io.PrintWriter;
049    //import java.util.HashMap;
050    import java.util.Enumeration;
051    import java.util.Hashtable;
052    //import java.util.Iterator;
053    //import java.util.List;
054    //import java.util.Map;
055    import java.util.Vector;
056    
057    import iaik.pkcs.pkcs11.Module;
058    import iaik.pkcs.pkcs11.Session;
059    import iaik.pkcs.pkcs11.Slot;
060    import iaik.pkcs.pkcs11.Token;
061    import iaik.pkcs.pkcs11.TokenException;
062    import iaik.pkcs.pkcs11.TokenInfo;
063    import iaik.pkcs.pkcs11.objects.Key;
064    import iaik.pkcs.pkcs11.objects.Object;
065    import iaik.pkcs.pkcs11.objects.PrivateKey;
066    import iaik.pkcs.pkcs11.objects.RSAPrivateKey;
067    import iaik.pkcs.pkcs11.objects.X509PublicKeyCertificate;
068    import iaik.x509.X509Certificate;
069    
070    
071    
072    /**
073     * This class contains only static methods. It is the place for all functions
074     * that are used by several classes in this package.
075     *
076     * @author Karl Scheibelhofer <Karl.Scheibelhofer@iaik.at>
077     */
078    public class Util {
079    
080      /**
081       * Maps mechanism strings to their codes as Long.
082       */
083      protected static Hashtable mechansimCodes_;
084    
085      /**
086       * Converts the names of mechanisms to their long value code.
087       *
088       * @param mechansimName The name of the mechanism to get the code; e.g.
089       *                      "CKM_RSA_PKCS".
090       * @return The code of the mechanism or null, if this name is unknown.
091       * @preconditions (mechansimName <> null)
092       * @postconditions
093       */
094      public static Long mechanismCodeToString(String mechansimName) {
095        if (mechansimName == null) {
096          throw new NullPointerException("Argument \"mechansimName\" must not be null.");
097        }
098    
099        Long mechanismCode;
100    
101        if (mechansimName.startsWith("0x")) {
102          // we try to parse it as hex encoded long
103          mechanismCode = new Long(Long.parseLong(mechansimName, 16));
104        } else {
105          if (mechansimCodes_ == null) {
106            mechansimCodes_ = new Hashtable(160);
107            mechansimCodes_.put("CKM_RSA_PKCS_KEY_PAIR_GEN", new Long(0x00000000));
108            mechansimCodes_.put("CKM_RSA_PKCS", new Long(0x00000001));
109            mechansimCodes_.put("CKM_RSA_9796", new Long(0x00000002));
110            mechansimCodes_.put("CKM_RSA_X_509", new Long(0x00000003));
111            mechansimCodes_.put("CKM_MD2_RSA_PKCS", new Long(0x00000004));
112            mechansimCodes_.put("CKM_MD5_RSA_PKCS", new Long(0x00000005));
113            mechansimCodes_.put("CKM_SHA1_RSA_PKCS", new Long(0x00000006));
114            mechansimCodes_.put("CKM_RIPEMD128_RSA_PKCS", new Long(0x00000007));
115            mechansimCodes_.put("CKM_RIPEMD160_RSA_PKCS", new Long(0x00000008));
116            mechansimCodes_.put("CKM_RSA_PKCS_OAEP", new Long(0x00000009));
117            mechansimCodes_.put("CKM_DSA_KEY_PAIR_GEN", new Long(0x00000010));
118            mechansimCodes_.put("CKM_DSA", new Long(0x00000011));
119            mechansimCodes_.put("CKM_DSA_SHA1", new Long(0x00000012));
120            mechansimCodes_.put("CKM_DH_PKCS_KEY_PAIR_GEN", new Long(0x00000020));
121            mechansimCodes_.put("CKM_DH_PKCS_DERIVE", new Long(0x00000021));
122            mechansimCodes_.put("CKM_RC2_KEY_GEN", new Long(0x00000100));
123            mechansimCodes_.put("CKM_RC2_ECB", new Long(0x00000101));
124            mechansimCodes_.put("CKM_RC2_CBC", new Long(0x00000102));
125            mechansimCodes_.put("CKM_RC2_MAC", new Long(0x00000103));
126            mechansimCodes_.put("CKM_RC2_MAC_GENERAL", new Long(0x00000104));
127            mechansimCodes_.put("CKM_RC2_CBC_PAD", new Long(0x00000105));
128            mechansimCodes_.put("CKM_RC4_KEY_GEN", new Long(0x00000110));
129            mechansimCodes_.put("CKM_RC4", new Long(0x00000111));
130            mechansimCodes_.put("CKM_DES_KEY_GEN", new Long(0x00000120));
131            mechansimCodes_.put("CKM_DES_ECB", new Long(0x00000121));
132            mechansimCodes_.put("CKM_DES_CBC", new Long(0x00000122));
133            mechansimCodes_.put("CKM_DES_MAC", new Long(0x00000123));
134            mechansimCodes_.put("CKM_DES_MAC_GENERAL", new Long(0x00000124));
135            mechansimCodes_.put("CKM_DES_CBC_PAD", new Long(0x00000125));
136            mechansimCodes_.put("CKM_DES2_KEY_GEN", new Long(0x00000130));
137            mechansimCodes_.put("CKM_DES3_KEY_GEN", new Long(0x00000131));
138            mechansimCodes_.put("CKM_DES3_ECB", new Long(0x00000132));
139            mechansimCodes_.put("CKM_DES3_CBC", new Long(0x00000133));
140            mechansimCodes_.put("CKM_DES3_MAC", new Long(0x00000134));
141            mechansimCodes_.put("CKM_DES3_MAC_GENERAL", new Long(0x00000135));
142            mechansimCodes_.put("CKM_DES3_CBC_PAD", new Long(0x00000136));
143            mechansimCodes_.put("CKM_CDMF_KEY_GEN", new Long(0x00000140));
144            mechansimCodes_.put("CKM_CDMF_ECB", new Long(0x00000141));
145            mechansimCodes_.put("CKM_CDMF_CBC", new Long(0x00000142));
146            mechansimCodes_.put("CKM_CDMF_MAC", new Long(0x00000143));
147            mechansimCodes_.put("CKM_CDMF_MAC_GENERAL", new Long(0x00000144));
148            mechansimCodes_.put("CKM_CDMF_CBC_PAD", new Long(0x00000145));
149            mechansimCodes_.put("CKM_MD2", new Long(0x00000200));
150            mechansimCodes_.put("CKM_MD2_HMAC", new Long(0x00000201));
151            mechansimCodes_.put("CKM_MD2_HMAC_GENERAL", new Long(0x00000202));
152            mechansimCodes_.put("CKM_MD5", new Long(0x00000210));
153            mechansimCodes_.put("CKM_MD5_HMAC", new Long(0x00000211));
154            mechansimCodes_.put("CKM_MD5_HMAC_GENERAL", new Long(0x00000212));
155            mechansimCodes_.put("CKM_SHA_1", new Long(0x00000220));
156            mechansimCodes_.put("CKM_SHA_1_HMAC", new Long(0x00000221));
157            mechansimCodes_.put("CKM_SHA_1_HMAC_GENERAL", new Long(0x00000222));
158            mechansimCodes_.put("CKM_RIPEMD128", new Long(0x00000230));
159            mechansimCodes_.put("CKM_RIPEMD128_HMAC", new Long(0x00000231));
160            mechansimCodes_.put("CKM_RIPEMD128_HMAC_GENERAL", new Long(0x00000232));
161            mechansimCodes_.put("CKM_RIPEMD160", new Long(0x00000240));
162            mechansimCodes_.put("CKM_RIPEMD160_HMAC", new Long(0x00000241));
163            mechansimCodes_.put("CKM_RIPEMD160_HMAC_GENERAL", new Long(0x00000242));
164            mechansimCodes_.put("CKM_CAST_KEY_GEN", new Long(0x00000300));
165            mechansimCodes_.put("CKM_CAST_ECB", new Long(0x00000301));
166            mechansimCodes_.put("CKM_CAST_CBC", new Long(0x00000302));
167            mechansimCodes_.put("CKM_CAST_MAC", new Long(0x00000303));
168            mechansimCodes_.put("CKM_CAST_MAC_GENERAL", new Long(0x00000304));
169            mechansimCodes_.put("CKM_CAST_CBC_PAD", new Long(0x00000305));
170            mechansimCodes_.put("CKM_CAST3_KEY_GEN", new Long(0x00000310));
171            mechansimCodes_.put("CKM_CAST3_ECB", new Long(0x00000311));
172            mechansimCodes_.put("CKM_CAST3_CBC", new Long(0x00000312));
173            mechansimCodes_.put("CKM_CAST3_MAC", new Long(0x00000313));
174            mechansimCodes_.put("CKM_CAST3_MAC_GENERAL", new Long(0x00000314));
175            mechansimCodes_.put("CKM_CAST3_CBC_PAD", new Long(0x00000315));
176            mechansimCodes_.put("CKM_CAST5_KEY_GEN", new Long(0x00000320));
177            mechansimCodes_.put("CKM_CAST128_KEY_GEN", new Long(0x00000320));
178            mechansimCodes_.put("CKM_CAST5_ECB", new Long(0x00000321));
179            mechansimCodes_.put("CKM_CAST128_ECB", new Long(0x00000321));
180            mechansimCodes_.put("CKM_CAST5_CBC", new Long(0x00000322));
181            mechansimCodes_.put("CKM_CAST128_CBC", new Long(0x00000322));
182            mechansimCodes_.put("CKM_CAST5_MAC", new Long(0x00000323));
183            mechansimCodes_.put("CKM_CAST128_MAC", new Long(0x00000323));
184            mechansimCodes_.put("CKM_CAST5_MAC_GENERAL", new Long(0x00000324));
185            mechansimCodes_.put("CKM_CAST128_MAC_GENERAL", new Long(0x00000324));
186            mechansimCodes_.put("CKM_CAST5_CBC_PAD", new Long(0x00000325));
187            mechansimCodes_.put("CKM_CAST128_CBC_PAD", new Long(0x00000325));
188            mechansimCodes_.put("CKM_RC5_KEY_GEN", new Long(0x00000330));
189            mechansimCodes_.put("CKM_RC5_ECB", new Long(0x00000331));
190            mechansimCodes_.put("CKM_RC5_CBC", new Long(0x00000332));
191            mechansimCodes_.put("CKM_RC5_MAC", new Long(0x00000333));
192            mechansimCodes_.put("CKM_RC5_MAC_GENERAL", new Long(0x00000334));
193            mechansimCodes_.put("CKM_RC5_CBC_PAD", new Long(0x00000335));
194            mechansimCodes_.put("CKM_IDEA_KEY_GEN", new Long(0x00000340));
195            mechansimCodes_.put("CKM_IDEA_ECB", new Long(0x00000341));
196            mechansimCodes_.put("CKM_IDEA_CBC", new Long(0x00000342));
197            mechansimCodes_.put("CKM_IDEA_MAC", new Long(0x00000343));
198            mechansimCodes_.put("CKM_IDEA_MAC_GENERAL", new Long(0x00000344));
199            mechansimCodes_.put("CKM_IDEA_CBC_PAD", new Long(0x00000345));
200            mechansimCodes_.put("CKM_GENERIC_SECRET_KEY_GEN", new Long(0x00000350));
201            mechansimCodes_.put("CKM_CONCATENATE_BASE_AND_KEY", new Long(0x00000360));
202            mechansimCodes_.put("CKM_CONCATENATE_BASE_AND_DATA", new Long(0x00000362));
203            mechansimCodes_.put("CKM_CONCATENATE_DATA_AND_BASE", new Long(0x00000363));
204            mechansimCodes_.put("CKM_XOR_BASE_AND_DATA", new Long(0x00000364));
205            mechansimCodes_.put("CKM_EXTRACT_KEY_FROM_KEY", new Long(0x00000365));
206            mechansimCodes_.put("CKM_SSL3_PRE_MASTER_KEY_GEN", new Long(0x00000370));
207            mechansimCodes_.put("CKM_SSL3_MASTER_KEY_DERIVE", new Long(0x00000371));
208            mechansimCodes_.put("CKM_SSL3_KEY_AND_MAC_DERIVE", new Long(0x00000372));
209            mechansimCodes_.put("CKM_SSL3_MD5_MAC", new Long(0x00000380));
210            mechansimCodes_.put("CKM_SSL3_SHA1_MAC", new Long(0x00000381));
211            mechansimCodes_.put("CKM_MD5_KEY_DERIVATION", new Long(0x00000390));
212            mechansimCodes_.put("CKM_MD2_KEY_DERIVATION", new Long(0x00000391));
213            mechansimCodes_.put("CKM_SHA1_KEY_DERIVATION", new Long(0x00000392));
214            mechansimCodes_.put("CKM_PBE_MD2_DES_CBC", new Long(0x000003A0));
215            mechansimCodes_.put("CKM_PBE_MD5_DES_CBC", new Long(0x000003A1));
216            mechansimCodes_.put("CKM_PBE_MD5_CAST_CBC", new Long(0x000003A2));
217            mechansimCodes_.put("CKM_PBE_MD5_CAST3_CBC", new Long(0x000003A3));
218            mechansimCodes_.put("CKM_PBE_MD5_CAST5_CBC", new Long(0x000003A4));
219            mechansimCodes_.put("CKM_PBE_MD5_CAST128_CBC", new Long(0x000003A4));
220            mechansimCodes_.put("CKM_PBE_SHA1_CAST5_CBC", new Long(0x000003A5));
221            mechansimCodes_.put("CKM_PBE_SHA1_CAST128_CBC", new Long(0x000003A5));
222            mechansimCodes_.put("CKM_PBE_SHA1_RC4_128", new Long(0x000003A6));
223            mechansimCodes_.put("CKM_PBE_SHA1_RC4_40", new Long(0x000003A7));
224            mechansimCodes_.put("CKM_PBE_SHA1_DES3_EDE_CBC", new Long(0x000003A8));
225            mechansimCodes_.put("CKM_PBE_SHA1_DES2_EDE_CBC", new Long(0x000003A9));
226            mechansimCodes_.put("CKM_PBE_SHA1_RC2_128_CBC", new Long(0x000003AA));
227            mechansimCodes_.put("CKM_PBE_SHA1_RC2_40_CBC", new Long(0x000003AB));
228            mechansimCodes_.put("CKM_PKCS5_PBKD2", new Long(0x000003B0));
229            mechansimCodes_.put("CKM_PBA_SHA1_WITH_SHA1_HMAC", new Long(0x000003C0));
230            mechansimCodes_.put("CKM_KEY_WRAP_LYNKS", new Long(0x00000400));
231            mechansimCodes_.put("CKM_KEY_WRAP_SET_OAEP", new Long(0x00000401));
232            mechansimCodes_.put("CKM_SKIPJACK_KEY_GEN", new Long(0x00001000));
233            mechansimCodes_.put("CKM_SKIPJACK_ECB64", new Long(0x00001001));
234            mechansimCodes_.put("CKM_SKIPJACK_CBC64", new Long(0x00001002));
235            mechansimCodes_.put("CKM_SKIPJACK_OFB64", new Long(0x00001003));
236            mechansimCodes_.put("CKM_SKIPJACK_CFB64", new Long(0x00001004));
237            mechansimCodes_.put("CKM_SKIPJACK_CFB32", new Long(0x00001005));
238            mechansimCodes_.put("CKM_SKIPJACK_CFB16", new Long(0x00001006));
239            mechansimCodes_.put("CKM_SKIPJACK_CFB8", new Long(0x00001007));
240            mechansimCodes_.put("CKM_SKIPJACK_WRAP", new Long(0x00001008));
241            mechansimCodes_.put("CKM_SKIPJACK_PRIVATE_WRAP", new Long(0x00001009));
242            mechansimCodes_.put("CKM_SKIPJACK_RELAYX", new Long(0x0000100a));
243            mechansimCodes_.put("CKM_KEA_KEY_PAIR_GEN", new Long(0x00001010));
244            mechansimCodes_.put("CKM_KEA_KEY_DERIVE", new Long(0x00001011));
245            mechansimCodes_.put("CKM_FORTEZZA_TIMESTAMP", new Long(0x00001020));
246            mechansimCodes_.put("CKM_BATON_KEY_GEN", new Long(0x00001030));
247            mechansimCodes_.put("CKM_BATON_ECB128", new Long(0x00001031));
248            mechansimCodes_.put("CKM_BATON_ECB96", new Long(0x00001032));
249            mechansimCodes_.put("CKM_BATON_CBC128", new Long(0x00001033));
250            mechansimCodes_.put("CKM_BATON_COUNTER", new Long(0x00001034));
251            mechansimCodes_.put("CKM_BATON_SHUFFLE", new Long(0x00001035));
252            mechansimCodes_.put("CKM_BATON_WRAP", new Long(0x00001036));
253            mechansimCodes_.put("CKM_ECDSA_KEY_PAIR_GEN", new Long(0x00001040));
254            mechansimCodes_.put("CKM_ECDSA", new Long(0x00001041));
255            mechansimCodes_.put("CKM_ECDSA_SHA1", new Long(0x00001042));
256            mechansimCodes_.put("CKM_JUNIPER_KEY_GEN", new Long(0x00001060));
257            mechansimCodes_.put("CKM_JUNIPER_ECB128", new Long(0x00001061));
258            mechansimCodes_.put("CKM_JUNIPER_CBC128", new Long(0x00001062));
259            mechansimCodes_.put("CKM_JUNIPER_COUNTER", new Long(0x00001063));
260            mechansimCodes_.put("CKM_JUNIPER_SHUFFLE", new Long(0x00001064));
261            mechansimCodes_.put("CKM_JUNIPER_WRAP", new Long(0x00001065));
262            mechansimCodes_.put("CKM_FASTHASH", new Long(0x00001070));
263            mechansimCodes_.put("CKM_VENDOR_DEFINED", new Long(0x80000000));
264          }
265    
266          mechanismCode = (Long) mechansimCodes_.get(mechansimName);
267        }
268    
269        return mechanismCode ;
270      }
271    
272      /**
273       * Lists all available tokens of the given module and lets the user select
274       * one, if there is more than one available.
275       *
276       * @param pkcs11Module The PKCS#11 module to use.
277       * @param output The output stream to write the user prompts to.
278       * @param input The input stream where to read user input from.
279       * @return The selected token or null, if no token is available or the user
280       *         canceled the action.
281       * @exception TokenException If listing the tokens failed.
282       * @exception IOException If writing a user prompt faild or if reading user
283       *                        input failed.
284       * @preconditions (pkcs11Module <> null)
285       *                and (output <> null)
286       *                and (input <> null)
287       * @postconditions
288       */
289      public static Token selectToken(Module pkcs11Module, PrintWriter output, BufferedReader input)
290          throws TokenException, IOException
291      {
292        if (pkcs11Module == null) {
293          throw new NullPointerException("Argument \"pkcs11Module\" must not be null.");
294        }
295        if (output == null) {
296          throw new NullPointerException("Argument \"output\" must not be null.");
297        }
298        if (input == null) {
299          throw new NullPointerException("Argument \"input\" must not be null.");
300        }
301    
302        output.println("################################################################################");
303        output.println("getting list of all tokens");
304        Slot[] slotsWithToken = pkcs11Module.getSlotList(Module.SlotRequirement.TOKEN_PRESENT);
305        Token[] tokens = new Token[slotsWithToken.length];
306        Hashtable tokenIDtoToken = new Hashtable(tokens.length);
307    
308        for (int i = 0; i < slotsWithToken.length; i++) {
309          output.println("________________________________________________________________________________");
310          tokens[i] = slotsWithToken[i].getToken();
311          TokenInfo tokenInfo = tokens[i].getTokenInfo();
312          long tokenID = tokens[i].getTokenID();
313          tokenIDtoToken.put(new Long(tokenID), tokens[i]);
314          output.println("Token ID: " + tokenID);
315          output.println(tokenInfo);
316          output.println("________________________________________________________________________________");
317        }
318        output.println("################################################################################");
319    
320        output.println("################################################################################");
321        Token token = null;
322        Long selectedTokenID = null;
323        if (tokens.length == 0) {
324          output.println("There is no slot with a present token.");
325        } else if (tokens.length == 1) {
326          output.println("Taking token with ID: " + tokens[0].getTokenID());
327          selectedTokenID = new Long(tokens[0].getTokenID());
328          token = tokens[0];
329        } else {
330          boolean gotTokenID = false;
331          while (!gotTokenID) {
332            output.print("Enter the ID of the token to use or 'x' to exit: ");
333            output.flush();
334            String tokenIDstring = input.readLine();
335            if (tokenIDstring.equalsIgnoreCase("x")) {
336              break;
337            }
338            try {
339              selectedTokenID = new Long(tokenIDstring);
340              token = (Token) tokenIDtoToken.get(selectedTokenID);
341              if (token != null) {
342                gotTokenID = true;
343              } else {
344                output.println("A token with the entered ID \"" + tokenIDstring + "\" does not exist. Try again.");
345              }
346            } catch (NumberFormatException ex) {
347              output.println("The entered ID \"" + tokenIDstring + "\" is invalid. Try again.");
348            }
349          }
350        }
351        output.println("################################################################################");
352    
353        return token ;
354      }
355    
356      /**
357       * Opens an authorized session for the given token. If the token requires the
358       * user to login for private operations, the method loggs in the user.
359       *
360       * @param token The token to open a session for.
361       * @param rwSession If the session should be a read-write session. This may be
362       *                  Token.SessionReadWriteBehavior.RO_SESSION or
363       *                  Token.SessionReadWriteBehavior.RW_SESSION.
364       * @param output The output stream to write the user prompts to.
365       * @param input The input stream where to read user input from.
366       * @return The selected token or null, if no token is available or the user
367       *         canceled the action.
368       * @exception TokenException If listing the tokens failed.
369       * @exception IOException If writing a user prompt faild or if reading user
370       *                        input failed.
371       * @preconditions (token <> null)
372       *                and (output <> null)
373       *                and (input <> null)
374       * @postconditions (result <> null)
375       */
376      public static Session openAuthorizedSession(Token token, boolean rwSession, PrintWriter output, BufferedReader input)
377          throws TokenException, IOException
378      {
379        if (token == null) {
380          throw new NullPointerException("Argument \"token\" must not be null.");
381        }
382        if (output == null) {
383          throw new NullPointerException("Argument \"output\" must not be null.");
384        }
385        if (input == null) {
386          throw new NullPointerException("Argument \"input\" must not be null.");
387        }
388    
389        output.println("################################################################################");
390        output.println("opening session");
391        Session session =
392            token.openSession(Token.SessionType.SERIAL_SESSION, rwSession, null, null);
393    
394        TokenInfo tokenInfo = token.getTokenInfo();
395        if (tokenInfo.isLoginRequired()) {
396          if (tokenInfo.isProtectedAuthenticationPath()) {
397            output.print("Please enter the user-PIN at the PIN-pad of your reader.");
398            output.flush();
399            session.login(Session.UserType.USER, null); // the token prompts the PIN by other means; e.g. PIN-pad
400          } else {
401            output.print("Enter user-PIN and press [return key]: ");
402            output.flush();
403            String userPINString = input.readLine();
404            session.login(Session.UserType.USER, userPINString.toCharArray());
405          }
406        }
407        output.println("################################################################################");
408    
409        return session ;
410      }
411    
412      /**
413       * Lists all keys that match the given key template and lets the user choose
414       * one, if there is more than one. If there is a corresponding certificate for
415       * a key, this method displays the certificate for this key.
416       *
417       * @param session The session to use for key and certificate searching.
418       * @param keyTemplate The template for searching for keys.
419       * @param output The output stream to write the user prompts to.
420       * @param input The input stream where to read user input from.
421       * @return The selected key or null, if there is no matching key or the user
422       *         canceled the operation. The return object also contains a
423       *         corresponding certificate, if there is one for the selected key.
424       * @exception TokenException If searching for keys or certificates failed.
425       * @exception IOException If writing a user prompt faild or if reading user
426       *                        input failed.
427       * @preconditions (session <> null)
428       *                and (keyTemplate <> keyTemplate)
429       *                and (output <> null)
430       *                and (input <> null)
431       * @postconditions (result <> null)
432       */
433      public static KeyAndCertificate selectKeyAndCertificate(Session session, Key keyTemplate,
434                                                              PrintWriter output, BufferedReader input)
435          throws TokenException, IOException
436      {
437        if (session == null) {
438          throw new NullPointerException("Argument \"session\" must not be null.");
439        }
440        if (keyTemplate == null) {
441          throw new NullPointerException("Argument \"keyTemplate\" must not be null.");
442        }
443        if (output == null) {
444          throw new NullPointerException("Argument \"output\" must not be null.");
445        }
446        if (input == null) {
447          throw new NullPointerException("Argument \"input\" must not be null.");
448        }
449    
450        output.println("################################################################################");
451        output.println("searching for keys");
452    
453        Vector keyList = new Vector(4);
454    
455        session.findObjectsInit(keyTemplate);
456        Object[] matchingKeys;
457    
458        while ((matchingKeys = session.findObjects(1)).length > 0) {
459          keyList.addElement(matchingKeys[0]);
460        }
461        session.findObjectsFinal();
462    
463        // try to find the corresponding certificates for the signature keys
464        Hashtable keyToCertificateTable = new Hashtable(4);
465        Enumeration keyListEnumeration = keyList.elements();
466        while (keyListEnumeration.hasMoreElements()) {
467          PrivateKey signatureKey = (PrivateKey) keyListEnumeration.nextElement();
468          byte[] keyID = signatureKey.getId().getByteArrayValue();
469          X509PublicKeyCertificate certificateTemplate = new X509PublicKeyCertificate();
470          certificateTemplate.getId().setByteArrayValue(keyID);
471    
472          session.findObjectsInit(certificateTemplate);
473          Object[] correspondingCertificates = session.findObjects(1);
474    
475          if (correspondingCertificates.length > 0) {
476            keyToCertificateTable.put(signatureKey, correspondingCertificates[0]);
477          }
478          session.findObjectsFinal();
479        }
480    
481        Key selectedKey = null;
482        X509PublicKeyCertificate correspondingCertificate = null;
483        if (keyList.size() == 0) {
484          output.println("Found NO matching key that can be used.");
485        } else if (keyList.size() == 1) {
486          // there is no choice, take this key
487          selectedKey = (Key) keyList.elementAt(0);
488          // create a IAIK JCE certificate from the PKCS11 certificate
489          correspondingCertificate = (X509PublicKeyCertificate) keyToCertificateTable.get(selectedKey);
490          String correspondingCertificateString = toString(correspondingCertificate);
491          output.println("Found just one private RSA signing key. This key will be used:");
492          output.println(selectedKey);
493          output.println("--------------------------------------------------------------------------------");
494          output.println("The certificate for this key is:");
495          output.println((correspondingCertificateString != null)
496                         ? correspondingCertificateString
497                         : "<no certificate found>");
498        } else {
499          // give the user the choice
500          output.println("found these private RSA signing keys:");
501          Hashtable objectHandleToObjectMap = new Hashtable(keyList.size());
502          Enumeration keyListEnumeration2 = keyList.elements();
503          while (keyListEnumeration2.hasMoreElements()) {
504            Object signatureKey = (Object) keyListEnumeration2.nextElement();
505            long objectHandle = signatureKey.getObjectHandle();
506            objectHandleToObjectMap.put(new Long(objectHandle), signatureKey);
507            correspondingCertificate = (X509PublicKeyCertificate) keyToCertificateTable.get(signatureKey);
508            String correspondingCertificateString = toString(correspondingCertificate);
509            output.println("________________________________________________________________________________");
510            output.println("RSA signature key with handle: " + objectHandle);
511            output.println(signatureKey);
512            output.println("--------------------------------------------------------------------------------");
513            output.println("The certificate for this key is: ");
514            output.println((correspondingCertificateString != null) ? correspondingCertificateString : "<no certificate found>");
515            output.println("________________________________________________________________________________");
516          }
517    
518          boolean gotObjectHandle = false;
519          Long selectedObjectHandle;
520          while (!gotObjectHandle) {
521            output.print("Enter the handle of the key to use for signing or 'x' to exit: ");
522            output.flush();
523            String objectHandleString = input.readLine();
524            if (objectHandleString.equalsIgnoreCase("x")) {
525              break;
526            }
527            try {
528              selectedObjectHandle = new Long(objectHandleString);
529              selectedKey = (RSAPrivateKey) objectHandleToObjectMap.get(selectedObjectHandle);
530              if (selectedKey != null) {
531                correspondingCertificate = (X509PublicKeyCertificate) keyToCertificateTable.get(selectedKey);
532                gotObjectHandle = true;
533              } else {
534                output.println("An object with the handle \"" + objectHandleString + "\" does not exist. Try again.");
535              }
536            } catch (NumberFormatException ex) {
537              output.println("The entered handle \"" + objectHandleString + "\" is invalid. Try again.");
538            }
539          }
540        }
541    
542        output.println("################################################################################");
543    
544        return (selectedKey != null)
545               ? new KeyAndCertificate(selectedKey, correspondingCertificate)
546               : null ;
547      }
548    
549      /**
550       * Gets a string representation of the given PKCS#11 certificate.
551       *
552       * @param certificate The PKCS#11 certificate.
553       * @return The string representing the certificate.
554       * @preconditions
555       * @postconditions
556       */
557      public static String toString(X509PublicKeyCertificate certificate) {
558        String certificateString = null;
559    
560        if (certificate != null) {
561          try {
562            X509Certificate correspondingCertificate = new X509Certificate(certificate.getValue().getByteArrayValue());
563            certificateString = correspondingCertificate.toString(true);
564          } catch (Exception ex) {
565            certificateString = certificate.toString();
566          }
567        }
568    
569        return certificateString ;
570      }
571    
572    }