[AAF-21] Updated Copyright Headers for AAF
[aaf/authz.git] / authz-certman / src / main / java / com / att / authz / cm / ca / AppCA.java
1 /*******************************************************************************\r
2  * ============LICENSE_START====================================================\r
3  * * org.onap.aaf\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 package com.att.authz.cm.ca;\r
24 \r
25 import java.io.File;\r
26 import java.io.IOException;\r
27 import java.net.Authenticator;\r
28 import java.net.MalformedURLException;\r
29 import java.net.PasswordAuthentication;\r
30 import java.net.URL;\r
31 import java.security.cert.CertStore;\r
32 import java.security.cert.CertStoreException;\r
33 import java.security.cert.Certificate;\r
34 import java.security.cert.CertificateException;\r
35 import java.security.cert.X509Certificate;\r
36 import java.util.ArrayList;\r
37 import java.util.Collection;\r
38 import java.util.Date;\r
39 import java.util.Iterator;\r
40 import java.util.List;\r
41 \r
42 import org.bouncycastle.operator.OperatorCreationException;\r
43 import org.bouncycastle.pkcs.PKCS10CertificationRequest;\r
44 import org.jscep.client.Client;\r
45 import org.jscep.client.ClientException;\r
46 import org.jscep.client.EnrollmentResponse;\r
47 import org.jscep.client.verification.CertificateVerifier;\r
48 import org.jscep.transaction.TransactionException;\r
49 \r
50 import com.att.authz.cm.cert.BCFactory;\r
51 import com.att.authz.cm.cert.CSRMeta;\r
52 import com.att.authz.cm.cert.StandardFields;\r
53 import com.att.authz.common.Define;\r
54 import com.att.cadi.cm.CertException;\r
55 import com.att.cadi.cm.Factory;\r
56 import com.att.cadi.config.Config;\r
57 import com.att.cadi.routing.GreatCircle;\r
58 import com.att.inno.env.Env;\r
59 import com.att.inno.env.TimeTaken;\r
60 import com.att.inno.env.Trans;\r
61 import com.att.inno.env.util.Split;\r
62 \r
63 public class AppCA extends CA {\r
64         public static final String CA_PERM_TYPE = Define.ROOT_NS+".ca"; // Permission Type for validation\r
65         private static final String AAF_DATA_DIR = "aaf_data_dir";\r
66         private static final String CA_PREFIX = "http://";\r
67         private static final String CA_POSTFIX="/certsrv/mscep_admin/mscep.dll";\r
68 \r
69         private final static String MS_PROFILE="1";\r
70         private static final String CM_TRUST_CAS = "cm_trust_cas";\r
71         private Clients clients;\r
72 \r
73         private static class AAFStdFields implements StandardFields {\r
74                 private final String env;\r
75                 public AAFStdFields(Trans trans) throws CertException {\r
76                         env = trans.getProperty(Config.AAF_ENV);\r
77                         if(env==null) {\r
78                                 throw new CertException(Config.AAF_ENV + " must be set to create Certificates");\r
79                         }\r
80                 }\r
81                 @Override\r
82                 public void set(CSRMeta csr) {\r
83                         // Environment\r
84                         csr.environment(env);\r
85                         // Standard Fields\r
86                         csr.o("ATT Services,Inc.");\r
87                         csr.l("St Louis");\r
88                         csr.st("Missouri");\r
89                         csr.c("US");\r
90                 }\r
91         }\r
92 \r
93         public AppCA(final Trans trans, final String name, final String urlstr, final String id, final String pw) throws IOException, CertificateException, CertException {\r
94                 super(name,new AAFStdFields(trans), CA_PERM_TYPE);\r
95                 \r
96                 clients = new Clients(trans,urlstr);\r
97                 \r
98                 \r
99                 // Set this for NTLM password Microsoft\r
100                 Authenticator.setDefault(new Authenticator() {\r
101                           public PasswordAuthentication getPasswordAuthentication () {\r
102                             return new PasswordAuthentication (\r
103                                         id,\r
104                                         trans.decryptor().decrypt(pw).toCharArray());\r
105                         }\r
106                 });\r
107 \r
108 \r
109 \r
110                 try {\r
111                         StringBuilder sb = new StringBuilder("CA Reported Trusted Certificates");\r
112                         List<X509Certificate> trustCerts = new ArrayList<X509Certificate>();\r
113                         for(Client client : clients) {\r
114                                 CertStore cs = client.getCaCertificate(MS_PROFILE);\r
115                                 \r
116                                 Collection<? extends Certificate> cc = cs.getCertificates(null);\r
117                                 for(Certificate c : cc) {\r
118                                         X509Certificate xc = (X509Certificate)c;\r
119                                         // Avoid duplicate Certificates from multiple servers\r
120                                         X509Certificate match = null;\r
121                                         for(X509Certificate t : trustCerts) {\r
122                                                 if(t.getSerialNumber().equals(xc.getSerialNumber())) {\r
123                                                         match = xc;\r
124                                                         break;\r
125                                                 }\r
126                                         }\r
127                                         if(match==null && xc.getSubjectDN().getName().startsWith("CN=ATT ")) {\r
128                                                 sb.append("\n\t");\r
129                                                 sb.append(xc.getSubjectDN());\r
130                                                 sb.append("\n\t\tSerial Number: ");\r
131                                                 String bi = xc.getSerialNumber().toString(16);\r
132                                                 for(int i=0;i<bi.length();++i) {\r
133                                                         if(i>1 && i%2==0) {\r
134                                                                 sb.append(':');\r
135                                                         }\r
136                                                         sb.append(bi.charAt(i));\r
137                                                 }\r
138                                                 sb.append("\n\t\tIssuer:        ");\r
139                                                 sb.append(xc.getIssuerDN());\r
140                                                 sb.append("\n\t\tNot Before:    ");\r
141                                                 sb.append(xc.getNotBefore());\r
142                                                 sb.append("\n\t\tNot After:     ");\r
143                                                 sb.append(xc.getNotAfter());\r
144                                                 sb.append("\n\t\tSigAlgorithm:  ");\r
145                                                 sb.append(xc.getSigAlgName());\r
146                                                 sb.append("\n\t\tType:          ");\r
147                                                 sb.append(xc.getType());\r
148                                                 sb.append("\n\t\tVersion:       ");\r
149                                                 sb.append(xc.getVersion());\r
150 \r
151                                                 trustCerts.add(xc);\r
152                                         }\r
153                                 }\r
154                         }\r
155                         trans.init().log(sb);\r
156                         // Add Additional ones from Property\r
157                         String data_dir = trans.getProperty(AAF_DATA_DIR);\r
158                         if(data_dir!=null) {\r
159                                 File data = new File(data_dir);\r
160                                 if(data.exists()) {\r
161                                         String trust_cas = trans.getProperty(CM_TRUST_CAS);\r
162                                         byte[] bytes;\r
163                                         if(trust_cas!=null) {\r
164                                                 for(String fname : Split.split(';', trust_cas)) {\r
165                                                         File crt = new File(data,fname);\r
166                                                         if(crt.exists()) {\r
167                                                                 bytes = Factory.decode(crt);\r
168                                                                 try {\r
169                                                                         Collection<? extends Certificate> cc = Factory.toX509Certificate(bytes);\r
170                                                                         for(Certificate c : cc) {\r
171                                                                                 trustCerts.add((X509Certificate)c);\r
172                                                                         }\r
173                                                                 } catch (CertificateException e) {\r
174                                                                         throw new CertException(e);\r
175                                                                 }\r
176                                                         }\r
177                                                 }\r
178                                         }\r
179                                 }\r
180                         }\r
181                         \r
182                         String[] trustChain = new String[trustCerts.size()];\r
183                         int i=-1;\r
184                         for( Certificate cert : trustCerts) {\r
185                                 trustChain[++i]=BCFactory.toString(trans,cert);\r
186                         }\r
187                         \r
188                         setTrustChain(trustChain);\r
189                 } catch (ClientException | CertStoreException e) {\r
190                         // Note:  Cannot validly start without all Clients, because we need to read all Issuing Certificates\r
191                         // This is acceptable risk for most things, as we're not real time in general\r
192                         throw new CertException(e);\r
193                 }\r
194         }\r
195 \r
196 \r
197         @Override\r
198         public X509Certificate sign(Trans trans, CSRMeta csrmeta) throws IOException, CertException {\r
199                 TimeTaken tt = trans.start("Generating CSR and Keys for New Certificate", Env.SUB);\r
200                 PKCS10CertificationRequest csr;\r
201                 try {\r
202                         csr = csrmeta.generateCSR(trans);\r
203                         if(trans.info().isLoggable()) {\r
204                                 trans.info().log(BCFactory.toString(trans, csr));\r
205                         } \r
206                         if(trans.info().isLoggable()) {\r
207                                 trans.info().log(csr);\r
208                         }\r
209                 } finally {\r
210                         tt.done();\r
211                 }\r
212                 \r
213                 tt = trans.start("Enroll CSR", Env.SUB);\r
214                 Client client = null;\r
215                 try {\r
216                         client = clients.best();\r
217                         EnrollmentResponse er = client.enrol(\r
218                                         csrmeta.initialConversationCert(trans),\r
219                                         csrmeta.keypair(trans).getPrivate(),\r
220                                         csr,\r
221                                         MS_PROFILE /* profile... MS can't deal with blanks*/);\r
222                         while(true) {\r
223                                 if(er.isSuccess()) {\r
224                                         for( Certificate cert : er.getCertStore().getCertificates(null)) {\r
225                                                 return (X509Certificate)cert;\r
226                                         }\r
227                                         break;\r
228                                 } else if (er.isPending()) {\r
229                                         trans.checkpoint("Polling, waiting on CA to complete");\r
230                                         Thread.sleep(3000);\r
231                                 } else if (er.isFailure()) {\r
232                                         throw new CertException(er.getFailInfo().toString());\r
233                                 }\r
234                         }\r
235                 } catch (ClientException e) {\r
236                         trans.error().log(e,"SCEP Client Error, Temporarily Invalidating Client");\r
237                         if(client!=null) {\r
238                                 clients.invalidate(client);\r
239                         }\r
240                 } catch (InterruptedException|TransactionException|CertificateException|OperatorCreationException | CertStoreException e) {\r
241                         trans.error().log(e);\r
242                 } finally {\r
243                         tt.done();\r
244                 }\r
245                 \r
246                 return null;\r
247         }\r
248 \r
249 \r
250         private class Clients implements Iterable<Client>{\r
251                 /**\r
252                  * CSO Servers are in Dallas and St Louis\r
253                  * GEO_LOCATION   LATITUDE    LONGITUDE    ZIPCODE   TIMEZONE\r
254                  * ------------   --------    ---------    -------   --------\r
255                  * DLLSTXCF       32.779295   -96.800014   75202     America/Chicago\r
256                  * STLSMORC       38.627345   -90.193774   63101     America/Chicago\r
257                  * \r
258                  * The online production issuing CA servers are:\r
259                  *      AAF - CADI Issuing CA 01        135.41.45.152   MOSTLS1AAFXXA02\r
260                  *      AAF - CADI Issuing CA 02        135.31.72.154   TXDLLS2AAFXXA02\r
261                  */\r
262                 \r
263                 private final Client[] client;\r
264                 private final Date[] failure;\r
265                 private int preferred;\r
266 \r
267                 public Clients(Trans trans, String urlstr) throws MalformedURLException { \r
268                         String[] urlstrs = Split.split(',', urlstr);\r
269                         client = new Client[urlstrs.length];\r
270                         failure = new Date[urlstrs.length];\r
271                         double distance = Double.MAX_VALUE;\r
272                         String localLat = trans.getProperty("AFT_LATITUDE","39.833333"); //Note: Defaulting to GEO center of US\r
273                         String localLong = trans.getProperty("AFT_LONGITUDE","-98.583333");\r
274                         for(int i=0;i<urlstrs.length;++i) {\r
275                                 String[] info = Split.split('/', urlstrs[i]);\r
276                                 if(info.length<3) {\r
277                                         throw new MalformedURLException("Configuration needs LAT and LONG, i.e. ip:port/lat/long");\r
278                                 }\r
279                                 client[i] = new Client(new URL(CA_PREFIX + info[0] + CA_POSTFIX), \r
280                                         new CertificateVerifier() {\r
281                                                 @Override\r
282                                                 public boolean verify(X509Certificate cert) {\r
283                                                         return true;\r
284                                                 }\r
285                                         }\r
286                                 );\r
287                                 double d = GreatCircle.calc(info[1],info[2],localLat,localLong);\r
288                                 if(d<distance) {\r
289                                         preferred = i;\r
290                                         distance=d;\r
291                                 }\r
292                         }\r
293                         trans.init().printf("Preferred Certificate Authority is %s",urlstrs[preferred]);\r
294                         for(int i=0;i<urlstrs.length;++i) {\r
295                                 if(i!=preferred) {\r
296                                         trans.init().printf("Alternate Certificate Authority is %s",urlstrs[i]);\r
297                                 }\r
298                         }\r
299                 }\r
300                 private Client best() throws ClientException {\r
301                         if(failure[preferred]==null) {\r
302                                 return client[preferred];\r
303                         } else {\r
304                                 Client c=null;\r
305                                 // See if Alternate available\r
306                                 for(int i=0;i<failure.length;++i) {\r
307                                         if(failure[i]==null) {\r
308                                                 c=client[i];\r
309                                         }\r
310                                 }\r
311                                 \r
312                                 // If not, see if any expirations can be cleared\r
313                                 Date now = new Date();\r
314                                 for(int i=0;i<failure.length;++i) {\r
315                                         if(now.after(failure[i])) {\r
316                                                 failure[i]=null;\r
317                                                 if(c==null) {\r
318                                                         c=client[i];\r
319                                                 }\r
320                                         }\r
321                                 }\r
322                                 \r
323                                 // if still nothing found, then throw.\r
324                                 if(c==null) {\r
325                                         throw new ClientException("No available machines to call");\r
326                                 } \r
327                                 return c;\r
328                         }\r
329                 }\r
330                 \r
331                 public void invalidate(Client clt) {\r
332                    for(int i=0;i<client.length;++i) {\r
333                            if(client[i].equals(clt)) {\r
334                                    failure[i]=new Date(System.currentTimeMillis()+180000 /* 3 mins */);\r
335                            }\r
336                    }\r
337                 }\r
338                 \r
339                 @Override\r
340                 public Iterator<Client> iterator() {\r
341                         return new Iterator<Client>() {\r
342                                 private int iter = 0;\r
343                                 @Override\r
344                                 public boolean hasNext() {\r
345                                         return iter < Clients.this.client.length;\r
346                                 }\r
347 \r
348                                 @Override\r
349                                 public Client next() {\r
350                                         return Clients.this.client[iter++];\r
351                                 }\r
352                                 \r
353                         };\r
354                 }\r
355         }\r
356 }\r