Fix Agent and CM Issues
[aaf/authz.git] / auth / auth-certman / src / main / java / org / onap / aaf / auth / cm / ca / CA.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 package org.onap.aaf.auth.cm.ca;
22
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.security.MessageDigest;
27 import java.security.Principal;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Set;
33 import java.util.regex.Pattern;
34
35 import org.bouncycastle.asn1.x500.style.BCStyle;
36 import org.onap.aaf.auth.cm.cert.CSRMeta;
37 import org.onap.aaf.auth.cm.cert.RDN;
38 import org.onap.aaf.cadi.Access;
39 import org.onap.aaf.cadi.Access.Level;
40 import org.onap.aaf.cadi.config.Config;
41 import org.onap.aaf.cadi.configure.CertException;
42 import org.onap.aaf.misc.env.Trans;
43 import org.onap.aaf.misc.env.util.Split;
44
45 public abstract class CA {
46     public static final Pattern IPV4_PATTERN = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z");
47     public static final Pattern IPV6_PATTERN = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z");
48     
49
50     private static final String MUST_EXIST_TO_CREATE_CSRS_FOR = " must exist to create CSRs for ";
51     //TODO figuring out what is an Issuing CA is a matter of convention.  Consider SubClassing for Open Source
52     public static final String ISSUING_CA = "Issuing CA";
53     public static final String CM_CA_PREFIX = "cm_ca.";
54     public static final String CM_CA_BASE_SUBJECT = ".baseSubject";
55     public static final String CM_CA_ENV_TAG = ".env_tag";
56     protected static final String CM_PUBLIC_DIR = "cm_public_dir";
57     private static final String CM_TRUST_CAS = "cm_trust_cas";
58     protected static final String CM_BACKUP_CAS = "cm_backup_cas";
59
60     public static final Set<String> EMPTY = Collections.unmodifiableSet(new HashSet<>());
61
62
63     private final String name;
64     private final String env;
65     private MessageDigest messageDigest;
66     private final String permNS;
67     private final String permType;
68     private final ArrayList<String> idDomains;
69     private String[] trustedCAs;
70     private String[] caIssuerDNs;
71     private List<RDN> rdns;
72     private final boolean env_tag;
73
74
75     protected CA(Access access, String caName, String env) throws IOException, CertException {
76         trustedCAs = new String[4]; // starting array
77         this.name = caName;
78         this.env = env;
79         this.env_tag = env==null || env.isEmpty()?false:
80                 Boolean.parseBoolean(access.getProperty(CM_CA_ENV_TAG, Boolean.FALSE.toString()));
81         permNS=null;
82         String prefix = CM_CA_PREFIX + name;
83         permType = access.getProperty(prefix + ".perm_type",null);
84         if (permType==null) {
85             throw new CertException(prefix + ".perm_type" + MUST_EXIST_TO_CREATE_CSRS_FOR + caName);
86         }
87         caIssuerDNs = Split.splitTrim(':', access.getProperty(Config.CADI_X509_ISSUERS, null));
88
89         String tag = CA.CM_CA_PREFIX+caName+CA.CM_CA_BASE_SUBJECT;
90
91         String fields = access.getProperty(tag, null);
92         if (fields==null) {
93             throw new CertException(tag + MUST_EXIST_TO_CREATE_CSRS_FOR + caName);
94         }
95         access.log(Level.INFO, tag, "=",fields);
96         rdns = RDN.parse('/',fields);
97         for (RDN rdn : rdns) {
98             if (rdn.aoi==BCStyle.EmailAddress) { // Cert Specs say Emails belong in Subject
99                 throw new CertException("email address is not allowed in " + CM_CA_BASE_SUBJECT);
100             }
101         }
102
103         idDomains = new ArrayList<>();
104         StringBuilder sb = null;
105         for (String s : Split.splitTrim(',', access.getProperty(CA.CM_CA_PREFIX+caName+".idDomains", ""))) {
106             if (s.length()>0) {
107                 if (sb==null) {
108                     sb = new StringBuilder();
109                 } else {
110                     sb.append(", ");
111                 }
112                 idDomains.add(s);
113                 sb.append(s);
114             }
115         }
116         if (sb!=null) {
117             access.printf(Level.INIT, "CA '%s' supports Personal Certificates for %s", caName, sb);
118         }
119
120         String dataDir = access.getProperty(CM_PUBLIC_DIR,null);
121         if (dataDir!=null) {
122             File data = new File(dataDir);
123             byte[] bytes;
124             if (data.exists()) {
125                 String trustCas = access.getProperty(CM_TRUST_CAS,null);
126                 if (trustCas!=null) {
127                     for (String fname : Split.splitTrim(',', trustCas)) {
128                         File crt;
129                         if (fname.contains("/")) {
130                             crt = new File(fname);
131                         } else {
132                             crt = new File(data,fname);
133                         }
134                         if (crt.exists()) {
135                             access.printf(Level.INIT, "Loading CA Cert from %s", crt.getAbsolutePath());
136                             bytes = new byte[(int)crt.length()];
137                             FileInputStream fis = new FileInputStream(crt);
138                             try {
139                                 int read = fis.read(bytes);
140                                 if (read>0) {
141                                     addTrustedCA(new String(bytes));
142                                 }
143                             } finally {
144                                 fis.close();
145                             }
146                         } else {
147                             access.printf(Level.INIT, "FAILED to Load CA Cert from %s", crt.getAbsolutePath());
148                         }
149                     }
150                 } else {
151                     access.printf(Level.INIT, "Cannot load external TRUST CAs: No property %s",CM_TRUST_CAS);
152                 }
153             } else {
154                 access.printf(Level.INIT, "Cannot load external TRUST CAs: %s doesn't exist, or is not accessible",data.getAbsolutePath());
155             }
156         }
157     }
158
159     protected void addCaIssuerDN(String issuerDN) {
160         boolean changed = true;
161         for (String id : caIssuerDNs) {
162             if (id.equals(issuerDN)) {
163                 changed = false;
164                 break;
165             }
166         }
167         if (changed) {
168             String[] newsa = new String[caIssuerDNs.length+1];
169             newsa[0]=issuerDN;
170             System.arraycopy(caIssuerDNs, 0, newsa, 1, caIssuerDNs.length);
171             caIssuerDNs = newsa;
172         }
173     }
174
175     protected synchronized void addTrustedCA(final String crtString) {
176         String crt;
177         if (crtString.endsWith("\n")) {
178             crt = crtString;
179         } else {
180             crt = crtString + '\n';
181         }
182         for (int i=0;i<trustedCAs.length;++i) {
183             if (trustedCAs[i]==null) {
184                 trustedCAs[i]=crt;
185                 return;
186             }
187         }
188         String[] temp = new String[trustedCAs.length+5];
189         System.arraycopy(trustedCAs,0,temp, 0, trustedCAs.length);
190         temp[trustedCAs.length]=crt;
191         trustedCAs = temp;
192     }
193
194     public String[] getCaIssuerDNs() {
195         return caIssuerDNs;
196     }
197
198     public String[] getTrustedCAs() {
199         return trustedCAs;
200     }
201
202     public boolean shouldAddEnvTag() {
203         return env_tag;
204     }
205
206     public String getEnv() {
207         return env;
208     }
209
210     protected void setMessageDigest(MessageDigest md) {
211         messageDigest = md;
212     }
213
214     /*
215      * End Required Constructor calls
216      */
217
218     public String getName() {
219         return name;
220     }
221
222
223     public String getPermNS() {
224         return permNS;
225     }
226
227     public String getPermType() {
228         return permType;
229     }
230
231     public abstract X509andChain sign(Trans trans, CSRMeta csrmeta) throws IOException, CertException;
232
233     /* (non-Javadoc)
234      * @see org.onap.aaf.auth.cm.ca.CA#inPersonalDomains(java.security.Principal)
235      */
236     public boolean inPersonalDomains(Principal p) {
237         int at = p.getName().indexOf('@');
238         if (at>=0) {
239             return idDomains.contains(p.getName().substring(at+1));
240         } else {
241             return false;
242         }
243     }
244
245     public MessageDigest messageDigest() {
246         return messageDigest;
247     }
248
249     public CSRMeta newCSRMeta() {
250         return new CSRMeta(rdns);
251     }
252
253 }