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.FileInputStream; 048 import java.io.FileOutputStream; 049 import java.io.InputStream; 050 import java.io.InputStreamReader; 051 import java.io.OutputStream; 052 import java.io.PrintStream; 053 import java.security.Security; 054 import java.util.Arrays; 055 import java.util.HashMap; 056 import java.util.Iterator; 057 import java.util.List; 058 import java.util.Map; 059 import java.util.Vector; 060 061 import javax.crypto.SecretKeyFactory; 062 import javax.crypto.spec.SecretKeySpec; 063 064 import iaik.asn1.structures.AlgorithmID; 065 import iaik.pkcs.pkcs11.Mechanism; 066 import iaik.pkcs.pkcs11.MechanismInfo; 067 import iaik.pkcs.pkcs11.Module; 068 import iaik.pkcs.pkcs11.Session; 069 import iaik.pkcs.pkcs11.Slot; 070 import iaik.pkcs.pkcs11.Token; 071 import iaik.pkcs.pkcs11.TokenInfo; 072 import iaik.pkcs.pkcs11.objects.Object; 073 import iaik.pkcs.pkcs11.objects.PrivateKey; 074 import iaik.pkcs.pkcs11.objects.X509PublicKeyCertificate; 075 import iaik.pkcs.pkcs7.EncryptedContentInfoStream; 076 import iaik.pkcs.pkcs7.EnvelopedDataStream; 077 import iaik.pkcs.pkcs7.IssuerAndSerialNumber; 078 import iaik.pkcs.pkcs7.RecipientInfo; 079 import iaik.security.provider.IAIK; 080 import iaik.x509.X509Certificate; 081 082 083 084 /** 085 * This demo shows how to use a PKCS#11 token to decrypt a PKCS#7 encrypted 086 * object. It only supports RSA decryption. This sample just decrypts the 087 * included symmetric key on the token and uses the symmetric key to decrypt 088 * the content on the host, i.e. in software. 089 * 090 * @author <a href="mailto:Karl.Scheibelhofer@iaik.at"> Karl Scheibelhofer </a> 091 * @version 0.1 092 * @invariants 093 */ 094 public class DecryptPKCS7 { 095 096 static PrintStream output_; 097 098 static BufferedReader input_; 099 100 static { 101 try { 102 //output_ = new PrintWriter(new FileWriter("GetInfo_output.txt"), true); 103 output_ = new PrintStream(System.out, true); 104 input_ = new BufferedReader(new InputStreamReader(System.in)); 105 } catch (Throwable thr) { 106 thr.printStackTrace(); 107 output_ = new PrintStream(System.out, true); 108 input_ = new BufferedReader(new InputStreamReader(System.in)); 109 } 110 } 111 112 public static void main(String[] args) { 113 if ((args.length != 2) && (args.length != 3)) { 114 printUsage(); 115 System.exit(1); 116 } 117 118 try { 119 Security.addProvider(new IAIK()); 120 121 Module pkcs11Module = Module.getInstance(args[0]); 122 pkcs11Module.initialize(null); 123 124 output_.println("################################################################################"); 125 output_.println("getting list of all tokens"); 126 Slot[] slotsWithToken = pkcs11Module.getSlotList(Module.SlotRequirement.TOKEN_PRESENT); 127 Token[] tokens = new Token[slotsWithToken.length]; 128 Map tokenIDtoToken = new HashMap(tokens.length); 129 130 for (int i = 0; i < slotsWithToken.length; i++) { 131 output_.println("________________________________________________________________________________"); 132 tokens[i] = slotsWithToken[i].getToken(); 133 TokenInfo tokenInfo = tokens[i].getTokenInfo(); 134 long tokenID = tokens[i].getTokenID(); 135 tokenIDtoToken.put(new Long(tokenID), tokens[i]); 136 output_.println("Token ID: " + tokenID); 137 output_.println(tokenInfo); 138 output_.println("________________________________________________________________________________"); 139 } 140 output_.println("################################################################################"); 141 142 output_.println("################################################################################"); 143 Token token = null; 144 Long selectedTokenID = null; 145 if (tokens.length == 0) { 146 output_.println("There is no slot with a present token."); 147 output_.flush(); 148 System.exit(0); 149 } else if (tokens.length == 1) { 150 output_.println("Taking token with ID: " + tokens[0].getTokenID()); 151 selectedTokenID = new Long(tokens[0].getTokenID()); 152 token = tokens[0]; 153 } else { 154 boolean gotTokenID = false; 155 while (!gotTokenID) { 156 output_.print("Enter the ID of the token to use or 'x' to exit: "); 157 output_.flush(); 158 String tokenIDstring = input_.readLine(); 159 if (tokenIDstring.equalsIgnoreCase("x")) { 160 output_.flush(); 161 System.exit(0); 162 } 163 try { 164 selectedTokenID = new Long(tokenIDstring); 165 token = (Token) tokenIDtoToken.get(selectedTokenID); 166 if (token != null) { 167 gotTokenID = true; 168 } else { 169 output_.println("A token with the entered ID \"" + tokenIDstring + "\" does not exist. Try again."); 170 } 171 } catch (NumberFormatException ex) { 172 output_.println("The entered ID \"" + tokenIDstring + "\" is invalid. Try again."); 173 } 174 } 175 } 176 177 // check, if this token can do RSA decryption 178 List supportedMechanisms = Arrays.asList(token.getMechanismList()); 179 if (!supportedMechanisms.contains(Mechanism.RSA_PKCS)) { 180 output_.print("This token does not support RSA!"); 181 output_.flush(); 182 System.exit(0); 183 } else { 184 MechanismInfo rsaMechanismInfo = token.getMechanismInfo(Mechanism.RSA_PKCS); 185 if (!rsaMechanismInfo.isDecrypt()) { 186 output_.print("This token does not support RSA decryption according to PKCS!"); 187 output_.flush(); 188 System.exit(0); 189 } 190 } 191 192 Session session = 193 token.openSession(Token.SessionType.SERIAL_SESSION, Token.SessionReadWriteBehavior.RO_SESSION, null, null); 194 195 TokenInfo tokenInfo = token.getTokenInfo(); 196 if (tokenInfo.isLoginRequired()) { 197 if (tokenInfo.isProtectedAuthenticationPath()) { 198 output_.println("Please enter the user PIN at the PIN-pad of your reader."); 199 session.login(Session.UserType.USER, null); // the token prompts the PIN by other means; e.g. PIN-pad 200 } else { 201 output_.print("Enter user-PIN and press [return key]: "); 202 output_.flush(); 203 String userPINString = input_.readLine(); 204 session.login(Session.UserType.USER, userPINString.toCharArray()); 205 } 206 } 207 208 // read all certificates that are on the token 209 List tokenCertificates = new Vector(); 210 X509PublicKeyCertificate certificateTemplate = new X509PublicKeyCertificate(); 211 session.findObjectsInit(certificateTemplate); 212 Object[] tokenCertificateObjects; 213 214 while ((tokenCertificateObjects = session.findObjects(1)).length > 0) { 215 tokenCertificates.add(tokenCertificateObjects[0]); 216 } 217 session.findObjectsFinal(); 218 219 output_.println("################################################################################"); 220 221 output_.println("################################################################################"); 222 output_.println("reading encrypted data from file: " + args[1]); 223 224 FileInputStream encryptedInputStream = new FileInputStream(args[1]); 225 226 EnvelopedDataStream envelopedData = new EnvelopedDataStream(encryptedInputStream); 227 228 RecipientInfo[] recipientInfos = envelopedData.getRecipientInfos(); 229 230 // search through the recipients and look, if we have one of the recipients' certificates on the token 231 boolean haveDecryptionKey = false; 232 InputStream decryptedDataInputStream = null; 233 for (int i = 0; i < recipientInfos.length; i++) { 234 IssuerAndSerialNumber issuerAndSerialNumber = recipientInfos[i].getIssuerAndSerialNumber(); 235 236 // look if there is a certificate on our token with the given issuer and serial number 237 X509PublicKeyCertificate matchingTokenCertificate = null; 238 Iterator tokenCertificatesIterator = tokenCertificates.iterator(); 239 while (tokenCertificatesIterator.hasNext()) { 240 X509PublicKeyCertificate tokenCertificate = (X509PublicKeyCertificate) tokenCertificatesIterator.next(); 241 X509Certificate parsedTokenCertificate = new X509Certificate(tokenCertificate.getValue().getByteArrayValue()); 242 if (issuerAndSerialNumber.isIssuerOf(parsedTokenCertificate)) { 243 output_.println("________________________________________________________________________________"); 244 output_.println("Found matching certificate on the token:"); 245 output_.println(parsedTokenCertificate.toString(true)); 246 output_.println("________________________________________________________________________________"); 247 matchingTokenCertificate = tokenCertificate; 248 break; 249 } 250 } 251 252 if (matchingTokenCertificate != null) { 253 // find the corresponding private key for the certificate 254 PrivateKey privateKeyTemplate = new PrivateKey(); 255 privateKeyTemplate.getId().setByteArrayValue(matchingTokenCertificate.getId().getByteArrayValue()); 256 257 session.findObjectsInit(privateKeyTemplate); 258 Object[] correspondingPrivateKeyObjects; 259 PrivateKey correspondingPrivateKey = null; 260 261 if ((correspondingPrivateKeyObjects = session.findObjects(1)).length > 0) { 262 correspondingPrivateKey = (PrivateKey) correspondingPrivateKeyObjects[0]; 263 output_.println("________________________________________________________________________________"); 264 output_.println("Found corresponding private key:"); 265 output_.println(correspondingPrivateKey); 266 output_.println("________________________________________________________________________________"); 267 } else { 268 output_.println("Found no private key with the same ID as the matching certificate."); 269 } 270 session.findObjectsFinal(); 271 272 // check, if the private key is a decrpytion key 273 PrivateKey decryptionKey = 274 ((correspondingPrivateKey != null) && (correspondingPrivateKey.getDecrypt().getBooleanValue().booleanValue())) 275 ? correspondingPrivateKey 276 : null; 277 278 if (decryptionKey != null) { 279 haveDecryptionKey = true; 280 output_.print("decrypting symmetric key... "); 281 byte[] encryptedSymmetricKey = recipientInfos[i].getEncryptedKey(); 282 // decrypt the encrypted symmetric key using the e.g. RSA on the smart-card 283 session.decryptInit(Mechanism.RSA_PKCS, decryptionKey); 284 byte[] decryptedSymmetricKey = session.decrypt(encryptedSymmetricKey); 285 output_.println("finished"); 286 287 // construct the symmetric key 288 output_.print("constructing symmetric key for software decryption... "); 289 EncryptedContentInfoStream encryptedContentInfo = 290 (EncryptedContentInfoStream) envelopedData.getEncryptedContentInfo(); 291 AlgorithmID contentEncryptionAlgorithm = encryptedContentInfo.getContentEncryptionAlgorithm(); 292 SecretKeySpec secretKeySpec = 293 new SecretKeySpec(decryptedSymmetricKey, contentEncryptionAlgorithm.getRawImplementationName()); 294 SecretKeyFactory secretKeyFactory = 295 SecretKeyFactory.getInstance(contentEncryptionAlgorithm.getRawImplementationName()); 296 javax.crypto.SecretKey secretKey = secretKeyFactory.generateSecret(secretKeySpec); 297 output_.println("finished"); 298 299 // decrypt the data (in software) 300 encryptedContentInfo.setupCipher(secretKey); 301 decryptedDataInputStream = encryptedContentInfo.getInputStream(); 302 303 // read decrypted data from decryptedDataInputStream 304 } 305 } 306 } 307 308 if (!haveDecryptionKey) { 309 output_.print("Found no decryption key that matches any recipient info in the encrypted PKCS#7 object."); 310 output_.flush(); 311 System.exit(0); 312 } 313 314 if (decryptedDataInputStream == null) { 315 output_.print("Could not decrypt the PKCS#7 object."); 316 output_.flush(); 317 System.exit(0); 318 } 319 output_.println("################################################################################"); 320 321 322 output_.println("################################################################################"); 323 OutputStream decryptedContentStream = (args.length == 3) ? new FileOutputStream(args[2]) : null; 324 byte[] buffer = new byte[1024]; 325 int bytesRead; 326 output_.println("The decrypted content data is: "); 327 output_.println("________________________________________________________________________________"); 328 while ((bytesRead = decryptedDataInputStream.read(buffer)) > 0) { 329 output_.write(buffer, 0, bytesRead); 330 if (decryptedContentStream != null) { 331 decryptedContentStream.write(buffer, 0, bytesRead); 332 } 333 } 334 output_.println(); 335 output_.println("________________________________________________________________________________"); 336 if (decryptedContentStream != null) { 337 output_.println("Decrypted content written to: " + args[2]); 338 decryptedContentStream.flush(); 339 decryptedContentStream.close(); 340 } 341 output_.println("################################################################################"); 342 343 session.closeSession(); 344 pkcs11Module.finalize(null); 345 346 } catch (Throwable th) { 347 th.printStackTrace(); 348 } 349 } 350 351 public static void printUsage() { 352 output_.println("Usage: DecryptPKCS7 <PKCS#11 module> <PKCS#7 encrypted data file> [<decrypted content data>]"); 353 output_.println(" e.g.: DecryptPKCS7 slbck.dll encryptedData.p7 decryptedContent.dat"); 354 output_.println("The given DLL must be in the search path of the system."); 355 } 356 357 }