1 /*
2  * @(#) $Id: FileBasedCADatabase.java,v 1.3 2003/07/08 08:13:52 pankaj Exp $
3  *
4  * Copyright (c) 2002-03 by Pankaj Kumar (http://www.pankaj-k.net). 
5  * All rights reserved.
6  *
7  * The license governing the use of this file can be found in the 
8  * root directory of the containing software.
9  */
10package org.jstk.cert.ca;
11
12import java.security.cert.Certificate;
13import java.security.cert.CertificateFactory;
14import java.security.cert.CertPath;
15import java.security.PrivateKey;
16import java.security.KeyStore;
17import java.security.KeyStoreException;
18import java.security.NoSuchAlgorithmException;
19import java.math.BigInteger;
20import java.util.Vector;
21import java.io.File;
22import java.io.FileInputStream;
23import java.io.FileOutputStream;
24import java.io.FileReader;
25import java.io.FileWriter;
26import java.io.BufferedReader;
27import java.io.PrintWriter;
28import java.io.IOException;
29
30public class FileBasedCADatabase implements CADatabaseSpi {
31    public static final String KEYSTORE = "ca.ks";
32    public static final String STORETYPE = "JCEKS";
33    public static final String ALIAS = "cakey";
34    public static final String STOREPASS = "changeit";
35    public static final String KEYPASS = "changeit";
36    public static final String ISSUED_DIR = "issued";
37    public static final String REVOKED_DIR = "revoked";
38    public static final String ISSUED_CERTS = "issued.certs";
39    public static final String REVOKED_CERTS = "revoked.certs";
40    public static final String SERIALNO_FILE = "serialno.cur";
41    public static final String START_SERIALNO = "999";
42
43    public static final BigInteger ONE = new BigInteger("1");
44
45    private String ksFileName;
46    private String serialNoFileName;
47    private String caDirName;
48    private PrivateKey caPrivateKey;
49    private Certificate caCert;
50    private CertPath caCertPath;
51    private File caDir;
52    private boolean initialized = false;
53    private String password;
54
55    public FileBasedCADatabase(FileBasedCADatabaseParams params) throws CADatabaseException {
56        String caDirName = params.getCADirName();
57        try {
58            password = params.getPassword();
59            if (!params.getCreateCA()){
60                init(caDirName);
61            } else {
62                create(caDirName, params.getCACerts(), params.getCAPrivateKey());
63            }
64        } catch (CADatabaseException de){
65            throw de;
66        } catch (Exception e){
67            throw new CADatabaseException("FileBasedCADatabase constructor failed.", e);
68        }
69        initialized = true;
70    }
71
72    private void init(String caDirName) throws Exception {
73
74        // Check for existence of caDirName and proper permissions
75        this.caDirName = caDirName;
76        caDir = new File(caDirName);
77        if (!caDir.exists()){
78            throw new CADatabaseException("Directory not found: " + caDirName);
79        } else if (!caDir.isDirectory()){
80            throw new CADatabaseException("Not a directory: " + caDirName);
81        } else if (!caDir.canRead()){
82            throw new CADatabaseException("No read permission: " + caDirName);
83        } else if (!caDir.canWrite()){
84            throw new CADatabaseException("No write permission: " + caDirName);
85        }
86
87        // Check CA KeyStore. It must have the private key and the CA certificate.
88        ksFileName = caDirName + File.separator + KEYSTORE;
89        FileInputStream fis = new FileInputStream(ksFileName);
90
91        KeyStore caKeyStore = KeyStore.getInstance(STORETYPE);
92        caKeyStore.load(fis, password.toCharArray());
93        fis.close();
94        if (caKeyStore.isKeyEntry(ALIAS)) {
95            caPrivateKey = (PrivateKey)caKeyStore.getKey(ALIAS, password.toCharArray());
96            caCert = caKeyStore.getCertificate(ALIAS);
97            Certificate[] caCerts = caKeyStore.getCertificateChain(ALIAS);
98            if (caCert == null || caCerts == null){
99                throw new CADatabaseException("Certificate not found in keystore: " + ksFileName);
00            }
01            CertificateFactory certFac = CertificateFactory.getInstance("X.509");
02            Vector certVec = new Vector();
03            for (int i = 0; i < caCerts.length; i++){
04                certVec.add(caCerts[i]);
05            }
06            caCertPath = certFac.generateCertPath(certVec);
07        } else {
08            throw new CADatabaseException("PrivateKey not found in keystore: " + ksFileName);
09        }
10
11        // Check for SerialNo. file
12        serialNoFileName = caDirName + File.separator + SERIALNO_FILE;
13        File serialNoFile = new File(serialNoFileName);
14        if (!serialNoFile.exists()){
15            PrintWriter pw = new PrintWriter(new FileWriter(serialNoFileName));
16            pw.println(START_SERIALNO);
17            pw.close();
18        }
19    }
20
21    private void create(String caDirName, Certificate[] caCerts, PrivateKey caKey) throws Exception {
22
23        // Check for existence of caDirName and proper permissions
24        this.caDirName = caDirName;
25        caDir = new File(caDirName);
26        if (caDir.exists()){
27            throw new CADatabaseException("Directory exists: " + caDirName);
28        }
29        if (!caDir.mkdirs()){
30            throw new CADatabaseException("Directory creation failed: " + caDirName);
31        }
32
33        // Check CA KeyStore. It must have the private key and the CA certificate.
34        ksFileName = caDirName + File.separator + KEYSTORE;
35        FileOutputStream fos = new FileOutputStream(ksFileName);
36
37        KeyStore caKeyStore = KeyStore.getInstance(STORETYPE);
38        caKeyStore.load(null, password.toCharArray());
39        caKeyStore.setKeyEntry(ALIAS, caKey, password.toCharArray(), caCerts);
40
41        caKeyStore.store(fos, password.toCharArray());
42        fos.close();
43
44        String issuedCertsDirName = caDirName + File.separator + ISSUED_DIR;
45        File issuedCertsDir = new File(issuedCertsDirName);
46        issuedCertsDir.mkdirs();
47
48        String revokedCertsDirName = caDirName + File.separator + REVOKED_DIR;
49        File revokedCertsDir = new File(revokedCertsDirName);
50        revokedCertsDir.mkdirs();
51
52        // Create Serial Number file.
53        serialNoFileName = caDirName + File.separator + SERIALNO_FILE;
54        File serialNoFile = new File(serialNoFileName);
55        if (!serialNoFile.exists()){
56            PrintWriter pw = new PrintWriter(new FileWriter(serialNoFileName));
57            pw.println(START_SERIALNO);
58            pw.close();
59        }
60    }
61
62    public IssuedCerts getIssuedCerts(){
63        String issuedCertsFileName = caDirName + File.separator + ISSUED_CERTS;
64        String issuedCertsDirName = caDirName + File.separator + ISSUED_DIR;
65        return new FileBasedIssuedCerts(issuedCertsFileName, issuedCertsDirName);
66    }
67
68    public RevokedCerts getRevokedCerts(){
69        String revokedCertsFileName = caDirName + File.separator + REVOKED_CERTS;
70        String revokedCertsDirName = caDirName + File.separator + REVOKED_DIR;
71        return new FileBasedRevokedCerts(revokedCertsFileName, revokedCertsDirName);
72    }
73
74    public Certificate getCACert(){
75        return caCert;
76    }
77    public CertPath getCACertPath(){
78        return caCertPath;
79    }
80    public PrivateKey getCAPrivateKey(){
81        return caPrivateKey;
82    }
83
84    public synchronized BigInteger nextSerialNumber() throws CADatabaseException {
85        try {
86            BufferedReader br = new BufferedReader(new FileReader(serialNoFileName));
87            String serialNoString = br.readLine();
88            BigInteger serialNo = new BigInteger(serialNoString);
89            br.close();
90            serialNo = serialNo.add(ONE);
91            PrintWriter pw = new PrintWriter(new FileWriter(serialNoFileName));
92            pw.println(serialNo.toString());
93            pw.close();
94            return serialNo;
95        } catch (Exception e){
96            throw new CADatabaseException("cannot get next serial number.", e);
97        }
98    }
99}
00
01