Local CA to use Keystores
[aaf/authz.git] / cadi / aaf / src / main / java / org / onap / aaf / cadi / cm / Factory.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.cm;
23
24 import java.io.BufferedReader;
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.DataInputStream;
28 import java.io.File;
29 import java.io.FileInputStream;
30 import java.io.FileNotFoundException;
31 import java.io.FileReader;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.InputStreamReader;
35 import java.io.Reader;
36 import java.io.StringReader;
37 import java.lang.reflect.Constructor;
38 import java.lang.reflect.InvocationTargetException;
39 import java.security.InvalidKeyException;
40 import java.security.Key;
41 import java.security.KeyFactory;
42 import java.security.KeyPair;
43 import java.security.KeyPairGenerator;
44 import java.security.NoSuchAlgorithmException;
45 import java.security.PrivateKey;
46 import java.security.Provider;
47 import java.security.PublicKey;
48 import java.security.SecureRandom;
49 import java.security.Security;
50 import java.security.Signature;
51 import java.security.SignatureException;
52 import java.security.cert.Certificate;
53 import java.security.cert.CertificateEncodingException;
54 import java.security.cert.CertificateException;
55 import java.security.cert.CertificateFactory;
56 import java.security.cert.X509Certificate;
57 import java.security.spec.InvalidKeySpecException;
58 import java.security.spec.PKCS8EncodedKeySpec;
59 import java.security.spec.X509EncodedKeySpec;
60 import java.util.Collection;
61 import java.util.List;
62
63 import javax.crypto.Cipher;
64 import javax.crypto.NoSuchPaddingException;
65
66 import org.onap.aaf.cadi.Symm;
67 import org.onap.aaf.misc.env.Env;
68 import org.onap.aaf.misc.env.TimeTaken;
69 import org.onap.aaf.misc.env.Trans;
70
71 public class Factory {
72         private static final String PRIVATE_KEY_HEADER = "PRIVATE KEY";
73         public static final String KEY_ALGO = "RSA";
74         public static final String SIG_ALGO = "SHA256withRSA";
75
76         public  static final int KEY_LENGTH = 2048;
77         private static final KeyPairGenerator keygen;
78         private static final KeyFactory keyFactory;
79         private static final CertificateFactory certificateFactory;
80         private static final SecureRandom random;
81         
82         
83         private static final Symm base64 = Symm.base64.copy(64);
84
85         static {
86                         random = new SecureRandom();
87                         KeyPairGenerator tempKeygen;
88                         try {
89                                 tempKeygen = KeyPairGenerator.getInstance(KEY_ALGO);//,"BC");
90                                 tempKeygen.initialize(KEY_LENGTH, random);
91                         } catch (NoSuchAlgorithmException e) {
92                                 tempKeygen = null;
93                                 e.printStackTrace(System.err);
94                         }
95                         keygen = tempKeygen;
96
97                         KeyFactory tempKeyFactory;
98                         try {
99                                 tempKeyFactory=KeyFactory.getInstance(KEY_ALGO);//,"BC"
100                         } catch (NoSuchAlgorithmException e) {
101                                 tempKeyFactory = null;
102                                 e.printStackTrace(System.err);
103                         };
104                         keyFactory = tempKeyFactory;
105                          
106                         CertificateFactory tempCertificateFactory;
107                         try {
108                                 tempCertificateFactory = CertificateFactory.getInstance("X.509");
109                         } catch (CertificateException e) {
110                                 tempCertificateFactory = null;
111                                 e.printStackTrace(System.err);
112                         }
113                         certificateFactory = tempCertificateFactory;
114
115                  
116         }
117
118
119         public static KeyPair generateKeyPair(Trans trans) {
120                 TimeTaken tt;
121                 if(trans!=null) {
122                         tt = trans.start("Generate KeyPair", Env.SUB);
123                 } else {
124                         tt = null;
125                 }
126                 try {
127                         return keygen.generateKeyPair();
128                 } finally {
129                         if(tt!=null) {
130                                 tt.done();
131                         }
132                 }
133         }  
134
135         private static final String LINE_END = "-----\n";
136
137         protected static String textBuilder(String kind, byte[] bytes) throws IOException {
138                 StringBuilder sb = new StringBuilder();
139                 sb.append("-----BEGIN ");
140                 sb.append(kind);
141                 sb.append(LINE_END);
142
143                 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
144                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
145                 base64.encode(bais, baos);
146                 sb.append(new String(baos.toByteArray()));
147                 
148                 if(sb.charAt(sb.length()-1)!='\n') {
149                         sb.append('\n');
150                 }
151                 sb.append("-----END ");
152                 sb.append(kind);
153                 sb.append(LINE_END);
154                 return sb.toString();
155         }
156         
157         public static PrivateKey toPrivateKey(Trans trans, String pk) throws IOException, CertException {
158                 byte[] bytes = decode(new StringReader(pk));
159                 return toPrivateKey(trans, bytes);
160         }
161         
162         public static PrivateKey toPrivateKey(Trans trans, byte[] bytes) throws IOException, CertException {
163                 TimeTaken tt=trans.start("Reconstitute Private Key", Env.SUB);
164                 try {
165                         return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
166                 } catch (InvalidKeySpecException e) {
167                         throw new CertException("Translating Private Key from PKCS8 KeySpec",e);
168                 } finally {
169                         tt.done();
170                 }
171         }
172         
173         public static PrivateKey toPrivateKey(Trans trans, File file) throws IOException, CertException {
174                 TimeTaken tt = trans.start("Decode Private Key File", Env.SUB);
175                 try {
176                         return toPrivateKey(trans,decode(file));
177                 }finally {
178                         tt.done();
179                 }
180         }
181
182         public static String toString(Trans trans, PrivateKey pk) throws IOException {
183 //              PKCS8EncodedKeySpec pemContents = new PKCS8EncodedKeySpec(pk.getEncoded());
184                 trans.debug().log("Private Key to String");
185                 return textBuilder(PRIVATE_KEY_HEADER,pk.getEncoded());
186         }
187
188         public static PublicKey toPublicKey(Trans trans, String pk) throws IOException {
189                 TimeTaken tt = trans.start("Reconstitute Public Key", Env.SUB);
190                 try {
191                         ByteArrayInputStream bais = new ByteArrayInputStream(pk.getBytes());
192                         ByteArrayOutputStream baos = new ByteArrayOutputStream();
193                         Symm.base64noSplit.decode(bais, baos);
194
195                         return keyFactory.generatePublic(new X509EncodedKeySpec(baos.toByteArray()));
196                 } catch (InvalidKeySpecException e) {
197                         trans.error().log(e,"Translating Public Key from X509 KeySpec");
198                         return null;
199                 } finally {
200                         tt.done();
201                 }
202         }
203         
204         public static String toString(Trans trans, PublicKey pk) throws IOException {
205                 trans.debug().log("Public Key to String");
206                 return textBuilder("PUBLIC KEY",pk.getEncoded());
207         }
208
209         public static Collection<? extends Certificate> toX509Certificate(String x509) throws CertificateException {
210                 return toX509Certificate(x509.getBytes());
211         }
212         
213         public static Collection<? extends Certificate> toX509Certificate(List<String> x509s) throws CertificateException {
214                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
215                 try {
216                         for(String x509 : x509s) {
217                                 baos.write(x509.getBytes());
218                         }
219                 } catch (IOException e) {
220                         throw new CertificateException(e);
221                 }
222                 return toX509Certificate(new ByteArrayInputStream(baos.toByteArray()));
223         }
224
225         public static Collection<? extends Certificate> toX509Certificate(byte[] x509) throws CertificateException {
226                 return certificateFactory.generateCertificates(new ByteArrayInputStream(x509));
227         }
228
229         public static Collection<? extends Certificate> toX509Certificate(Trans trans, File file) throws CertificateException, FileNotFoundException {
230                 FileInputStream fis = new FileInputStream(file);
231                 try {
232                         return toX509Certificate(fis);
233                 } finally {
234                         try {
235                                 fis.close();
236                         } catch (IOException e) {
237                                 throw new CertificateException(e);
238                         }
239                 }
240         }
241
242         public static Collection<? extends Certificate> toX509Certificate(InputStream is) throws CertificateException {
243                 return certificateFactory.generateCertificates(is);
244         }
245
246         public static String toString(Trans trans, Certificate cert) throws IOException, CertException {
247                 if(trans.debug().isLoggable()) {
248                         StringBuilder sb = new StringBuilder("Certificate to String");
249                         if(cert instanceof X509Certificate) {
250                                 sb.append(" - ");
251                                 sb.append(((X509Certificate)cert).getSubjectDN());
252                         }
253                         trans.debug().log(sb);
254                 }
255                 try {
256                         if(cert==null) {
257                                 throw new CertException("Certificate not built");
258                         }
259                         return textBuilder("CERTIFICATE",cert.getEncoded());
260                 } catch (CertificateEncodingException e) {
261                         throw new CertException(e);
262                 }
263         }
264
265         public static Cipher pkCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
266                 return Cipher.getInstance(KEY_ALGO); 
267         }
268
269         public static Cipher pkCipher(Key key, boolean encrypt) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
270                 Cipher cipher = Cipher.getInstance(KEY_ALGO);
271                 cipher.init(encrypt?Cipher.ENCRYPT_MODE:Cipher.DECRYPT_MODE,key);
272                 return cipher;
273         }
274
275         public static byte[] strip(Reader rdr) throws IOException {
276                 BufferedReader br = new BufferedReader(rdr);
277                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
278                 String line;
279                 while((line=br.readLine())!=null) {
280                         if(line.length()>0 &&
281                            !line.startsWith("-----") &&
282                            line.indexOf(':')<0) {  // Header elements
283                                 baos.write(line.getBytes());
284                         }
285                 }
286                 return baos.toByteArray();
287         }
288         
289         public static class StripperInputStream extends InputStream {
290                 private Reader created;
291                 private BufferedReader br;
292                 private int idx;
293                 private String line;
294
295                 public StripperInputStream(Reader rdr) {
296                         if(rdr instanceof BufferedReader) {
297                                 br = (BufferedReader)rdr;
298                         } else {
299                                 br = new BufferedReader(rdr);
300                         }
301                         created = null;
302                 }
303                 
304                 public StripperInputStream(File file) throws FileNotFoundException {
305                         this(new FileReader(file));
306                         created = br;
307                 }
308
309                 public StripperInputStream(InputStream is) throws FileNotFoundException {
310                         this(new InputStreamReader(is));
311                         created = br;
312                 }
313
314                 @Override
315                 public int read() throws IOException {
316                         if(line==null || idx>=line.length()) {
317                                 while((line=br.readLine())!=null) {
318                                         if(line.length()>0 &&
319                                            !line.startsWith("-----") &&
320                                            line.indexOf(':')<0) {  // Header elements
321                                                 break;
322                                         }
323                                 }
324
325                                 if(line==null) {
326                                         return -1;
327                                 }
328                                 idx = 0;
329                         }
330                         return line.charAt(idx++);
331                 }
332
333                 /* (non-Javadoc)
334                  * @see java.io.InputStream#close()
335                  */
336                 @Override
337                 public void close() throws IOException {
338                         if(created!=null) {
339                                 created.close();
340                         }
341                 }
342         }
343
344         public static class Base64InputStream extends InputStream {
345                 private InputStream created;
346                 private InputStream is;
347                 private byte trio[];
348                 private byte duo[];
349                 private int idx;
350
351                 
352                 public Base64InputStream(File file) throws FileNotFoundException {
353                         this(new FileInputStream(file));
354                         created = is;
355                 }
356
357                 public Base64InputStream(InputStream is) throws FileNotFoundException {
358                         this.is = is;
359                         trio = new byte[3];
360                         idx = 4;
361                 }
362
363                 @Override
364                 public int read() throws IOException {
365                         if(duo==null || idx>=duo.length) {
366                                 int read = is.read(trio);
367                                 if(read==-1) {
368                                         return -1;
369                                 }
370                                 duo = Symm.base64.decode(trio);
371                                 if(duo==null || duo.length==0) {
372                                         return -1;
373                                 }
374                                 idx=0;
375                         }
376                         
377                         return duo[idx++];
378                 }
379
380                 /* (non-Javadoc)
381                  * @see java.io.InputStream#close()
382                  */
383                 @Override
384                 public void close() throws IOException {
385                         if(created!=null) {
386                                 created.close();
387                         }
388                 }
389         }
390
391         public static byte[] decode(byte[] bytes) throws IOException {
392                 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
393                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
394                 Symm.base64.decode(bais, baos);
395                 return baos.toByteArray();
396         }
397         
398         public static byte[] decode(File f) throws IOException {
399                 FileReader fr = new FileReader(f);
400                 try {
401                         return Factory.decode(fr);
402                 } finally {
403                         fr.close();
404                 }
405
406         }
407         public static byte[] decode(Reader rdr) throws IOException {
408                 return decode(strip(rdr));
409         }
410
411
412         public static byte[] binary(File file) throws IOException {
413                 DataInputStream dis = new DataInputStream(new FileInputStream(file));
414                 try {
415                         byte[] bytes = new byte[(int)file.length()];
416                         dis.readFully(bytes);
417                         return bytes;
418                 } finally {
419                         dis.close();
420                 }
421         }
422
423
424         public static byte[] sign(Trans trans, byte[] bytes, PrivateKey pk) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
425                 TimeTaken tt = trans.start("Sign Data", Env.SUB);
426                 try {
427                         Signature sig = Signature.getInstance(SIG_ALGO);
428                         sig.initSign(pk, random);
429                         sig.update(bytes);
430                         return sig.sign();
431                 } finally {
432                         tt.done();
433                 }
434         }
435
436         public static String toSignatureString(byte[] signed) throws IOException {
437                 return textBuilder("SIGNATURE", signed);
438         }
439
440         public static boolean verify(Trans trans, byte[] bytes, byte[] signature, PublicKey pk) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
441                 TimeTaken tt = trans.start("Verify Data", Env.SUB);
442                 try {
443                         Signature sig = Signature.getInstance(SIG_ALGO);
444                         sig.initVerify(pk);
445                         sig.update(bytes);
446                         return sig.verify(signature);
447                 } finally {
448                         tt.done();
449                 }       
450         }
451
452         /**
453          * Get the Security Provider, or, if not exists yet, attempt to load
454          * 
455          * @param providerType
456          * @param params
457          * @return
458          * @throws CertException
459          */
460         public static synchronized Provider getSecurityProvider(String providerType, String[][] params) throws CertException {
461                 Provider p = Security.getProvider(providerType);
462                 if(p!=null) {
463                         switch(providerType) {
464                                 case "PKCS12":
465                                         
466                                         break;
467                                 case "PKCS11": // PKCS11 only known to be supported by Sun
468                                         try {
469                                                 Class<?> clsSunPKCS11 = Class.forName("sun.security.pkcs11.SunPKCS11");
470                                                 Constructor<?> cnst = clsSunPKCS11.getConstructor(String.class);
471                                                 Object sunPKCS11 = cnst.newInstance(params[0][0]);
472                                                 if (sunPKCS11==null) {
473                                                         throw new CertException("SunPKCS11 Provider cannot be constructed for " + params[0][0]);
474                                                 }
475                                                 Security.addProvider((Provider)sunPKCS11);
476                                         } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
477                                                 throw new CertException(e);
478                                         }
479                                         break;
480                                 default:
481                                         throw new CertException(providerType + " is not a known Security Provider for your JDK.");
482                         }
483                 }
484                 return p;
485         }
486 }