[AAF-21] Initial code import
[aaf/cadi.git] / core / src / main / java / com / att / cadi / taf / cert / X509Taf.java
1 /*******************************************************************************\r
2  * ============LICENSE_START====================================================\r
3  * * org.onap.aai\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * Copyright © 2017 Amdocs\r
7  * * ===========================================================================\r
8  * * Licensed under the Apache License, Version 2.0 (the "License");\r
9  * * you may not use this file except in compliance with the License.\r
10  * * You may obtain a copy of the License at\r
11  * * \r
12  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
13  * * \r
14  *  * Unless required by applicable law or agreed to in writing, software\r
15  * * distributed under the License is distributed on an "AS IS" BASIS,\r
16  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
17  * * See the License for the specific language governing permissions and\r
18  * * limitations under the License.\r
19  * * ============LICENSE_END====================================================\r
20  * *\r
21  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
22  * *\r
23  ******************************************************************************/\r
24 package com.att.cadi.taf.cert;\r
25 \r
26 import java.io.ByteArrayInputStream;\r
27 import java.io.ByteArrayOutputStream;\r
28 import java.io.IOException;\r
29 import java.security.GeneralSecurityException;\r
30 import java.security.MessageDigest;\r
31 import java.security.NoSuchAlgorithmException;\r
32 import java.security.Principal;\r
33 import java.security.Signature;\r
34 import java.security.cert.CertificateException;\r
35 import java.security.cert.CertificateFactory;\r
36 import java.security.cert.X509Certificate;\r
37 import java.util.ArrayList;\r
38 \r
39 import javax.net.ssl.TrustManagerFactory;\r
40 import javax.servlet.http.HttpServletRequest;\r
41 import javax.servlet.http.HttpServletResponse;\r
42 \r
43 import com.att.cadi.Access;\r
44 import com.att.cadi.Access.Level;\r
45 import com.att.cadi.CachedPrincipal;\r
46 import com.att.cadi.CachedPrincipal.Resp;\r
47 import com.att.cadi.CadiException;\r
48 import com.att.cadi.Lur;\r
49 import com.att.cadi.Symm;\r
50 import com.att.cadi.Taf.LifeForm;\r
51 import com.att.cadi.config.Config;\r
52 import com.att.cadi.config.SecurityInfoC;\r
53 import com.att.cadi.config.SecurityInfo;\r
54 import com.att.cadi.lur.LocalPermission;\r
55 import com.att.cadi.principal.TGuardPrincipal;\r
56 import com.att.cadi.principal.X509Principal;\r
57 import com.att.cadi.taf.HttpTaf;\r
58 import com.att.cadi.taf.TafResp;\r
59 import com.att.cadi.taf.TafResp.RESP;\r
60 import com.att.cadi.util.Split;\r
61 \r
62 public class X509Taf implements HttpTaf {\r
63         \r
64         public static final CertificateFactory certFactory;\r
65         public static final MessageDigest messageDigest;\r
66         public static final TrustManagerFactory tmf;\r
67         private Access access;\r
68         private CertIdentity[] certIdents;\r
69         private Lur lur;\r
70         private ArrayList<String> cadiIssuers;\r
71         private String env;\r
72         private SecurityInfo si;\r
73 \r
74         static {\r
75                 try {\r
76                         certFactory = CertificateFactory.getInstance("X.509");\r
77                         messageDigest = MessageDigest.getInstance("SHA-256"); // use this to clone\r
78                         tmf = TrustManagerFactory.getInstance(SecurityInfoC.SslKeyManagerFactoryAlgorithm);\r
79                 } catch (Exception e) {\r
80                         throw new RuntimeException("X.509 and SHA-256 are required for X509Taf",e);\r
81                 }\r
82         }\r
83         \r
84         public X509Taf(Access access, Lur lur, CertIdentity ... cis) throws CertificateException, NoSuchAlgorithmException, CadiException {\r
85                 this.access = access;\r
86                 env = access.getProperty(Config.AAF_ENV,null);\r
87                 if(env==null) {\r
88                         throw new CadiException("X509Taf requires Environment ("+Config.AAF_ENV+") to be set.");\r
89                 }\r
90                 this.lur = lur;\r
91                 this.cadiIssuers = new ArrayList<String>();\r
92                 for(String ci : access.getProperty(Config.CADI_X509_ISSUERS, "CN=ATT CADI Issuing CA 01, OU=CSO, O=ATT, C=US:CN=ATT CADI Issuing CA 02, OU=CSO, O=ATT, C=US").split(":")) {\r
93                         cadiIssuers.add(ci);\r
94                 }\r
95                 try {\r
96                         Class<?> dci = access.classLoader().loadClass("com.att.authz.cadi.DirectCertIdentity");\r
97                         CertIdentity temp[] = new CertIdentity[cis.length+1];\r
98                         System.arraycopy(cis, 0, temp, 1, cis.length);\r
99                         temp[0] = (CertIdentity) dci.newInstance();\r
100                         certIdents=temp;\r
101                 } catch (Exception e) {\r
102                         certIdents = cis;\r
103                 }\r
104                 \r
105                 try {\r
106                         si = new SecurityInfo(access);\r
107                 } catch (GeneralSecurityException | IOException e1) {\r
108                         throw new CadiException(e1);\r
109                 }\r
110         }\r
111 \r
112         public static final X509Certificate getCert(byte[] certBytes) throws CertificateException {\r
113                 ByteArrayInputStream bais = new ByteArrayInputStream(certBytes);\r
114                 return (X509Certificate)certFactory.generateCertificate(bais);\r
115         }\r
116 \r
117         public static final byte[] getFingerPrint(byte[] ba) {\r
118                 MessageDigest md;\r
119                 try {\r
120                         md = (MessageDigest)messageDigest.clone();\r
121                 } catch (CloneNotSupportedException e) {\r
122                         // should never get here\r
123                         return new byte[0];\r
124                 }\r
125                 md.update(ba);\r
126                 return md.digest();\r
127         }\r
128 \r
129         public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {\r
130                 // Check for Mutual SSL\r
131                 try {\r
132                         X509Certificate[] certarr = (X509Certificate[])req.getAttribute("javax.servlet.request.X509Certificate");\r
133                         if(certarr!=null && certarr.length>0) {\r
134                                 si.checkClientTrusted(certarr);\r
135                                 // Note: If the Issuer is not in the TrustStore, it's not added to the Cert list\r
136                                 if(cadiIssuers.contains(certarr[0].getIssuerDN().toString())) {\r
137                                         String x500 = certarr[0].getSubjectDN().getName();\r
138                                         int ou=x500.indexOf("OU=");\r
139                                         if(ou>0) {\r
140                                                 ou+=3;\r
141                                                 int comma = x500.indexOf(',',ou);\r
142                                                 if(comma>0) {\r
143                                                         String id= x500.substring(ou,comma);\r
144                                                         String idenv[] = id.split(":");\r
145                                                         if(idenv.length==1 || (idenv.length>1 && env.equals(idenv[1]))) {\r
146                                                                 return new X509HttpTafResp(access, \r
147                                                                         new X509Principal(idenv[0], certarr[0],null), \r
148                                                                                 id + " validated by CADI x509", RESP.IS_AUTHENTICATED);\r
149                                                         }\r
150                                                 }\r
151                                         }\r
152                                 }\r
153                         }\r
154 \r
155                         byte[] array = null;\r
156                         byte[] certBytes = null;\r
157                         X509Certificate cert=null;\r
158                         String responseText=null;\r
159                         String authHeader = req.getHeader("Authorization");\r
160 \r
161                         if(certarr!=null) {  // If cert !=null, Cert is Tested by Mutual Protocol.\r
162                                 if(authHeader!=null) { // This is only intended to be a Secure Connection, not an Identity\r
163                                         return new X509HttpTafResp(access, null, "Certificate verified, but another Identity is presented", RESP.TRY_ANOTHER_TAF);\r
164                                 }\r
165                                 cert = certarr[0];\r
166                                 responseText = ", validated by Mutual SSL Protocol";\r
167                         } else {                 // If cert == null, Get Declared Cert (in header), but validate by having them sign something\r
168                                 if(authHeader != null && authHeader.startsWith("x509 ")) {\r
169                                         ByteArrayOutputStream baos = new ByteArrayOutputStream(authHeader.length());\r
170                                         try {\r
171                                                 array = authHeader.getBytes();\r
172                                                 ByteArrayInputStream bais = new ByteArrayInputStream(array);\r
173                                                 Symm.base64noSplit.decode(bais, baos, 5);\r
174                                                 certBytes = baos.toByteArray();\r
175                                                 cert = getCert(certBytes);\r
176                                                 \r
177                                                 /** \r
178                                                  * Identity from CERT if well know CA and specific encoded information\r
179                                                  */\r
180                                                 // If found Identity doesn't work, try SignedStuff Protocol\r
181 //                                                                      cert.checkValidity();\r
182 //                                                                      cert.--- GET FINGERPRINT?\r
183                                                 String stuff = req.getHeader("Signature");\r
184                                                 if(stuff==null) \r
185                                                         return new X509HttpTafResp(access, null, "Header entry 'Signature' required to validate One way X509 Certificate", RESP.TRY_ANOTHER_TAF);\r
186                                                 String data = req.getHeader("Data"); \r
187 //                                                                      if(data==null) \r
188 //                                                                              return new X509HttpTafResp(access, null, "No signed Data to validate with X509 Certificate", RESP.TRY_ANOTHER_TAF);\r
189 \r
190                                                 // Note: Data Pos shows is "<signatureType> <data>"\r
191 //                                                                      int dataPos = (stuff.indexOf(' ')); // determine what is Algorithm\r
192                                                 // Get Signature \r
193                                                 bais = new ByteArrayInputStream(stuff.getBytes());\r
194                                                 baos = new ByteArrayOutputStream(stuff.length());\r
195                                                 Symm.base64noSplit.decode(bais, baos);\r
196                                                 array = baos.toByteArray();\r
197 //                                                                      Signature sig = Signature.getInstance(stuff.substring(0, dataPos)); // get Algorithm from first part of Signature\r
198                                                 \r
199                                                 Signature sig = Signature.getInstance(cert.getSigAlgName()); \r
200                                                 sig.initVerify(cert.getPublicKey());\r
201                                                 sig.update(data.getBytes());\r
202                                                 if(!sig.verify(array)) {\r
203                                                         access.log(Level.ERROR, "Signature doesn't Match");\r
204                                                         return new X509HttpTafResp(access, null, "Certificate NOT verified", RESP.TRY_ANOTHER_TAF);\r
205                                                 }\r
206                                                 responseText = ", validated by Signed Data";\r
207                                         } catch (Exception e) {\r
208                                                 access.log(e, "Exception while validating Cert");\r
209                                                 return new X509HttpTafResp(access, null, "Certificate NOT verified", RESP.TRY_ANOTHER_TAF);\r
210                                         }\r
211                                         \r
212                                 } else {\r
213                                         return new X509HttpTafResp(access, null, "No Certificate Info on Transaction", RESP.TRY_ANOTHER_TAF);\r
214                                 }\r
215                         }\r
216 \r
217                         // A cert has been found, match Identify\r
218                         Principal prin=null;\r
219                         \r
220                         for(int i=0;prin==null && i<certIdents.length;++i) {\r
221                                 if((prin=certIdents[i].identity(req, cert, certBytes))!=null) {\r
222                                         responseText = prin.getName() + " matches Certificate " + cert.getSubjectX500Principal().getName() + responseText;\r
223 //                                      xresp = new X509HttpTafResp(\r
224 //                                                              access,\r
225 //                                                              prin,\r
226 //                                                              prin.getName() + " matches Certificate " + cert.getSubjectX500Principal().getName() + responseText,\r
227 //                                                              RESP.IS_AUTHENTICATED);\r
228                                         \r
229                                 }\r
230                         }\r
231 \r
232                         // if Principal is found, check for "AS_USER" and whether this entity is trusted to declare\r
233                         if(prin!=null) {\r
234                                 String as_user=req.getHeader(Config.CADI_USER_CHAIN);\r
235                                 if(as_user!=null) {\r
236                                         if(as_user.startsWith("TGUARD ") && lur.fish(prin, new LocalPermission("com.att.aaf.trust|"+prin.getName()+"|tguard"))) {\r
237                                                 prin = new TGuardPrincipal(as_user.substring(7));\r
238                                                 responseText=prin.getName() + " set via trust of " + responseText;\r
239                                         }\r
240                                 }\r
241                                 return new X509HttpTafResp(\r
242                                         access,\r
243                                         prin,\r
244                                         responseText,\r
245                                         RESP.IS_AUTHENTICATED);\r
246                         }\r
247                 } catch(Exception e) {\r
248                         return new X509HttpTafResp(access, null, e.getMessage(), RESP.TRY_ANOTHER_TAF); \r
249                 }\r
250         \r
251                 return new X509HttpTafResp(access, null, "Certificate NOT verified", RESP.TRY_ANOTHER_TAF);\r
252         }\r
253 \r
254         public Resp revalidate(CachedPrincipal prin) {\r
255                 return null;\r
256         }\r
257 \r
258 }\r