View Javadoc

1   package net.sf.bse;
2   
3   /*
4    * Copyright (c) 2002-2003 BSE project contributors 
5    * (http://bse.sourceforge.net/)
6    * 
7    * Permission is hereby granted, free of charge, to any person obtaining a copy
8    * of this software and associated documentation files (the "Software"), to deal
9    * in the Software without restriction, including without limitation the rights
10   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11   * copies of the Software, and to permit persons to whom the Software is
12   * furnished to do so, subject to the following conditions:
13   * 
14   * The above copyright notice and this permission notice shall be included in
15   * all copies or substantial portions of the Software.
16   * 
17   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23   * THE SOFTWARE.
24   */
25  
26  import java.io.ByteArrayInputStream;
27  import java.io.FileOutputStream;
28  import java.io.PrintStream;
29  import java.math.BigInteger;
30  import java.security.KeyFactory;
31  import java.security.PrivateKey;
32  import java.security.cert.X509Certificate;
33  import java.security.spec.PKCS8EncodedKeySpec;
34  import java.util.Collection;
35  import java.util.Hashtable;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.StringTokenizer;
40  
41  import org.bouncycastle.asn1.DERConstructedSequence;
42  import org.bouncycastle.asn1.DERIA5String;
43  import org.bouncycastle.asn1.x509.GeneralName;
44  import org.bouncycastle.asn1.x509.GeneralNames;
45  import org.bouncycastle.asn1.x509.X509Extensions;
46  import org.bouncycastle.asn1.x509.X509Name;
47  import org.bouncycastle.jce.PKCS10CertificationRequest;
48  import org.bouncycastle.jce.X509KeyUsage;
49  import org.bouncycastle.jce.X509V3CertificateGenerator;
50  import org.bouncycastle.jce.provider.X509CertificateObject;
51  
52  /***
53   * Command to sign a leaf certificate.
54   *
55   * @author Bill Foote (bill.foote@sun.com)
56   * @author Aleksi Peebles (aleksi.peebles@infocast.fi)
57   * @version $Revision: 1.3 $ $Date: 2004/05/06 10:00:34 $
58   */
59  //TODO better rename class to SignCertificate as non-leaf certs are now supported?
60  public class SignLeafCertificate extends Command
61  {    
62      public SignLeafCertificate(Map args)
63      {
64          super(args);
65      }
66      
67      public void usageMessage(PrintStream out)
68      {
69          out.println(
70  "Command:  sign\n\n" +
71  
72  "    Signs an MHP certificate\n\n" +
73  
74  "    Arguments:\n\n" +
75  
76  "        csrFile:     Certificate signing request file\n" +
77  "        certFile:    File containing signer's X509 certificate\n" +
78  "        keyFile:     File containing signer's private key\n" +
79  "        validFrom:   Date cert to be valid from, in dd/mm/yyyy format\n" +
80  "        validUntil:  Date cert to be valid until, in dd/mm/yyyy format\n" +
81  "        file:        Where to store the results\n\n" +
82  
83  "    Optional arguments:\n\n" +
84  "        leaf:        Whether the certificate is a leaf (true, default)\n" +
85  "                     or not (false)\n");
86      }
87      
88      public String[] getRequiredArgs()
89      {
90          return new String[] { "csrFile:", "certFile:", "keyFile:", 
91              "validFrom:", "validUntil:", "file:" };
92      }
93      
94      public String[] getOptionalArgs()
95      {
96        return new String[] { "leaf:"};
97      }
98          
99      private X509CertificateObject readIssuerCert() throws Exception
100     {
101         String fn = getArg("certFile:");
102         System.out.println("Reading issuer cert from " + fn + ".");
103         ByteArrayInputStream bis = 
104             new ByteArrayInputStream(readBytesFromFile(fn));
105         return readX509(bis);
106     }
107          
108     private PrivateKey readIssuerKey() throws Exception
109     {
110         String fn = getArg("keyFile:");
111         System.out.println("Reading issuer key from " + fn + ".");
112         byte[] encoded = readBytesFromFile(fn);
113         PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
114         KeyFactory fact = KeyFactory.getInstance("RSA", "BC");
115         PrivateKey key = fact.generatePrivate(spec);
116         return key;
117     }
118     
119     public void run() throws Exception
120     {
121         String token = getArg("leaf:");
122         boolean isLeaf = token == null || token.equals("true"); 
123         System.out.println(
124           "Signing " + (isLeaf ? "leaf " : "") + "certificate request.");
125         
126         // Do a bit of argument checking before time-consuming operations...
127         getDateArg("validFrom:");
128         getDateArg("validUntil:");
129         
130         X509CertificateObject issuerCert = readIssuerCert();
131         PrivateKey issuerKey = readIssuerKey();
132         
133         // Read request file        
134         PKCS10CertificationRequest csr = new PKCS10CertificationRequest(
135             readBytesFromFile(getArg("csrFile:")));        
136         
137         // Is there a better way to get these from csr?
138         String subject = 
139             csr.getCertificationRequestInfo().getSubject().toString();
140         String c = null, cn = null, o = null, e = null;
141         StringTokenizer st = new StringTokenizer(subject, ",");
142         while (st.hasMoreTokens())
143         {
144             token = st.nextToken();
145             if (token.startsWith("C="))
146             {
147                 c = token.substring(2);
148             }
149             else if (token.startsWith("CN="))
150             {
151                 cn = token.substring(3);
152             }
153             else if (token.startsWith("O="))
154             {
155                 o = token.substring(2);
156             }
157             else if (token.startsWith("E="))
158             {
159                 e = token.substring(2);
160             }
161         }
162         
163         // Generate leaf certificate
164         X509V3CertificateGenerator cg = new X509V3CertificateGenerator();
165         cg.reset();
166         cg.setIssuerDN((X509Name)issuerCert.getSubjectDN());
167         Hashtable ht = new Hashtable();                
168         ht.put(X509Name.C, c);
169         ht.put(X509Name.CN, cn);
170         ht.put(X509Name.O, o);
171         cg.setSubjectDN(new X509Name(ht));
172         cg.setNotBefore(getDateArg("validFrom:"));
173         cg.setNotAfter(getDateArg("validUntil:"));
174         cg.setPublicKey(csr.getPublicKey());
175         cg.setSerialNumber(BigInteger.valueOf(1));
176         cg.setSignatureAlgorithm("MD5WITHRSA"); // SHA1WITHRSA is OK, too
177         
178         cg.addExtension(X509Extensions.KeyUsage.getId(), true,
179         new X509KeyUsage(
180         isLeaf ? X509KeyUsage.digitalSignature : X509KeyUsage.keyCertSign));
181         
182         GeneralName gn = new GeneralName(new DERIA5String(e), 1);
183         DERConstructedSequence seq = new DERConstructedSequence();
184         seq.addObject(gn);
185         cg.addExtension(X509Extensions.SubjectAlternativeName.getId(), false,
186             new GeneralNames(seq));
187         
188         Collection issuerNames = issuerCert.getSubjectAlternativeNames();
189         if (issuerNames != null) {
190             Iterator iter = issuerNames.iterator();
191             while (iter.hasNext()) {
192                 List generalNames = (List) iter.next();
193                 //find the first rfc822Name (email address)
194                 if (((Integer) generalNames.get(0)).intValue() == 1) {
195                     Object name = generalNames.get(1);
196                     DERIA5String derString;
197                     if (name instanceof String) {
198                         derString = new DERIA5String((String) name);
199                     } else {
200                         derString = new DERIA5String((byte[]) name);
201                     }
202                     seq = new DERConstructedSequence();
203                     seq.addObject(new GeneralName(derString, 1));
204                     cg.addExtension(
205                         X509Extensions.IssuerAlternativeName.getId(),
206                         false, new GeneralNames(seq));
207                     break; //while (iter.hasNext())
208                 }
209             }
210         }
211         
212         X509Certificate cert = cg.generateX509Certificate(issuerKey, "BC");
213 
214         // Now, write leaf certificate
215         String fn = getArg("file:") + "_public.x509.crt";
216         System.out.println("Writing cert to " + fn + ".");
217         FileOutputStream str = new FileOutputStream(fn);
218         str.write(cert.getEncoded());
219         str.close();
220 
221         System.out.println();
222         System.out.println("Done!");
223         System.out.println();
224     }
225 }