Collection syntax change because of Sonar
[aaf/authz.git] / cadi / core / src / main / java / org / onap / aaf / cadi / taf / cert / X509Taf.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
22 package org.onap.aaf.cadi.taf.cert;
23
24 import java.io.ByteArrayInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.security.MessageDigest;
27 import java.security.NoSuchAlgorithmException;
28 import java.security.Signature;
29 import java.security.cert.CertificateException;
30 import java.security.cert.CertificateFactory;
31 import java.security.cert.X509Certificate;
32 import java.util.ArrayList;
33
34 import javax.net.ssl.TrustManagerFactory;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import org.onap.aaf.cadi.Access;
39 import org.onap.aaf.cadi.Access.Level;
40 import org.onap.aaf.cadi.CachedPrincipal;
41 import org.onap.aaf.cadi.CachedPrincipal.Resp;
42 import org.onap.aaf.cadi.CadiException;
43 import org.onap.aaf.cadi.CredVal;
44 import org.onap.aaf.cadi.Lur;
45 import org.onap.aaf.cadi.Symm;
46 import org.onap.aaf.cadi.Taf.LifeForm;
47 import org.onap.aaf.cadi.config.Config;
48 import org.onap.aaf.cadi.config.SecurityInfo;
49 import org.onap.aaf.cadi.config.SecurityInfoC;
50 import org.onap.aaf.cadi.principal.TaggedPrincipal;
51 import org.onap.aaf.cadi.principal.X509Principal;
52 import org.onap.aaf.cadi.taf.HttpTaf;
53 import org.onap.aaf.cadi.taf.TafResp;
54 import org.onap.aaf.cadi.taf.TafResp.RESP;
55 import org.onap.aaf.cadi.taf.basic.BasicHttpTaf;
56 import org.onap.aaf.cadi.util.Split;
57
58 public class X509Taf implements HttpTaf {
59         
60         private static final String CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION = "Certificate NOT valid for Authentication";
61         public static final CertificateFactory certFactory;
62         public static final MessageDigest messageDigest;
63         public static final TrustManagerFactory tmf;
64         private Access access;
65         private CertIdentity[] certIdents;
66 //      private Lur lur;
67         private ArrayList<String> cadiIssuers;
68         private String env;
69         private SecurityInfo si;
70         private BasicHttpTaf bht;
71
72         static {
73                 try {
74                         certFactory = CertificateFactory.getInstance("X.509");
75                         messageDigest = MessageDigest.getInstance("SHA-256"); // use this to clone
76                         tmf = TrustManagerFactory.getInstance(SecurityInfoC.SSL_KEY_MANAGER_FACTORY_ALGORITHM);
77                 } catch (Exception e) {
78                         throw new RuntimeException("X.509 and SHA-256 are required for X509Taf",e);
79                 }
80         }
81         
82         public X509Taf(Access access, Lur lur, CertIdentity ... cis) throws CertificateException, NoSuchAlgorithmException, CadiException {
83                 this.access = access;
84                 env = access.getProperty(Config.AAF_ENV,null);
85                 if(env==null) {
86                         throw new CadiException("X509Taf requires Environment ("+Config.AAF_ENV+") to be set.");
87                 }
88 //              this.lur = lur;
89                 this.cadiIssuers = new ArrayList<>();
90                 for(String ci : access.getProperty(Config.CADI_X509_ISSUERS, "").split(":")) {
91                         access.printf(Level.INIT, "Trusting Identity for Certificates signed by \"%s\"",ci);
92                         cadiIssuers.add(ci);
93                 }
94                 try {
95                         Class<?> dci = access.classLoader().loadClass("org.onap.aaf.auth.direct.DirectCertIdentity");
96                         if(dci==null) {
97                                 certIdents = cis;
98                         } else {
99                                 CertIdentity temp[] = new CertIdentity[cis.length+1];
100                                 System.arraycopy(cis, 0, temp, 1, cis.length);
101                                 temp[0] = (CertIdentity) dci.newInstance();
102                                 certIdents=temp;
103                         }
104                 } catch (Exception e) {
105                         certIdents = cis;
106                 }
107                 
108                 si = new SecurityInfo(access);
109         }
110
111         public static final X509Certificate getCert(byte[] certBytes) throws CertificateException {
112                 ByteArrayInputStream bais = new ByteArrayInputStream(certBytes);
113                 return (X509Certificate)certFactory.generateCertificate(bais);
114         }
115
116         public static final byte[] getFingerPrint(byte[] ba) {
117                 MessageDigest md;
118                 try {
119                         md = (MessageDigest)messageDigest.clone();
120                 } catch (CloneNotSupportedException e) {
121                         // should never get here
122                         return new byte[0];
123                 }
124                 md.update(ba);
125                 return md.digest();
126         }
127
128         @Override
129         public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
130                 // Check for Mutual SSL
131                 try {
132                         X509Certificate[] certarr = (X509Certificate[])req.getAttribute("javax.servlet.request.X509Certificate");
133                         if(certarr!=null && certarr.length>0) {
134                                 si.checkClientTrusted(certarr);
135                                 // Note: If the Issuer is not in the TrustStore, it's not added to the Cert list
136                                 String issuer = certarr[0].getIssuerDN().toString();
137                                 if(cadiIssuers.contains(issuer)) {
138                                         String subject = certarr[0].getSubjectDN().getName();
139                                         // avoiding extra object creation, since this is validated EVERY transaction with a Cert
140                                         int at = subject.indexOf('@');
141                                         if(at>=0) {
142                                                 int start = subject.lastIndexOf(',', at);
143                                                 if(start<0) {
144                                                         start = 0;
145                                                 }
146                                                 int end = subject.indexOf(',', at);
147                                                 if(end<0) {
148                                                         end=subject.length();
149                                                 }
150                                                 int temp;
151                                                 if(((temp=subject.indexOf("OU=",start))>=0 && temp<end) || 
152                                                    ((temp=subject.indexOf("CN=",start))>=0 && temp<end)) {
153                                                         String[] sa = Split.splitTrim(':', subject, temp+3,end);
154                                                         if(sa.length==1 || (sa.length>1 && env!=null && env.equals(sa[1]))) { // Check Environment 
155                                                                 return new X509HttpTafResp(access, 
156                                                                                 new X509Principal(sa[0], certarr[0],(byte[])null,bht), 
157                                                                                 "X509Taf validated " + sa[0] + (sa.length<2?"":" for aaf_env " + env ), RESP.IS_AUTHENTICATED);
158                                                         }
159                                                 }
160                                                 
161                                         }
162                                 }
163                         }
164                 
165
166                         byte[] array = null;
167                         byte[] certBytes = null;
168                         X509Certificate cert=null;
169                         String responseText=null;
170                         String authHeader = req.getHeader("Authorization");
171
172                         if(certarr!=null) {  // If cert !=null, Cert is Tested by Mutual Protocol.
173                                 if(authHeader!=null) { // This is only intended to be a Secure Connection, not an Identity
174                                         for(String auth : Split.split(',',authHeader)) {
175                                                 if(auth.startsWith("Bearer ")) { // Bearer = OAuth... Don't use as Authenication
176                                                         return new X509HttpTafResp(access, null, "Certificate verified, but Bearer Token is presented", RESP.TRY_ANOTHER_TAF);
177                                                 }
178                                         }
179                                 }
180                                 cert = certarr[0];
181                                 responseText = ", validated by Mutual SSL Protocol";
182                         } else {                 // If cert == null, Get Declared Cert (in header), but validate by having them sign something
183                                 if(authHeader != null) {
184                                         for(String auth : Split.splitTrim(',',authHeader)) {
185                                                 if(auth.startsWith("x509 ")) {
186                                                         ByteArrayOutputStream baos = new ByteArrayOutputStream(auth.length());
187                                                         try {
188                                                                 array = auth.getBytes();
189                                                                 ByteArrayInputStream bais = new ByteArrayInputStream(array);
190                                                                 Symm.base64noSplit.decode(bais, baos, 5);
191                                                                 certBytes = baos.toByteArray();
192                                                                 cert = getCert(certBytes);
193                                                                 
194                                                                 /** 
195                                                                  * Identity from CERT if well know CA and specific encoded information
196                                                                  */
197                                                                 // If found Identity doesn't work, try SignedStuff Protocol
198                 //                                                                      cert.checkValidity();
199                 //                                                                      cert.--- GET FINGERPRINT?
200                                                                 String stuff = req.getHeader("Signature");
201                                                                 if(stuff==null) 
202                                                                         return new X509HttpTafResp(access, null, "Header entry 'Signature' required to validate One way X509 Certificate", RESP.TRY_ANOTHER_TAF);
203                                                                 String data = req.getHeader("Data"); 
204                 //                                                                      if(data==null) 
205                 //                                                                              return new X509HttpTafResp(access, null, "No signed Data to validate with X509 Certificate", RESP.TRY_ANOTHER_TAF);
206                 
207                                                                 // Note: Data Pos shows is "<signatureType> <data>"
208                 //                                                                      int dataPos = (stuff.indexOf(' ')); // determine what is Algorithm
209                                                                 // Get Signature 
210                                                                 bais = new ByteArrayInputStream(stuff.getBytes());
211                                                                 baos = new ByteArrayOutputStream(stuff.length());
212                                                                 Symm.base64noSplit.decode(bais, baos);
213                                                                 array = baos.toByteArray();
214                 //                                                                      Signature sig = Signature.getInstance(stuff.substring(0, dataPos)); // get Algorithm from first part of Signature
215                                                                 
216                                                                 Signature sig = Signature.getInstance(cert.getSigAlgName()); 
217                                                                 sig.initVerify(cert.getPublicKey());
218                                                                 sig.update(data.getBytes());
219                                                                 if(!sig.verify(array)) {
220                                                                         access.log(Level.ERROR, "Signature doesn't Match");
221                                                                         return new X509HttpTafResp(access, null, CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION, RESP.TRY_ANOTHER_TAF);
222                                                                 }
223                                                                 responseText = ", validated by Signed Data";
224                                                         } catch (Exception e) {
225                                                                 access.log(e, "Exception while validating Cert");
226                                                                 return new X509HttpTafResp(access, null, CERTIFICATE_NOT_VALID_FOR_AUTHENTICATION, RESP.TRY_ANOTHER_TAF);
227                                                         }
228                                                 }
229                                         }
230                                 }
231                                 if(cert==null) {
232                                         return new X509HttpTafResp(access, null, "No Certificate Info on Transaction", RESP.TRY_ANOTHER_TAF);
233                                 }
234                                 
235                                 // A cert has been found, match Identify
236                                 TaggedPrincipal prin=null;
237                                 
238                                 for(int i=0;prin==null && i<certIdents.length;++i) {
239                                         if((prin=certIdents[i].identity(req, cert, certBytes))!=null) {
240                                                 responseText = prin.getName() + " matches Certificate " + cert.getSubjectX500Principal().getName() + responseText;
241                                         }
242                                 }
243         
244                                 // if Principal is found, check for "AS_USER" and whether this entity is trusted to declare
245                                 if(prin!=null) {
246                                         return new X509HttpTafResp(
247                                                 access,
248                                                 prin,
249                                                 responseText,
250                                                 RESP.IS_AUTHENTICATED);
251                                 }
252                         }
253                 } catch(Exception e) {
254                         return new X509HttpTafResp(access, null, e.getMessage(), RESP.TRY_ANOTHER_TAF); 
255                 }
256         
257                 return new X509HttpTafResp(access, null, "Certificate cannot be used for authentication", RESP.TRY_ANOTHER_TAF);
258         }
259
260         @Override
261         public Resp revalidate(CachedPrincipal prin, Object state) {
262                 return null;
263         }
264
265         public void add(BasicHttpTaf bht) {
266                 this.bht = bht;
267         }
268         
269         public CredVal getCredVal(final String key) {
270                 if(bht==null) {
271                         return null;
272                 } else {
273                         return bht.getCredVal(key);
274                 }
275         }
276         
277 }