Fix Agent and CM Issues
[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.Collections;
43 import java.util.Date;
44 import java.util.GregorianCalendar;
45 import java.util.List;
46
47 import org.bouncycastle.asn1.x500.X500Name;
48 import org.bouncycastle.asn1.x500.X500NameBuilder;
49 import org.bouncycastle.asn1.x509.BasicConstraints;
50 import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
51 import org.bouncycastle.asn1.x509.Extension;
52 import org.bouncycastle.asn1.x509.GeneralName;
53 import org.bouncycastle.asn1.x509.GeneralNames;
54 import org.bouncycastle.asn1.x509.KeyPurposeId;
55 import org.bouncycastle.asn1.x509.KeyUsage;
56 import org.bouncycastle.cert.X509v3CertificateBuilder;
57 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
58 import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
59 import org.bouncycastle.crypto.params.RSAKeyParameters;
60 import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
61 import org.bouncycastle.operator.OperatorCreationException;
62 import org.onap.aaf.auth.cm.cert.BCFactory;
63 import org.onap.aaf.auth.cm.cert.CSRMeta;
64 import org.onap.aaf.auth.cm.cert.RDN;
65 import org.onap.aaf.auth.env.NullTrans;
66 import org.onap.aaf.cadi.Access;
67 import org.onap.aaf.cadi.Access.Level;
68 import org.onap.aaf.cadi.configure.CertException;
69 import org.onap.aaf.cadi.configure.Factory;
70 import org.onap.aaf.misc.env.Env;
71 import org.onap.aaf.misc.env.TimeTaken;
72 import org.onap.aaf.misc.env.Trans;
73
74 public class LocalCA extends CA {
75     private final static BigInteger ONE = new BigInteger("1");
76     // Extensions
77     private static final KeyPurposeId[] ASN_WebUsage = new KeyPurposeId[] {
78                 KeyPurposeId.id_kp_serverAuth, // WebServer
79                 KeyPurposeId.id_kp_clientAuth // WebClient
80                 };
81
82     private final PrivateKey caKey;
83     private final X500Name issuer;
84     private BigInteger serial;
85     private final X509ChainWithIssuer x509cwi; // "Cert" is CACert
86
87
88     public LocalCA(Access access, final String name, final String env, final String[][] params) throws IOException, CertException {
89         super(access, name, env);
90
91         serial = new BigInteger(64,new SecureRandom());
92
93         if (params.length<1 || params[0].length<2) {
94             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>]+");
95         }
96
97         // Read in the Private Key
98         String configured;
99         File f = new File(params[0][0]);
100         if (f.exists() && f.isFile()) {
101             String fileName = f.getName();
102             if (fileName.endsWith(".key")) {
103                 caKey = Factory.toPrivateKey(NullTrans.singleton(),f);
104                 List<FileReader> frs = new ArrayList<>(params.length-1);
105                 try {
106                     String dir = access.getProperty(CM_PUBLIC_DIR, "");
107                     if (!"".equals(dir) && !dir.endsWith("/")) {
108                         dir = dir + '/';
109                     }
110
111                     String path;
112                     for (int i=1; i<params[0].length; ++i) { // first param is Private Key, remainder are TrustChain
113                         path = !params[0][i].contains("/")?dir+params[0][i]:params[0][i];
114                         access.printf(Level.INIT, "Loading a TrustChain Member for %s from %s\n",name, path);
115                         frs.add(new FileReader(path));
116                     }
117                     x509cwi = new X509ChainWithIssuer(frs);
118                 } finally {
119                     for (FileReader fr : frs) {
120                         if (fr!=null) {
121                             fr.close();
122                         }
123                     }
124                 }
125                 configured = "Configured with " + fileName;
126             } else {
127                 if (params.length<1 || params[0].length<3) {
128                     throw new CertException("LocalCA parameters must be <keystore [.p12|.pkcs12|.jks|.pkcs11(sun only)]; <alias>; enc:<encrypted Keystore Password>>");
129                 }
130                 try {
131                     Provider p;
132                     KeyStore keyStore;
133                     FileInputStream fis = null;
134                     if (fileName.endsWith(".pkcs11")) {
135                         String ksType="PKCS11";
136                         p = Factory.getSecurityProvider(ksType,params);
137                         keyStore = KeyStore.getInstance(ksType,p);
138                     } else if (fileName.endsWith(".jks")) {
139                         keyStore = KeyStore.getInstance("JKS");
140                             fis = new FileInputStream(f);
141                     } else if (fileName.endsWith(".p12") || fileName.endsWith(".pkcs12")) {
142                         keyStore = KeyStore.getInstance("PKCS12");
143                             fis = new FileInputStream(f);
144                     } else {
145                         throw new CertException("Unknown Keystore type from filename " + fileName);
146                     }
147
148                     KeyStore.ProtectionParameter keyPass;
149
150                     try {
151                         String pass = access.decrypt(params[0][2]/*encrypted passcode*/, true);
152                         if (pass==null || pass.isEmpty()) {
153                             throw new CertException("Passcode for " + fileName + " cannot be decrypted.");
154                         }
155                         char[] ksPass = pass.toCharArray();
156                         //Assuming Key Pass is same as Keystore Pass
157                         keyPass = new KeyStore.PasswordProtection(ksPass);
158
159                         keyStore.load(fis,ksPass);
160                     } finally {
161                         if (fis != null) {
162                             fis.close();
163                         }
164                     }
165                     Entry entry;
166                     if (fileName.endsWith(".pkcs11")) {
167                         entry = keyStore.getEntry(params[0][1]/*alias*/, null);
168                     } else {
169                         entry = keyStore.getEntry(params[0][1]/*alias*/, keyPass);
170                     }
171                     if (entry==null) {
172                         throw new CertException("There is no Keystore entry with name '" + params[0][1] +'\'');
173                     }
174                     PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry)entry;
175                     caKey = privateKeyEntry.getPrivateKey();
176
177                     x509cwi = new X509ChainWithIssuer(privateKeyEntry.getCertificateChain());
178                     configured =  "keystore \"" + fileName + "\", alias " + params[0][1];
179                 } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableEntryException e) {
180                     throw new CertException("Exception opening Keystore " + fileName, e);
181                 }
182             }
183         } else {
184             throw new CertException("Private Key, " + f.getPath() + ", does not exist");
185         }
186
187         X500NameBuilder xnb = new X500NameBuilder();
188         List<RDN> rp = RDN.parse(',', x509cwi.getIssuerDN());
189         Collections.reverse(rp);
190         for (RDN rnd : rp) {
191             xnb.addRDN(rnd.aoi,rnd.value);
192         }
193         issuer = xnb.build();
194         access.printf(Level.INIT, "LocalCA is configured with %s.  The Issuer DN is %s.",
195                 configured, issuer.toString());
196     }
197
198     /* (non-Javadoc)
199      * @see org.onap.aaf.auth.cm.service.CA#sign(org.bouncycastle.pkcs.PKCS10CertificationRequest)
200      */
201     @Override
202     public X509andChain sign(Trans trans, CSRMeta csrmeta) throws IOException, CertException {
203         GregorianCalendar gc = new GregorianCalendar();
204         Date start = gc.getTime();
205         gc.add(GregorianCalendar.MONTH, 12);
206         Date end = gc.getTime();
207         X509Certificate x509;
208         TimeTaken tt = trans.start("Create/Sign Cert",Env.SUB);
209         try {
210             BigInteger bi;
211
212             synchronized(ONE) {
213                 bi = serial;
214                 serial = serial.add(ONE);
215             }
216
217             RSAPublicKey rpk = (RSAPublicKey)csrmeta.keypair(trans).getPublic();
218             X509v3CertificateBuilder xcb = new X509v3CertificateBuilder(
219                     issuer,
220                     bi, // replace with Serialnumber scheme
221                     start,
222                     end,
223                     csrmeta.x500Name(),
224                     SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(new RSAKeyParameters(false,rpk.getModulus(),rpk.getPublicExponent()))
225 //                    new SubjectPublicKeyInfo(ASN1Sequence.getInstance(caCert.getPublicKey().getEncoded()))
226                     );
227             List<GeneralName> lsan = new ArrayList<>();
228             // Email
229             lsan.add(new GeneralName(GeneralName.rfc822Name,csrmeta.email()));
230             for (String s : csrmeta.sans()) {
231                         if(IPV4_PATTERN.matcher(s).matches() || IPV6_PATTERN.matcher(s).matches()) {
232                         lsan.add(new GeneralName(GeneralName.iPAddress,s));
233                         } else {                
234                                 lsan.add(new GeneralName(GeneralName.dNSName,s));
235                         }
236             }
237             GeneralName[] sans = new GeneralName[lsan.size()];
238             lsan.toArray(sans);
239
240             JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
241                 xcb.addExtension(Extension.basicConstraints,
242                         false, new BasicConstraints(false
243                                 ))
244                     .addExtension(Extension.keyUsage,
245                         true, new KeyUsage(KeyUsage.digitalSignature
246                                          | KeyUsage.keyEncipherment
247                                          | KeyUsage.nonRepudiation))
248                     .addExtension(Extension.extendedKeyUsage,
249                                   true, new ExtendedKeyUsage(ASN_WebUsage))
250                     .addExtension(Extension.authorityKeyIdentifier,
251                               false, extUtils.createAuthorityKeyIdentifier(x509cwi.cert))
252                     .addExtension(Extension.subjectKeyIdentifier,
253                               false, extUtils.createSubjectKeyIdentifier(rpk))
254                     .addExtension(Extension.subjectAlternativeName,
255                             false, new GeneralNames(sans))
256 //                    .addExtension(MiscObjectIdentifiers.netscape, true, new NetscapeCertType(
257 //                            NetscapeCertType.sslClient|NetscapeCertType.sslClient))
258                     ;
259
260             x509 = new JcaX509CertificateConverter().getCertificate(
261                     xcb.build(BCFactory.contentSigner(caKey)));
262         } catch (GeneralSecurityException|OperatorCreationException e) {
263             throw new CertException(e);
264         } finally {
265             tt.done();
266         }
267
268         return new X509andChain(x509,x509cwi.trustChain);
269     }
270
271 }