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