001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package dummyCA;
028:
029: import java.security.spec.X509EncodedKeySpec;
030: import java.security.spec.InvalidKeySpecException;
031: import java.security.*;
032: import java.security.cert.CertificateEncodingException;
033: import java.io.*;
034: import java.math.BigInteger;
035: import java.util.Calendar;
036: import java.util.TimeZone;
037:
038: /**
039: * This class represents certificate authority.
040: */
041: public class Authority {
042:
043: /** Keystore name for CA credentials. */
044: private static String keystoreFilename = "j2se_test_keystore.bin";
045: /** Keystore password. */
046: private static String keystorePassword = "keystorepwd";
047: /** Alias for CA keys. */
048: private static String keyAlias = "dummyca";
049: /** CA key password. */
050: private static String keyPassword = "keypwd";
051: /** Algorithm name for CA signature for new certificates. */
052: private static String CASign = "SHA1withRSA";
053:
054: /**
055: * Signature algorithm attributes - OID, crypto algorithm ID, signature
056: * algorithm ID.
057: */
058: private static String[][] algorithms = {
059: { "1.2.840.10040.4.3", "DSA", "SHA1withDSA" },
060: { "1.2.840.113549.1.1.4", "RSA", "MD5withRSA" },
061: { "1.2.840.113549.1.1.5", "RSA", "SHA1withRSA" },
062: { "1.2.840.10045.4.1", "ECDSA", "ECDSA" } };
063:
064: /** Is this authority initialized? */
065: private static boolean init;
066: /** CA certificate. */
067: private static java.security.cert.X509Certificate CACert;
068: /** CA private key. */
069: private static PrivateKey CAPrivKey;
070: /** Current serial number for new certificate. */
071: private static long SerialNumber;
072: /** TLV structure to be used in new certificates. */
073: private static TLV CASignatureAlgorithm;
074: /** TLV structure to be used for generation of new certificates. */
075: private static TLV CACertPointer;
076:
077: /**
078: * Initializes the CA.
079: * @param o servlet instance that should be used to obtain keystore or
080: * null if keystore should be opened as file.
081: * @return true if initialization was successful.
082: */
083: synchronized public static boolean init(Object o) {
084:
085: if (init) {
086: return true;
087: }
088:
089: try {
090: InputStream keystoreStream;
091:
092: if (o == null) {
093: keystoreStream = new FileInputStream(new File(
094: keystoreFilename));
095: } else {
096: keystoreStream = o.getClass().getResourceAsStream(
097: keystoreFilename);
098: }
099: KeyStore jcaKeystore = KeyStore.getInstance(KeyStore
100: .getDefaultType());
101:
102: try {
103:
104: if (keystorePassword == null) {
105: jcaKeystore.load(keystoreStream, null);
106: } else {
107: jcaKeystore.load(keystoreStream, keystorePassword
108: .toCharArray());
109: }
110: } finally {
111: keystoreStream.close();
112: }
113:
114: // retrieve CA certificate and private key
115:
116: CACert = (java.security.cert.X509Certificate) jcaKeystore
117: .getCertificate(keyAlias);
118: CAPrivKey = (PrivateKey) jcaKeystore.getKey(keyAlias,
119: keyPassword.toCharArray());
120:
121: String CASignOID = null;
122: for (int i = 0; i < algorithms.length; i++) {
123: if (CASign.equals(algorithms[i][2])) {
124: CASignOID = algorithms[i][0];
125: break;
126: }
127: }
128:
129: if (CASignOID == null) {
130: return false;
131: }
132:
133: // CA signature algorithm identifier
134: CASignatureAlgorithm = new TLV(TLV.SEQUENCE_TYPE);
135: CASignatureAlgorithm.child = new TLV(TLV.OID_TYPE, TLV
136: .StringToOID(CASignOID));
137: CASignatureAlgorithm.child.next = new TLV(TLV.NULL_TYPE,
138: new byte[0]);
139:
140: // Parse CA certificate
141: CACertPointer = new TLV(CACert.getEncoded(), 0);
142:
143: CACertPointer = CACertPointer.child.child;
144: if (CACertPointer.type == TLV.VERSION_TYPE) {
145: CACertPointer = CACertPointer.next;
146: }
147: // CACertPointer is at SerialNumber field
148:
149: } catch (Exception e) {
150: return false;
151: }
152:
153: // serial number initial value
154:
155: Calendar c = Calendar.getInstance();
156: long t = c.get(Calendar.YEAR) - 2000;
157: t = t * 365 + c.get(Calendar.DAY_OF_YEAR) - 1;
158: t = t * 24 + c.get(Calendar.HOUR_OF_DAY);
159: t = t * 60 + c.get(Calendar.MINUTE);
160: t = t * 60 + c.get(Calendar.SECOND);
161: t = t * 1000 + c.get(Calendar.MILLISECOND);
162: SerialNumber = t;
163:
164: init = true;
165:
166: return true;
167: }
168:
169: /**
170: * Returns the serial number value to be used for new certificate.
171: * @return the serial number value.
172: */
173: synchronized private static long getSerialNumber() {
174: return SerialNumber++;
175: }
176:
177: /** Parsed certificate enrollment request. */
178: private TLV CSR;
179: /** Generated certificate. */
180: private TLV Certificate;
181: /** Generated IssuerAndSerialNumber data structure. */
182: private TLV IssuerAndSerialNumber;
183: /** Current status of CA. */
184: private String Status = "Ready.";
185:
186: /**
187: * Creates a new certificate.
188: * @param data certificate enrollment request
189: * @return true if a new certificate was generated
190: */
191: public boolean createCertificate(byte[] data) {
192:
193: if (!init) {
194: Status = "Can't load CA credentials.";
195: return false;
196: }
197:
198: try {
199: CSR = new TLV(data, 0);
200: } catch (Exception e) {
201: Status = "Error parsing the CSR.";
202: return false;
203: }
204:
205: try {
206: if (!checkSign()) {
207: Status = "Signature mismatch.";
208: return false;
209: }
210: } catch (Exception e) {
211: Status = "Can't check signature.";
212: return false;
213: }
214:
215: try {
216: create();
217: } catch (Exception e) {
218: Status = "Can't create certificate.";
219: return false;
220: }
221:
222: ByteArrayOutputStream os = new ByteArrayOutputStream();
223: PrintStream ps = new PrintStream(os);
224: ps.println("Last CSR:");
225: CSR.print(ps);
226: ps.println();
227: ps.println("Certificate:");
228: Certificate.print(ps);
229: ps.println();
230: ps.println("IssuerAndSerialNumber:");
231: IssuerAndSerialNumber.print(ps);
232: ps.close();
233: Status = os.toString();
234:
235: return true;
236: }
237:
238: /**
239: * Returns current status.
240: * @return current status
241: */
242: public String getStatus() {
243: return Status;
244: }
245:
246: /**
247: * Verifies signature in certificate enrollment request.
248: * @return true if signature is verified
249: * @throws IOException if IOException occurs
250: * @throws NoSuchAlgorithmException if NoSuchAlgorithmException occurs
251: * @throws InvalidKeySpecException if InvalidKeySpecException occurs
252: * @throws InvalidKeyException if InvalidKeyException occurs
253: * @throws SignatureException if SignatureException occurs
254: */
255: private boolean checkSign() throws IOException,
256: NoSuchAlgorithmException, InvalidKeySpecException,
257: InvalidKeyException, SignatureException {
258:
259: String algorithmOID = CSR.child.next.child.getOID();
260: String cryptoAlg = "";
261: String signAlg = "";
262:
263: for (int i = 0; i < algorithms.length; i++) {
264: if (algorithmOID.equals(algorithms[i][0])) {
265: cryptoAlg = algorithms[i][1];
266: signAlg = algorithms[i][2];
267: break;
268: }
269: }
270:
271: byte[] subjectPKInfo = CSR.child.child.next.next.getDERData();
272: X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
273: subjectPKInfo);
274: KeyFactory factory = KeyFactory.getInstance(cryptoAlg);
275: PublicKey key = factory.generatePublic(keySpec);
276:
277: Signature sig = Signature.getInstance(signAlg);
278: sig.initVerify(key);
279: sig.update(CSR.child.getDERData());
280:
281: byte[] sign = CSR.child.next.next.getValue();
282: byte[] signature = new byte[sign.length - 1];
283: System.arraycopy(sign, 1, signature, 0, signature.length);
284: return sig.verify(signature);
285: }
286:
287: /**
288: * Creates a new certificate.
289: * @throws NoSuchAlgorithmException if an exception occurs during signature
290: * operation
291: * @throws InvalidKeyException if an exception occurs during signature
292: * operation
293: * @throws SignatureException if an exception occurs during signature
294: * operation
295: */
296: private void create() throws NoSuchAlgorithmException,
297: InvalidKeyException, SignatureException {
298:
299: // Prepare TBSCertificate data structure
300: TLV TBSCert = new TLV(TLV.SEQUENCE_TYPE);
301:
302: // serial number
303: BigInteger serialNumber = BigInteger.valueOf(getSerialNumber());
304:
305: TLV current = new TLV(TLV.INTEGER_TYPE, serialNumber
306: .toByteArray());
307: TBSCert.child = current;
308:
309: // signature algorithm identifier (CA)
310: current.next = CASignatureAlgorithm.copy();
311: current = current.next;
312:
313: // issuer
314: current.next = CACertPointer.next.next.next.next.copy();
315: current = current.next;
316:
317: // validity
318: current.next = new TLV(TLV.SEQUENCE_TYPE);
319: current = current.next;
320:
321: Calendar calendar = Calendar.getInstance();
322: calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
323: current.child = TLV.createUTCTime(calendar);
324: calendar.add(Calendar.DAY_OF_MONTH, 30);
325: current.child.next = TLV.createUTCTime(calendar);
326:
327: // subject
328: current.next = CSR.child.child.next.copy();
329: current = current.next;
330:
331: // subject public key info
332: current.next = CSR.child.child.next.next.copy();
333:
334: // TBSCertificate is complete, now sign it
335:
336: Signature s = Signature.getInstance(CASign);
337: s.initSign(CAPrivKey);
338: s.update(TBSCert.getDERData());
339:
340: byte[] sign1 = s.sign();
341: byte[] sign2 = new byte[sign1.length + 1];
342: System.arraycopy(sign1, 0, sign2, 1, sign1.length);
343:
344: // create Certificate data structure
345:
346: Certificate = new TLV(TLV.SEQUENCE_TYPE);
347:
348: // TBSCertificate
349: Certificate.child = TBSCert;
350:
351: // signatureAlgorithm
352: current = CASignatureAlgorithm.copy();
353: TBSCert.next = current;
354:
355: // signatureValue
356: current.next = new TLV(TLV.BITSTRING_TYPE, sign2);
357:
358: // create IssuerAndSerialNumber data structure
359:
360: IssuerAndSerialNumber = new TLV(TLV.SEQUENCE_TYPE);
361: IssuerAndSerialNumber.child = CACertPointer.next.next.next.next
362: .copy();
363: IssuerAndSerialNumber.child.next = new TLV(TLV.INTEGER_TYPE,
364: serialNumber.toByteArray());
365: }
366:
367: /**
368: * Returns PkiPath data structure for generated certificate.
369: * @return PkiPath data structure for generated certificate
370: */
371: public byte[] getPkiPath() {
372: TLV PkiPath = new TLV(TLV.SEQUENCE_TYPE);
373: try {
374: PkiPath.child = new TLV(CACert.getEncoded(), 0);
375: } catch (CertificateEncodingException e) {
376: // it was already requested during initialization without
377: // exception
378: }
379: PkiPath.child.next = Certificate;
380: return PkiPath.getDERData();
381: }
382:
383: /**
384: * Returns IssuerAndSerialNumber data structure for generated certificate.
385: * @return IssuerAndSerialNumber data structure for generated certificate
386: */
387: public byte[] getIssuerAndSerialNumber() {
388: return IssuerAndSerialNumber.getDERData();
389: }
390:
391: /**
392: * Returns DER encoded new certificate.
393: * @return DER encoded new certificate
394: */
395: public byte[] getCertificate() {
396: return Certificate.getDERData();
397: }
398: }
|