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 iaik.asn1.ObjectID;
047    import iaik.asn1.structures.Name;
048    import iaik.pkcs.pkcs11.Module;
049    import iaik.pkcs.pkcs11.Session;
050    import iaik.pkcs.pkcs11.Token;
051    import iaik.pkcs.pkcs11.TokenInfo;
052    import iaik.pkcs.pkcs11.objects.DHPrivateKey;
053    import iaik.pkcs.pkcs11.objects.DSAPrivateKey;
054    import iaik.pkcs.pkcs11.objects.Key;
055    import iaik.pkcs.pkcs11.objects.Object;
056    import iaik.pkcs.pkcs11.objects.RSAPrivateKey;
057    import iaik.pkcs.pkcs11.objects.X509PublicKeyCertificate;
058    import iaik.security.provider.IAIK;
059    
060    import java.io.BufferedReader;
061    import java.io.FileInputStream;
062    import java.io.InputStreamReader;
063    import java.io.PrintWriter;
064    import java.security.MessageDigest;
065    import java.security.Provider;
066    import java.security.PublicKey;
067    import java.security.Security;
068    import java.security.cert.CertificateFactory;
069    import java.security.interfaces.DSAParams;
070    import java.util.Collection;
071    
072    import javax.crypto.spec.DHParameterSpec;
073    
074    
075    /**
076     * This demo program imports a given X.509 certificate onto a PKCS#11 token.
077     *
078     * @author <a href="mailto:Karl.Scheibelhofer@iaik.at"> Karl Scheibelhofer </a>
079     * @version 0.1
080     * @invariants
081     */
082    public class ImportCertificate {
083    
084      static BufferedReader input_;
085    
086      static PrintWriter output_;
087    
088      static {
089        try {
090          //output_ = new PrintWriter(new FileWriter("SignAndVerify_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 != 2) {
102          printUsage();
103          System.exit(1);
104        }
105    
106        try {
107    
108            Security.addProvider(new IAIK());
109    
110            // try if we have the ECC provider available, if yes, add it
111            try {
112              Class eccProviderClass = Class.forName("iaik.security.ecc.provider.ECCProvider");
113              Provider eccProvider = (Provider) eccProviderClass.newInstance();
114              Security.addProvider(eccProvider);
115            } catch (Exception ex) {
116              // ignore, we only need it for pkcs#12 files containing ECC keys
117            }
118      
119            Module pkcs11Module = Module.getInstance(args[0]);
120            pkcs11Module.initialize(null);
121    
122            Token token = Util.selectToken(pkcs11Module, output_, input_);
123            if (token == null) {
124              output_.println("We have no token to proceed. Finished.");
125              output_.flush();
126              System.exit(0);
127            }
128            TokenInfo tokenInfo = token.getTokenInfo();
129    
130            output_.println("################################################################################");
131            output_.println("Information of Token:");
132            output_.println(tokenInfo);
133            output_.println("################################################################################");
134    
135            output_.println("################################################################################");
136            output_.println("Reading certificate from file: " + args[1]);
137    
138            Session session = Util.openAuthorizedSession(token, Token.SessionReadWriteBehavior.RW_SESSION, output_, input_);
139    
140            //  parse certificate
141            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "IAIK");
142            FileInputStream fileInputStream = new FileInputStream(args[1]);
143            Collection certificateChain = certificateFactory.generateCertificates(fileInputStream);
144            if (certificateChain.size() < 1) {
145              output_.println("Did not find any certificate in the given input file. Finished.");
146              output_.flush();
147              System.exit(0);
148            }
149            iaik.x509.X509Certificate x509Certificate = 
150                (iaik.x509.X509Certificate) certificateChain.iterator().next();
151            certificateChain.remove(x509Certificate);
152    
153            output_.println("################################################################################");
154    
155            output_.println("################################################################################");
156            output_.println("Searching for corresponding private key on token.");
157    
158            PublicKey publicKey = x509Certificate.getPublicKey();
159    
160            Object searchTemplate = null;
161            if (publicKey.getAlgorithm().equalsIgnoreCase("RSA")) {
162              java.security.interfaces.RSAPublicKey rsaPublicKey = (java.security.interfaces.RSAPublicKey) publicKey;
163              RSAPrivateKey rsaPrivateKeySearchTemplate = new RSAPrivateKey();
164              byte[] modulus = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(rsaPublicKey.getModulus());
165              rsaPrivateKeySearchTemplate.getModulus().setByteArrayValue(modulus);
166              searchTemplate = rsaPrivateKeySearchTemplate;
167            } else if (publicKey.getAlgorithm().equalsIgnoreCase("DSA")) {
168              java.security.interfaces.DSAPublicKey dsaPublicKey = (java.security.interfaces.DSAPublicKey) publicKey;
169              DSAParams dsaParams = dsaPublicKey.getParams();
170              DSAPrivateKey dsaPrivateKeySearchTemplate = new DSAPrivateKey();
171              byte[] g = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dsaParams.getG());
172              byte[] p = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dsaParams.getP());
173              byte[] q = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dsaParams.getQ());
174              dsaPrivateKeySearchTemplate.getBase().setByteArrayValue(g);
175              dsaPrivateKeySearchTemplate.getPrime().setByteArrayValue(p);
176              dsaPrivateKeySearchTemplate.getSubprime().setByteArrayValue(q);
177              searchTemplate = dsaPrivateKeySearchTemplate;
178            } else if (publicKey.getAlgorithm().equalsIgnoreCase("DH")
179                       || publicKey.getAlgorithm().equalsIgnoreCase("DiffieHellman")) {
180              javax.crypto.interfaces.DHPublicKey dhPublicKey = (javax.crypto.interfaces.DHPublicKey) publicKey;
181              DHParameterSpec dhParams = dhPublicKey.getParams();
182              DHPrivateKey dhPrivateKeySearchTemplate = new DHPrivateKey();
183              byte[] g = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dhParams.getG());
184              byte[] p = iaik.pkcs.pkcs11.Util.unsignedBigIntergerToByteArray(dhParams.getP());
185              dhPrivateKeySearchTemplate.getBase().setByteArrayValue(g);
186              dhPrivateKeySearchTemplate.getPrime().setByteArrayValue(p);
187              searchTemplate = dhPrivateKeySearchTemplate;
188            }
189    
190            byte[] objectID = null;
191            if (searchTemplate != null) {
192              session.findObjectsInit(searchTemplate);
193              Object[] foundKeyObjects = session.findObjects(1);
194              if (foundKeyObjects.length > 0) {
195                Key foundKey = (Key) foundKeyObjects[0];
196                objectID = foundKey.getId().getByteArrayValue();
197                output_.println("found a correponding key on the token: ");
198                output_.println(foundKey);
199              } else {
200                output_.println("found no correponding key on the token.");
201              }
202              session.findObjectsFinal();
203            } else {
204              output_.println("public key is neither RSA, DSA nor DH.");
205            }
206    
207            output_.println("################################################################################");
208    
209    
210            output_.println("################################################################################");
211            output_.println("Create certificate object(s) on token.");
212    
213            iaik.x509.X509Certificate currentCertificate = x509Certificate; // start with user cert
214            boolean importedCompleteChain = false;
215            while (!importedCompleteChain) {
216              // create certificate object template
217              X509PublicKeyCertificate pkcs11X509PublicKeyCertificate = new X509PublicKeyCertificate();
218              Name subjectName = (Name) currentCertificate.getSubjectDN();
219              Name issuerName = (Name) currentCertificate.getIssuerDN();
220              String subjectCommonName = subjectName.getRDN(ObjectID.commonName);
221              String issuerCommonName = issuerName.getRDN(ObjectID.commonName);
222              char[] label = (subjectCommonName + "'s " +
223                              ((issuerCommonName != null) ? issuerCommonName + " " : "") +  "Certificate").toCharArray();
224              byte[] newObjectID;
225              // if we need a new object ID, create one
226              if (objectID == null) {
227                if (publicKey instanceof java.security.interfaces.RSAPublicKey) {
228                  newObjectID = ((java.security.interfaces.RSAPublicKey) publicKey).getModulus().toByteArray();
229                  MessageDigest digest = MessageDigest.getInstance("SHA-1");
230                  newObjectID = digest.digest(newObjectID);
231                } else if (publicKey instanceof java.security.interfaces.DSAPublicKey) {
232                  newObjectID = ((java.security.interfaces.DSAPublicKey) publicKey).getY().toByteArray();
233                  MessageDigest digest = MessageDigest.getInstance("SHA-1");
234                  newObjectID = digest.digest(newObjectID);
235                } else {
236                  newObjectID = currentCertificate.getFingerprint("SHA-1");
237                }
238              } else {
239                // we already got one from a corresponding private key before
240                newObjectID = objectID;
241              }
242      
243              byte[] encodedSubject = ((Name) currentCertificate.getSubjectDN()).getEncoded();
244              byte[] encodedIssuer = ((Name) currentCertificate.getIssuerDN()).getEncoded();
245      
246              // serial number should be an DER encoded ASN.1 integer
247      /*
248              INTEGER asn1Integer = new INTEGER(userCertificate.getSerialNumber());
249              ByteArrayOutputStream buffer = new ByteArrayOutputStream();
250              DerCoder.encodeTo(asn1Integer, buffer);
251              pkcs11X509PublicKeyCertificate.getSerialNumber().setByteArrayValue(buffer.toByteArray());
252       */
253              // Netscape deviates from the standard here, for use with Netscape rather use
254              byte[] serialNumber = currentCertificate.getSerialNumber().toByteArray();
255      
256              pkcs11X509PublicKeyCertificate.getToken().setBooleanValue(Boolean.TRUE);
257              pkcs11X509PublicKeyCertificate.getPrivate().setBooleanValue(Boolean.FALSE);
258              pkcs11X509PublicKeyCertificate.getLabel().setCharArrayValue(label);
259              pkcs11X509PublicKeyCertificate.getId().setByteArrayValue(newObjectID);
260              pkcs11X509PublicKeyCertificate.getSubject().setByteArrayValue(encodedSubject);
261              pkcs11X509PublicKeyCertificate.getIssuer().setByteArrayValue(encodedIssuer);
262              pkcs11X509PublicKeyCertificate.getSerialNumber().setByteArrayValue(serialNumber);
263              pkcs11X509PublicKeyCertificate.getValue().setByteArrayValue(currentCertificate.getEncoded());
264      
265              output_.println(pkcs11X509PublicKeyCertificate);
266              output_.println("________________________________________________________________________________");
267              session.createObject(pkcs11X509PublicKeyCertificate);
268              
269              if (certificateChain.size() > 0) {
270                currentCertificate = (iaik.x509.X509Certificate) certificateChain.iterator().next();
271                certificateChain.remove(currentCertificate);
272                objectID = null; // do not use the same ID for other certificates
273              } else {
274                importedCompleteChain = true;
275              }
276            }
277    
278            output_.println("################################################################################");
279    
280            session.closeSession();
281            pkcs11Module.finalize(null);
282    
283        } catch (Throwable thr) {
284          thr.printStackTrace();
285        }
286      }
287    
288      public static void printUsage() {
289        output_.println("Usage: ImportCertificate <PKCS#11 module> " +
290                        "<DER encoded X.509 certificate, certificate chain, or PKCS#7 certificate chain>");
291        output_.println(" e.g.: ImportCertificate pk2priv.dll myCertificate.der");
292        output_.println("The given DLL must be in the search path of the system.");
293      }
294    
295    }