1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\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
12 * * http://www.apache.org/licenses/LICENSE-2.0
\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
21 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
23 ******************************************************************************/
\r
24 package com.att.cadi.taf.cert;
\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
39 import javax.net.ssl.TrustManagerFactory;
\r
40 import javax.servlet.http.HttpServletRequest;
\r
41 import javax.servlet.http.HttpServletResponse;
\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
62 public class X509Taf implements HttpTaf {
\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
70 private ArrayList<String> cadiIssuers;
\r
72 private SecurityInfo si;
\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
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
88 throw new CadiException("X509Taf requires Environment ("+Config.AAF_ENV+") to be set.");
\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
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
101 } catch (Exception e) {
\r
106 si = new SecurityInfo(access);
\r
107 } catch (GeneralSecurityException | IOException e1) {
\r
108 throw new CadiException(e1);
\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
117 public static final byte[] getFingerPrint(byte[] ba) {
\r
120 md = (MessageDigest)messageDigest.clone();
\r
121 } catch (CloneNotSupportedException e) {
\r
122 // should never get here
\r
123 return new byte[0];
\r
126 return md.digest();
\r
129 public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
\r
130 // Check for Mutual SSL
\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
141 int comma = x500.indexOf(',',ou);
\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
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
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
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
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
178 * Identity from CERT if well know CA and specific encoded information
\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
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
188 // return new X509HttpTafResp(access, null, "No signed Data to validate with X509 Certificate", RESP.TRY_ANOTHER_TAF);
\r
190 // Note: Data Pos shows is "<signatureType> <data>"
\r
191 // int dataPos = (stuff.indexOf(' ')); // determine what is Algorithm
\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
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
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
213 return new X509HttpTafResp(access, null, "No Certificate Info on Transaction", RESP.TRY_ANOTHER_TAF);
\r
217 // A cert has been found, match Identify
\r
218 Principal prin=null;
\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
226 // prin.getName() + " matches Certificate " + cert.getSubjectX500Principal().getName() + responseText,
\r
227 // RESP.IS_AUTHENTICATED);
\r
232 // if Principal is found, check for "AS_USER" and whether this entity is trusted to declare
\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
241 return new X509HttpTafResp(
\r
245 RESP.IS_AUTHENTICATED);
\r
247 } catch(Exception e) {
\r
248 return new X509HttpTafResp(access, null, e.getMessage(), RESP.TRY_ANOTHER_TAF);
\r
251 return new X509HttpTafResp(access, null, "Certificate NOT verified", RESP.TRY_ANOTHER_TAF);
\r
254 public Resp revalidate(CachedPrincipal prin) {
\r