1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\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
11 * * http://www.apache.org/licenses/LICENSE-2.0
\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
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
23 package org.onap.aaf.cadi.taf.cert;
\r
25 import java.io.ByteArrayInputStream;
\r
26 import java.io.ByteArrayOutputStream;
\r
27 import java.io.IOException;
\r
28 import java.security.GeneralSecurityException;
\r
29 import java.security.MessageDigest;
\r
30 import java.security.NoSuchAlgorithmException;
\r
31 import java.security.Principal;
\r
32 import java.security.Signature;
\r
33 import java.security.cert.CertificateException;
\r
34 import java.security.cert.CertificateFactory;
\r
35 import java.security.cert.X509Certificate;
\r
36 import java.util.ArrayList;
\r
38 import javax.net.ssl.TrustManagerFactory;
\r
39 import javax.servlet.http.HttpServletRequest;
\r
40 import javax.servlet.http.HttpServletResponse;
\r
42 import org.onap.aaf.cadi.Access;
\r
43 import org.onap.aaf.cadi.CachedPrincipal;
\r
44 import org.onap.aaf.cadi.CadiException;
\r
45 import org.onap.aaf.cadi.Lur;
\r
46 import org.onap.aaf.cadi.Symm;
\r
47 import org.onap.aaf.cadi.Access.Level;
\r
48 import org.onap.aaf.cadi.CachedPrincipal.Resp;
\r
49 import org.onap.aaf.cadi.Taf.LifeForm;
\r
50 import org.onap.aaf.cadi.config.Config;
\r
51 import org.onap.aaf.cadi.config.SecurityInfo;
\r
52 import org.onap.aaf.cadi.config.SecurityInfoC;
\r
53 import org.onap.aaf.cadi.lur.LocalPermission;
\r
54 import org.onap.aaf.cadi.principal.TGuardPrincipal;
\r
55 import org.onap.aaf.cadi.principal.X509Principal;
\r
56 import org.onap.aaf.cadi.taf.HttpTaf;
\r
57 import org.onap.aaf.cadi.taf.TafResp;
\r
58 import org.onap.aaf.cadi.taf.TafResp.RESP;
\r
59 import org.onap.aaf.cadi.util.Split;
\r
61 public class X509Taf implements HttpTaf {
\r
63 public static final CertificateFactory certFactory;
\r
64 public static final MessageDigest messageDigest;
\r
65 public static final TrustManagerFactory tmf;
\r
66 private Access access;
\r
67 private CertIdentity[] certIdents;
\r
69 private ArrayList<String> cadiIssuers;
\r
71 private SecurityInfo si;
\r
75 certFactory = CertificateFactory.getInstance("X.509");
\r
76 messageDigest = MessageDigest.getInstance("SHA-256"); // use this to clone
\r
77 tmf = TrustManagerFactory.getInstance(SecurityInfoC.SslKeyManagerFactoryAlgorithm);
\r
78 } catch (Exception e) {
\r
79 throw new RuntimeException("X.509 and SHA-256 are required for X509Taf",e);
\r
83 public X509Taf(Access access, Lur lur, CertIdentity ... cis) throws CertificateException, NoSuchAlgorithmException, CadiException {
\r
84 this.access = access;
\r
85 env = access.getProperty(Config.AAF_ENV,null);
\r
87 throw new CadiException("X509Taf requires Environment ("+Config.AAF_ENV+") to be set.");
\r
90 this.cadiIssuers = new ArrayList<String>();
\r
91 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
92 cadiIssuers.add(ci);
\r
95 Class<?> dci = access.classLoader().loadClass("com.att.authz.cadi.DirectCertIdentity");
\r
96 CertIdentity temp[] = new CertIdentity[cis.length+1];
\r
97 System.arraycopy(cis, 0, temp, 1, cis.length);
\r
98 temp[0] = (CertIdentity) dci.newInstance();
\r
100 } catch (Exception e) {
\r
105 si = new SecurityInfo(access);
\r
106 } catch (GeneralSecurityException | IOException e1) {
\r
107 throw new CadiException(e1);
\r
111 public static final X509Certificate getCert(byte[] certBytes) throws CertificateException {
\r
112 ByteArrayInputStream bais = new ByteArrayInputStream(certBytes);
\r
113 return (X509Certificate)certFactory.generateCertificate(bais);
\r
116 public static final byte[] getFingerPrint(byte[] ba) {
\r
119 md = (MessageDigest)messageDigest.clone();
\r
120 } catch (CloneNotSupportedException e) {
\r
121 // should never get here
\r
122 return new byte[0];
\r
125 return md.digest();
\r
128 public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
\r
129 // Check for Mutual SSL
\r
131 X509Certificate[] certarr = (X509Certificate[])req.getAttribute("javax.servlet.request.X509Certificate");
\r
132 if(certarr!=null && certarr.length>0) {
\r
133 si.checkClientTrusted(certarr);
\r
134 // Note: If the Issuer is not in the TrustStore, it's not added to the Cert list
\r
135 if(cadiIssuers.contains(certarr[0].getIssuerDN().toString())) {
\r
136 String x500 = certarr[0].getSubjectDN().getName();
\r
137 int ou=x500.indexOf("OU=");
\r
140 int comma = x500.indexOf(',',ou);
\r
142 String id= x500.substring(ou,comma);
\r
143 String idenv[] = id.split(":");
\r
144 if(idenv.length==1 || (idenv.length>1 && env.equals(idenv[1]))) {
\r
145 return new X509HttpTafResp(access,
\r
146 new X509Principal(idenv[0], certarr[0],null),
\r
147 id + " validated by CADI x509", RESP.IS_AUTHENTICATED);
\r
154 byte[] array = null;
\r
155 byte[] certBytes = null;
\r
156 X509Certificate cert=null;
\r
157 String responseText=null;
\r
158 String authHeader = req.getHeader("Authorization");
\r
160 if(certarr!=null) { // If cert !=null, Cert is Tested by Mutual Protocol.
\r
161 if(authHeader!=null) { // This is only intended to be a Secure Connection, not an Identity
\r
162 return new X509HttpTafResp(access, null, "Certificate verified, but another Identity is presented", RESP.TRY_ANOTHER_TAF);
\r
165 responseText = ", validated by Mutual SSL Protocol";
\r
166 } else { // If cert == null, Get Declared Cert (in header), but validate by having them sign something
\r
167 if(authHeader != null && authHeader.startsWith("x509 ")) {
\r
168 ByteArrayOutputStream baos = new ByteArrayOutputStream(authHeader.length());
\r
170 array = authHeader.getBytes();
\r
171 ByteArrayInputStream bais = new ByteArrayInputStream(array);
\r
172 Symm.base64noSplit.decode(bais, baos, 5);
\r
173 certBytes = baos.toByteArray();
\r
174 cert = getCert(certBytes);
\r
177 * Identity from CERT if well know CA and specific encoded information
\r
179 // If found Identity doesn't work, try SignedStuff Protocol
\r
180 // cert.checkValidity();
\r
181 // cert.--- GET FINGERPRINT?
\r
182 String stuff = req.getHeader("Signature");
\r
184 return new X509HttpTafResp(access, null, "Header entry 'Signature' required to validate One way X509 Certificate", RESP.TRY_ANOTHER_TAF);
\r
185 String data = req.getHeader("Data");
\r
187 // return new X509HttpTafResp(access, null, "No signed Data to validate with X509 Certificate", RESP.TRY_ANOTHER_TAF);
\r
189 // Note: Data Pos shows is "<signatureType> <data>"
\r
190 // int dataPos = (stuff.indexOf(' ')); // determine what is Algorithm
\r
192 bais = new ByteArrayInputStream(stuff.getBytes());
\r
193 baos = new ByteArrayOutputStream(stuff.length());
\r
194 Symm.base64noSplit.decode(bais, baos);
\r
195 array = baos.toByteArray();
\r
196 // Signature sig = Signature.getInstance(stuff.substring(0, dataPos)); // get Algorithm from first part of Signature
\r
198 Signature sig = Signature.getInstance(cert.getSigAlgName());
\r
199 sig.initVerify(cert.getPublicKey());
\r
200 sig.update(data.getBytes());
\r
201 if(!sig.verify(array)) {
\r
202 access.log(Level.ERROR, "Signature doesn't Match");
\r
203 return new X509HttpTafResp(access, null, "Certificate NOT verified", RESP.TRY_ANOTHER_TAF);
\r
205 responseText = ", validated by Signed Data";
\r
206 } catch (Exception e) {
\r
207 access.log(e, "Exception while validating Cert");
\r
208 return new X509HttpTafResp(access, null, "Certificate NOT verified", RESP.TRY_ANOTHER_TAF);
\r
212 return new X509HttpTafResp(access, null, "No Certificate Info on Transaction", RESP.TRY_ANOTHER_TAF);
\r
216 // A cert has been found, match Identify
\r
217 Principal prin=null;
\r
219 for(int i=0;prin==null && i<certIdents.length;++i) {
\r
220 if((prin=certIdents[i].identity(req, cert, certBytes))!=null) {
\r
221 responseText = prin.getName() + " matches Certificate " + cert.getSubjectX500Principal().getName() + responseText;
\r
222 // xresp = new X509HttpTafResp(
\r
225 // prin.getName() + " matches Certificate " + cert.getSubjectX500Principal().getName() + responseText,
\r
226 // RESP.IS_AUTHENTICATED);
\r
231 // if Principal is found, check for "AS_USER" and whether this entity is trusted to declare
\r
233 String as_user=req.getHeader(Config.CADI_USER_CHAIN);
\r
234 if(as_user!=null) {
\r
235 if(as_user.startsWith("TGUARD ") && lur.fish(prin, new LocalPermission("com.att.aaf.trust|"+prin.getName()+"|tguard"))) {
\r
236 prin = new TGuardPrincipal(as_user.substring(7));
\r
237 responseText=prin.getName() + " set via trust of " + responseText;
\r
240 return new X509HttpTafResp(
\r
244 RESP.IS_AUTHENTICATED);
\r
246 } catch(Exception e) {
\r
247 return new X509HttpTafResp(access, null, e.getMessage(), RESP.TRY_ANOTHER_TAF);
\r
250 return new X509HttpTafResp(access, null, "Certificate NOT verified", RESP.TRY_ANOTHER_TAF);
\r
253 public Resp revalidate(CachedPrincipal prin) {
\r