2 * Copyright (c) 2016, 2017 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.aaa.encrypt.impl;
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;
50 * Provides a basic encryption service implementation with configuration knobs.
52 * @author - Sharon Aicler (saichler@gmail.com)
55 public class AAAEncryptionServiceImpl implements AAAEncryptionService {
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();
63 private final SecretKey key;
64 private final IvParameterSpec ivspec;
65 private final Cipher encryptCipher;
66 private final Cipher decryptCipher;
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());
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);
86 final byte[] enryptionKeySalt = Base64.getDecoder().decode(encrySrvConfig.getEncryptSalt());
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);
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);
107 encryptCipher = cipher;
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);
116 decryptCipher = cipher;
120 public String encrypt(final String data) {
121 // We could not instantiate the encryption key, hence no encryption or
122 // decryption will be done.
124 LOG.warn("Encryption Key is NULL, will not encrypt data.");
128 final byte[] cryptobytes;
130 synchronized (encryptCipher) {
131 cryptobytes = encryptCipher.doFinal(data.getBytes(Charset.defaultCharset()));
133 } catch (IllegalBlockSizeException | BadPaddingException e) {
134 LOG.error("Failed to encrypt data.", e);
137 return Base64.getEncoder().encodeToString(cryptobytes);
141 public byte[] encrypt(final byte[] data) {
142 // We could not instantiate the encryption key, hence no encryption or
143 // decryption will be done.
145 LOG.warn("Encryption Key is NULL, will not encrypt data.");
149 synchronized (encryptCipher) {
150 return encryptCipher.doFinal(data);
152 } catch (IllegalBlockSizeException | BadPaddingException e) {
153 LOG.error("Failed to encrypt data.", e);
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;
165 final byte[] cryptobytes = Base64.getDecoder().decode(encryptedData);
166 final byte[] clearbytes;
168 clearbytes = decryptCipher.doFinal(cryptobytes);
169 } catch (IllegalBlockSizeException | BadPaddingException e) {
170 LOG.error("Failed to decrypt encoded data", e);
171 return encryptedData;
173 return new String(clearbytes, Charset.defaultCharset());
177 public byte[] decrypt(final byte[] encryptedData) {
178 if (encryptedData == null) {
179 LOG.warn("encryptedData is null.");
180 return encryptedData;
183 return decryptCipher.doFinal(encryptedData);
184 } catch (IllegalBlockSizeException | BadPaddingException e) {
185 LOG.error("Failed to decrypt encoded data", e);
187 return encryptedData;
190 private static void updateEncrySrvConfig(final String newPwd, final String newSalt) {
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);
210 LOG.warn("The encryption service config file does not exist {}", DEFAULT_CONFIG_FILE_PATH);
212 } catch (ParserConfigurationException | TransformerException | SAXException | IOException e) {
213 LOG.error("Error while updating the encryption service config file", e);
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);