Bump upstreams
[aaa.git] / aaa-encrypt-service / impl / src / main / java / org / opendaylight / aaa / encrypt / impl / AAAEncryptionServiceImpl.java
1 /*
2  * Copyright (c) 2016, 2017 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.aaa.encrypt.impl;
9
10 import java.io.File;
11 import java.io.IOException;
12 import java.nio.charset.Charset;
13 import java.security.InvalidAlgorithmParameterException;
14 import java.security.InvalidKeyException;
15 import java.security.NoSuchAlgorithmException;
16 import java.security.SecureRandom;
17 import java.security.spec.InvalidKeySpecException;
18 import java.security.spec.KeySpec;
19 import java.util.Base64;
20 import javax.crypto.BadPaddingException;
21 import javax.crypto.Cipher;
22 import javax.crypto.IllegalBlockSizeException;
23 import javax.crypto.NoSuchPaddingException;
24 import javax.crypto.SecretKey;
25 import javax.crypto.SecretKeyFactory;
26 import javax.crypto.spec.IvParameterSpec;
27 import javax.crypto.spec.PBEKeySpec;
28 import javax.crypto.spec.SecretKeySpec;
29 import javax.xml.parsers.DocumentBuilder;
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import javax.xml.parsers.ParserConfigurationException;
32 import javax.xml.transform.Transformer;
33 import javax.xml.transform.TransformerException;
34 import javax.xml.transform.TransformerFactory;
35 import javax.xml.transform.dom.DOMSource;
36 import javax.xml.transform.stream.StreamResult;
37 import org.apache.commons.lang3.RandomStringUtils;
38 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
39 import org.opendaylight.mdsal.binding.api.DataBroker;
40 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
41 import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfig;
42 import org.opendaylight.yang.gen.v1.config.aaa.authn.encrypt.service.config.rev160915.AaaEncryptServiceConfigBuilder;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45 import org.w3c.dom.Document;
46 import org.w3c.dom.Node;
47 import org.xml.sax.SAXException;
48
49 /**
50  * Provides a basic encryption service implementation with configuration knobs.
51  *
52  * @author - Sharon Aicler (saichler@gmail.com)
53  */
54 @Deprecated
55 public class AAAEncryptionServiceImpl implements AAAEncryptionService {
56
57     private static final Logger LOG = LoggerFactory.getLogger(AAAEncryptionServiceImpl.class);
58     private static final String DEFAULT_CONFIG_FILE_PATH = "etc" + File.separator + "opendaylight" + File.separator
59             + "datastore" + File.separator + "initial" + File.separator + "config" + File.separator
60             + "aaa-encrypt-service-config.xml";
61     private static final SecureRandom RANDOM = new SecureRandom();
62
63     private final SecretKey key;
64     private final IvParameterSpec ivspec;
65     private final Cipher encryptCipher;
66     private final Cipher decryptCipher;
67
68     public AAAEncryptionServiceImpl(AaaEncryptServiceConfig encrySrvConfig, final DataBroker dataBroker) {
69         SecretKey tempKey = null;
70         IvParameterSpec tempIvSpec = null;
71         if (encrySrvConfig.getEncryptSalt() == null) {
72             throw new IllegalArgumentException(
73                     "null encryptSalt in AaaEncryptServiceConfig: " + encrySrvConfig.toString());
74         }
75         if (encrySrvConfig.getEncryptKey() != null && encrySrvConfig.getEncryptKey().isEmpty()) {
76             LOG.debug("Set the Encryption service password and encrypt salt");
77             String newPwd = RandomStringUtils.random(encrySrvConfig.getPasswordLength(), true, true);
78             byte[] salt = new byte[16];
79             RANDOM.nextBytes(salt);
80             String encodedSalt = Base64.getEncoder().encodeToString(salt);
81             encrySrvConfig = new AaaEncryptServiceConfigBuilder(encrySrvConfig).setEncryptKey(newPwd)
82                     .setEncryptSalt(encodedSalt).build();
83             updateEncrySrvConfig(newPwd, encodedSalt);
84             initializeConfigDataTree(encrySrvConfig, dataBroker);
85         }
86         final byte[] enryptionKeySalt = Base64.getDecoder().decode(encrySrvConfig.getEncryptSalt());
87         try {
88             final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encrySrvConfig.getEncryptMethod());
89             final KeySpec spec = new PBEKeySpec(encrySrvConfig.getEncryptKey().toCharArray(), enryptionKeySalt,
90                     encrySrvConfig.getEncryptIterationCount(), encrySrvConfig.getEncryptKeyLength());
91             tempKey = keyFactory.generateSecret(spec);
92             tempKey = new SecretKeySpec(tempKey.getEncoded(), encrySrvConfig.getEncryptType());
93             tempIvSpec = new IvParameterSpec(enryptionKeySalt);
94         } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
95             LOG.error("Failed to initialize secret key", e);
96         }
97         key = tempKey;
98         ivspec = tempIvSpec;
99         Cipher cipher = null;
100         try {
101             cipher = Cipher.getInstance(encrySrvConfig.getCipherTransforms());
102             cipher.init(Cipher.ENCRYPT_MODE, key, ivspec);
103         } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException
104                 | InvalidKeyException e) {
105             LOG.error("Failed to create encrypt cipher.", e);
106         }
107         encryptCipher = cipher;
108         cipher = null;
109         try {
110             cipher = Cipher.getInstance(encrySrvConfig.getCipherTransforms());
111             cipher.init(Cipher.DECRYPT_MODE, key, ivspec);
112         } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException
113                 | InvalidKeyException e) {
114             LOG.error("Failed to create decrypt cipher.", e);
115         }
116         decryptCipher = cipher;
117     }
118
119     @Override
120     public String encrypt(final String data) {
121         // We could not instantiate the encryption key, hence no encryption or
122         // decryption will be done.
123         if (key == null) {
124             LOG.warn("Encryption Key is NULL, will not encrypt data.");
125             return data;
126         }
127
128         final byte[] cryptobytes;
129         try {
130             synchronized (encryptCipher) {
131                 cryptobytes = encryptCipher.doFinal(data.getBytes(Charset.defaultCharset()));
132             }
133         } catch (IllegalBlockSizeException | BadPaddingException e) {
134             LOG.error("Failed to encrypt data.", e);
135             return data;
136         }
137         return Base64.getEncoder().encodeToString(cryptobytes);
138     }
139
140     @Override
141     public byte[] encrypt(final byte[] data) {
142         // We could not instantiate the encryption key, hence no encryption or
143         // decryption will be done.
144         if (key == null) {
145             LOG.warn("Encryption Key is NULL, will not encrypt data.");
146             return data;
147         }
148         try {
149             synchronized (encryptCipher) {
150                 return encryptCipher.doFinal(data);
151             }
152         } catch (IllegalBlockSizeException | BadPaddingException e) {
153             LOG.error("Failed to encrypt data.", e);
154             return data;
155         }
156     }
157
158     @Override
159     public String decrypt(final String encryptedData) {
160         if (key == null || encryptedData == null || encryptedData.length() == 0) {
161             LOG.warn("String {} was not decrypted.", encryptedData);
162             return encryptedData;
163         }
164
165         final byte[] cryptobytes = Base64.getDecoder().decode(encryptedData);
166         final byte[] clearbytes;
167         try {
168             clearbytes = decryptCipher.doFinal(cryptobytes);
169         } catch (IllegalBlockSizeException | BadPaddingException e) {
170             LOG.error("Failed to decrypt encoded data", e);
171             return encryptedData;
172         }
173         return new String(clearbytes, Charset.defaultCharset());
174     }
175
176     @Override
177     public byte[] decrypt(final byte[] encryptedData) {
178         if (encryptedData == null) {
179             LOG.warn("encryptedData is null.");
180             return encryptedData;
181         }
182         try {
183             return decryptCipher.doFinal(encryptedData);
184         } catch (IllegalBlockSizeException | BadPaddingException e) {
185             LOG.error("Failed to decrypt encoded data", e);
186         }
187         return encryptedData;
188     }
189
190     private static void updateEncrySrvConfig(final String newPwd, final String newSalt) {
191         try {
192             final String encryptKeyTag = "encrypt-key";
193             final String encryptSaltTag = "encrypt-salt";
194             LOG.debug("Update encryption service config file");
195             final File configFile = new File(DEFAULT_CONFIG_FILE_PATH);
196             if (configFile.exists()) {
197                 final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
198                 final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
199                 final Document doc = docBuilder.parse(configFile);
200                 final Node keyNode = doc.getElementsByTagName(encryptKeyTag).item(0);
201                 keyNode.setTextContent(newPwd);
202                 final Node salt = doc.getElementsByTagName(encryptSaltTag).item(0);
203                 salt.setTextContent(newSalt);
204                 final TransformerFactory transformerFactory = TransformerFactory.newInstance();
205                 final Transformer transformer = transformerFactory.newTransformer();
206                 final DOMSource source = new DOMSource(doc);
207                 final StreamResult result = new StreamResult(new File(DEFAULT_CONFIG_FILE_PATH));
208                 transformer.transform(source, result);
209             } else {
210                 LOG.warn("The encryption service config file does not exist {}", DEFAULT_CONFIG_FILE_PATH);
211             }
212         } catch (ParserConfigurationException | TransformerException | SAXException | IOException e) {
213             LOG.error("Error while updating the encryption service config file", e);
214         }
215     }
216
217     private static void initializeConfigDataTree(final AaaEncryptServiceConfig encrySrvConfig,
218             final DataBroker dataBroker) {
219         if (MdsalUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
220                 MdsalUtils.getEncryptionSrvConfigIid()) == null) {
221             MdsalUtils.initalizeDatastore(LogicalDatastoreType.CONFIGURATION, dataBroker,
222                     MdsalUtils.getEncryptionSrvConfigIid(), encrySrvConfig);
223         }
224     }
225 }