pkcs11 key/cert import for CA use
[aaf/authz.git] / auth / auth-certman / src / main / java / org / onap / aaf / auth / cm / ca / LocalCA.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21 package org.onap.aaf.auth.cm.ca;
22
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileReader;
26 import java.io.IOException;
27 import java.math.BigInteger;
28 import java.security.GeneralSecurityException;
29 import java.security.KeyStore;
30 import java.security.KeyStore.Entry;
31 import java.security.KeyStore.PrivateKeyEntry;
32 import java.security.KeyStoreException;
33 import java.security.NoSuchAlgorithmException;
34 import java.security.PrivateKey;
35 import java.security.Provider;
36 import java.security.SecureRandom;
37 import java.security.UnrecoverableEntryException;
38 import java.security.cert.CertificateException;
39 import java.security.cert.X509Certificate;
40 import java.security.interfaces.RSAPublicKey;
41 import java.util.ArrayList;
42 import java.util.Date;
43 import java.util.GregorianCalendar;
44 import java.util.List;
45
46 import org.bouncycastle.asn1.x500.X500Name;
47 import org.bouncycastle.asn1.x500.X500NameBuilder;
48 import org.bouncycastle.asn1.x509.BasicConstraints;
49 import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
50 import org.bouncycastle.asn1.x509.Extension;
51 import org.bouncycastle.asn1.x509.GeneralName;
52 import org.bouncycastle.asn1.x509.GeneralNames;
53 import org.bouncycastle.asn1.x509.KeyPurposeId;
54 import org.bouncycastle.asn1.x509.KeyUsage;
55 import org.bouncycastle.cert.X509v3CertificateBuilder;
56 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
57 import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
58 import org.bouncycastle.crypto.params.RSAKeyParameters;
59 import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
60 import org.bouncycastle.operator.OperatorCreationException;
61 import org.onap.aaf.auth.cm.cert.BCFactory;
62 import org.onap.aaf.auth.cm.cert.CSRMeta;
63 import org.onap.aaf.auth.cm.cert.RDN;
64 import org.onap.aaf.auth.env.NullTrans;
65 import org.onap.aaf.cadi.Access;
66 import org.onap.aaf.cadi.Access.Level;
67 import org.onap.aaf.cadi.cm.CertException;
68 import org.onap.aaf.cadi.cm.Factory;
69 import org.onap.aaf.misc.env.Env;
70 import org.onap.aaf.misc.env.TimeTaken;
71 import org.onap.aaf.misc.env.Trans;
72
73 public class LocalCA extends CA {
74
75         // Extensions
76         private static final KeyPurposeId[] ASN_WebUsage = new KeyPurposeId[] {
77                                 KeyPurposeId.id_kp_serverAuth, // WebServer
78                                 KeyPurposeId.id_kp_clientAuth};// WebClient
79                                 
80         private final PrivateKey caKey;
81         private final X500Name issuer;
82         private final SecureRandom random = new SecureRandom();
83         private byte[] serialish;
84         private final X509ChainWithIssuer x509cwi; // "Cert" is CACert
85
86         public LocalCA(Access access, final String name, final String env, final String[][] params) throws IOException, CertException {
87                 super(access, name, env);
88                 serialish = new byte[24];
89                 if(params.length<1 || params[0].length<2) {
90                         throw new IOException("LocalCA expects cm_ca.<ca name>=org.onap.aaf.auth.cm.ca.LocalCA,<full path to key file>[;<Full Path to Trust Chain, ending with actual CA>]+");
91                 }
92                 
93                 // Read in the Private Key
94                 String configured;
95                 File f = new File(params[0][0]);
96                 if(f.exists() && f.isFile()) {
97                         String fileName = f.getName();
98                         if(fileName.endsWith(".key")) {
99                                 caKey = Factory.toPrivateKey(NullTrans.singleton(),f);
100                                 List<FileReader> frs = new ArrayList<FileReader>(params.length-1);
101                                 try {
102                                         String dir = access.getProperty(CM_PUBLIC_DIR, "");
103                                         if(!"".equals(dir) && !dir.endsWith("/")) {
104                                                 dir = dir + '/';
105                                         }
106
107                                         String path;
108                                         for(int i=1; i<params[0].length; ++i) { // first param is Private Key, remainder are TrustChain
109                                                 path = !params[0][i].contains("/")?dir+params[0][i]:params[0][i];
110                                                 access.printf(Level.INIT, "Loading a TrustChain Member for %s from %s\n",name, path);
111                                                 frs.add(new FileReader(path));
112                                         }
113                                         x509cwi = new X509ChainWithIssuer(frs);
114                                 } finally {
115                                         for(FileReader fr : frs) {
116                                                 if(fr!=null) {
117                                                         fr.close();
118                                                 }
119                                         }
120                                 }
121                                 configured = "Configured with " + fileName;
122                         } else {
123                                 if(params.length<1 || params[0].length<3) {
124                                         throw new CertException("LocalCA parameters must be <keystore [.p12|.pkcs12|.jks|.pkcs11(sun only)]; <alias>; enc:<encrypted Keystore Password>>");
125                                 }
126                                 try {
127                                         Provider p;
128                                         KeyStore keyStore;
129                                         FileInputStream fis = null;
130                                         if(fileName.endsWith(".pkcs11")) {
131                                                 String ksType;
132                                                 p = Factory.getSecurityProvider(ksType="PKCS11",params);
133                                                 keyStore = KeyStore.getInstance(ksType,p);
134                                         } else if(fileName.endsWith(".jks")) {
135                                                 keyStore = KeyStore.getInstance("JKS");
136                                                 fis = new FileInputStream(f);
137                                         } else if(fileName.endsWith(".p12") || fileName.endsWith(".pkcs12")) {
138                                                 keyStore = KeyStore.getInstance("PKCS12");
139                                                 fis = new FileInputStream(f);
140                                         } else {
141                                                 throw new CertException("Unknown Keystore type from filename " + fileName);
142                                         }
143                                         
144                                         KeyStore.ProtectionParameter keyPass;
145
146                                         try {
147                                                 String pass = access.decrypt(params[0][2]/*encrypted passcode*/, true);
148                                                 if(pass==null) {
149                                                         throw new CertException("Passcode for " + fileName + " cannot be decrypted.");
150                                                 }
151                                                 char[] ksPass = pass.toCharArray();
152                                                 //Assuming Key Pass is same as Keystore Pass
153                                                 keyPass = new KeyStore.PasswordProtection(ksPass);
154
155                                                 keyStore.load(fis,ksPass);
156                                         } finally {
157                                                 if (fis != null)
158                                                         fis.close();
159                                         }
160                                         Entry entry;
161                                         if(fileName.endsWith(".pkcs11")) {
162                                                 entry = keyStore.getEntry(params[0][1]/*alias*/, null);
163                                         } else {
164                                                 entry = keyStore.getEntry(params[0][1]/*alias*/, keyPass);
165                                         }
166                                         if(entry==null) {
167                                                 throw new CertException("There is no Keystore entry with name '" + params[0][1] +'\'');
168                                         }
169                                         PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry)entry;
170                                         caKey = privateKeyEntry.getPrivateKey();
171                                         
172                                         x509cwi = new X509ChainWithIssuer(privateKeyEntry.getCertificateChain());
173                                         configured =  "keystore \"" + fileName + "\", alias " + params[0][1];
174                                 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableEntryException e) {
175                                         throw new CertException("Exception opening Keystore " + fileName, e);
176                                 }
177                         }
178                 } else {
179                         throw new CertException("Private Key, " + f.getPath() + ", does not exist");
180                 }
181                 
182                 X500NameBuilder xnb = new X500NameBuilder();
183                 for(RDN rnd : RDN.parse(',', x509cwi.getIssuerDN())) {
184                         xnb.addRDN(rnd.aoi,rnd.value);
185                 }
186                 issuer = xnb.build();
187                 access.printf(Level.INIT, "LocalCA is configured with %s.  The Issuer DN is %s.",
188                                 configured, issuer.toString());
189         }
190
191         /* (non-Javadoc)
192          * @see org.onap.aaf.auth.cm.service.CA#sign(org.bouncycastle.pkcs.PKCS10CertificationRequest)
193          */
194         @Override
195         public X509andChain sign(Trans trans, CSRMeta csrmeta) throws IOException, CertException {
196                 GregorianCalendar gc = new GregorianCalendar();
197                 Date start = gc.getTime();
198                 gc.add(GregorianCalendar.MONTH, 2);
199                 Date end = gc.getTime();
200                 X509Certificate x509;
201                 TimeTaken tt = trans.start("Create/Sign Cert",Env.SUB);
202                 try {
203                         BigInteger bi;
204                         synchronized(serialish) {
205                                 random.nextBytes(serialish);
206                                 bi = new BigInteger(serialish);
207                         }
208                                 
209                         RSAPublicKey rpk = (RSAPublicKey)csrmeta.keypair(trans).getPublic();
210                         X509v3CertificateBuilder xcb = new X509v3CertificateBuilder(
211                                         issuer,
212                                         bi, // replace with Serialnumber scheme
213                                         start,
214                                         end,
215                                         csrmeta.x500Name(),
216                                         SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(new RSAKeyParameters(false,rpk.getModulus(),rpk.getPublicExponent()))
217 //                                      new SubjectPublicKeyInfo(ASN1Sequence.getInstance(caCert.getPublicKey().getEncoded()))
218                                         );
219                         List<GeneralName> lsan = new ArrayList<GeneralName>();
220                         for(String s : csrmeta.sans()) {
221                                 lsan.add(new GeneralName(GeneralName.dNSName,s));
222                         }
223                         GeneralName[] sans = new GeneralName[lsan.size()];
224                         lsan.toArray(sans);
225
226                     JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
227                         xcb.addExtension(Extension.basicConstraints,
228                         false, new BasicConstraints(false))
229                             .addExtension(Extension.keyUsage,
230                                 true, new KeyUsage(KeyUsage.digitalSignature
231                                                  | KeyUsage.keyEncipherment))
232                             .addExtension(Extension.extendedKeyUsage,
233                                           true, new ExtendedKeyUsage(ASN_WebUsage))
234
235                     .addExtension(Extension.authorityKeyIdentifier,
236                                           false, extUtils.createAuthorityKeyIdentifier(x509cwi.cert))
237                             .addExtension(Extension.subjectKeyIdentifier,
238                                           false, extUtils.createSubjectKeyIdentifier(x509cwi.cert.getPublicKey()))
239                             .addExtension(Extension.subjectAlternativeName,
240                                         false, new GeneralNames(sans))
241                                                            ;
242         
243                         x509 = new JcaX509CertificateConverter().getCertificate(
244                                         xcb.build(BCFactory.contentSigner(caKey)));
245                 } catch (GeneralSecurityException|OperatorCreationException e) {
246                         throw new CertException(e);
247                 } finally {
248                         tt.done();
249                 }
250                 
251                 return new X509ChainWithIssuer(x509cwi,x509);
252         }
253
254 }