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