/*******************************************************************************
* Copyright (c) 2011, 2016 Eurotech and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eurotech
* Red Hat Inc - Fix possible NPE, fix loading error
* - Clean up kura.properties handling
*******************************************************************************/
package org.eclipse.kura.core.crypto;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.eclipse.kura.KuraErrorCode;
import org.eclipse.kura.KuraException;
import org.eclipse.kura.crypto.CryptoService;
import org.eclipse.kura.system.SystemService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CryptoServiceImpl implements CryptoService {
private static final Logger logger = LoggerFactory.getLogger(CryptoServiceImpl.class);
private static final String ALGORITHM = "AES";
private static final byte[] SECRET_KEY = "rv;ipse329183!@#".getBytes();
private String m_keystorePasswordPath;
private SystemService m_systemService;
public void setSystemService(SystemService systemService) {
this.m_systemService = systemService;
}
public void unsetSystemService(SystemService systemService) {
this.m_systemService = null;
}
protected void activate() {
if (this.m_systemService == null) {
throw new IllegalStateException("Unable to get instance of: " + SystemService.class.getName());
}
this.m_keystorePasswordPath = this.m_systemService.getKuraDataDirectory() + File.separator + "store.save";
}
@Override
public char[] encryptAes(char[] value) throws KuraException {
String encryptedValue = null;
try {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = c.doFinal(new String(value).getBytes());
encryptedValue = base64Encode(encryptedBytes);
} catch (NoSuchAlgorithmException e) {
throw new KuraException(KuraErrorCode.OPERATION_NOT_SUPPORTED);
} catch (NoSuchPaddingException e) {
throw new KuraException(KuraErrorCode.OPERATION_NOT_SUPPORTED);
} catch (InvalidKeyException e) {
throw new KuraException(KuraErrorCode.ENCODE_ERROR);
} catch (IllegalBlockSizeException e) {
throw new KuraException(KuraErrorCode.ENCODE_ERROR);
} catch (BadPaddingException e) {
throw new KuraException(KuraErrorCode.ENCODE_ERROR);
}
return encryptedValue.toCharArray();
}
private byte[] base64Decode(String internalStringValue) {
Object convertedData = null;
try {
Class<?> clazz = Class.forName("javax.xml.bind.DatatypeConverter");
Method method = clazz.getMethod("parseBase64Binary", String.class);
convertedData = method.invoke(null, internalStringValue);
} catch (ClassNotFoundException e) {
convertedData = base64DecodeJava8(internalStringValue);
} catch (LinkageError e) {
convertedData = base64DecodeJava8(internalStringValue);
} catch (Exception e) {
}
if (convertedData != null && convertedData instanceof byte[]) {
return (byte[]) convertedData;
}
return null;
}
private Object base64DecodeJava8(String internalStringValue) {
Object convertedData = null;
try {
Class<?> clazz = Class.forName("java.util.Base64");
Method decoderMethod = clazz.getMethod("getDecoder", (Class<?>[]) null);
Object decoder = decoderMethod.invoke(null, new Object[0]);
Class<?> base64Decoder = Class.forName("java.util.Base64$Decoder");
Method decodeMethod = base64Decoder.getMethod("decode", String.class);
convertedData = decodeMethod.invoke(decoder, internalStringValue);
} catch (Exception e1) {
}
return convertedData;
}
private String base64Encode(byte[] encryptedBytes) {
Object convertedData = null;
try {
Class<?> clazz = Class.forName("javax.xml.bind.DatatypeConverter");
Method method = clazz.getMethod("printBase64Binary", byte[].class);
convertedData = method.invoke(null, encryptedBytes);
} catch (ClassNotFoundException e) {
convertedData = base64EncodeJava8(encryptedBytes);
} catch (LinkageError e) {
convertedData = base64EncodeJava8(encryptedBytes);
} catch (Exception e) {
}
if (convertedData != null) {
return (String) convertedData;
}
return null;
}
private Object base64EncodeJava8(byte[] encryptedBytes) {
Object convertedData = null;
try {
Class<?> clazz = Class.forName("java.util.Base64");
Method encoderMethod = clazz.getMethod("getEncoder", (Class<?>[]) null);
Object encoder = encoderMethod.invoke(null, new Object[0]);
Class<?> base64Encoder = Class.forName("java.util.Base64$Encoder");
Method encodeMethod = base64Encoder.getMethod("encodeToString", byte[].class);
convertedData = encodeMethod.invoke(encoder, encryptedBytes);
} catch (Exception e1) {
}
return convertedData;
}
@Override
public char[] decryptAes(char[] encryptedValue) throws KuraException {
Key key = generateKey();
Cipher c;
try {
c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
String internalStringValue = new String(encryptedValue);
byte[] decodedValue = base64Decode(internalStringValue);
if (encryptedValue.length > 0 && decodedValue.length == 0) {
throw new KuraException(KuraErrorCode.DECODER_ERROR);
}
byte[] decryptedBytes = c.doFinal(decodedValue);
String decryptedValue = new String(decryptedBytes);
return decryptedValue.toCharArray();
} catch (NoSuchAlgorithmException e) {
throw new KuraException(KuraErrorCode.OPERATION_NOT_SUPPORTED);
} catch (NoSuchPaddingException e) {
throw new KuraException(KuraErrorCode.OPERATION_NOT_SUPPORTED);
} catch (InvalidKeyException e) {
throw new KuraException(KuraErrorCode.DECODER_ERROR);
} catch (BadPaddingException e) {
throw new KuraException(KuraErrorCode.DECODER_ERROR);
} catch (IllegalBlockSizeException e) {
throw new KuraException(KuraErrorCode.DECODER_ERROR);
}
}
@Override
@Deprecated
public String encryptAes(String value) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
char[] encryptedValue = null;
try {
encryptedValue = encryptAes(value.toCharArray());
} catch (KuraException e) {
Throwable t = e.getCause();
if (t instanceof NoSuchAlgorithmException) {
throw (NoSuchAlgorithmException) t;
} else if (t instanceof NoSuchPaddingException) {
throw (NoSuchPaddingException) t;
} else if (t instanceof InvalidKeyException) {
throw (InvalidKeyException) t;
} else if (t instanceof IllegalBlockSizeException) {
throw (IllegalBlockSizeException) t;
} else if (t instanceof BadPaddingException) {
throw (BadPaddingException) t;
}
}
return new String(encryptedValue);
}
@Override
@Deprecated
public String decryptAes(String encryptedValue) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IOException, IllegalBlockSizeException, BadPaddingException {
try {
return new String(decryptAes(encryptedValue.toCharArray()));
} catch (KuraException e) {
throw new IOException();
}
}
@Override
public String sha1Hash(String s) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest cript = MessageDigest.getInstance("SHA-1");
cript.reset();
cript.update(s.getBytes("UTF8"));
byte[] encodedBytes = cript.digest();
return base64Encode(encodedBytes);
}
@Override
public String encodeBase64(String stringValue) throws NoSuchAlgorithmException, UnsupportedEncodingException {
byte[] bytesValue = stringValue.getBytes("UTF-8");
String encodedValue = base64Encode(bytesValue);
return encodedValue;
}
@Override
public String decodeBase64(String encodedValue) throws NoSuchAlgorithmException, UnsupportedEncodingException {
byte[] decodedBytes = base64Decode(encodedValue);
String decodedValue = new String(decodedBytes, "UTF-8");
return decodedValue;
}
@Override
public char[] getKeyStorePassword(String keyStorePath) {
Properties props = new Properties();
char[] password = null;
FileInputStream fis = null;
File f = new File(this.m_keystorePasswordPath);
if (!f.exists()) {
return "changeit".toCharArray();
}
try {
fis = new FileInputStream(this.m_keystorePasswordPath);
props.load(fis);
Object value = props.get(keyStorePath);
if (value != null) {
String encryptedPassword = (String) value;
password = decryptAes(encryptedPassword.toCharArray());
}
} catch (FileNotFoundException e) {
logger.warn("File not found exception while getting keystore password - ", e);
} catch (IOException e) {
logger.warn("IOException while getting keystore password - ", e);
} catch (KuraException e) {
logger.warn("KuraException while getting keystore password - ", e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
logger.warn("IOException while closing source - ", e);
}
}
}
return password;
}
@Override
public void setKeyStorePassword(String keyStorePath, char[] password) throws KuraException {
Properties props = new Properties();
char[] encryptedPassword = encryptAes(password);
props.put(keyStorePath, new String(encryptedPassword));
FileOutputStream fos = null;
try {
fos = new FileOutputStream(this.m_keystorePasswordPath);
props.store(fos, "Do not edit this file. It's automatically generated by Kura");
} catch (FileNotFoundException e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
} catch (IOException e) {
throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
logger.warn("IOException while closing destination - ", e);
}
}
}
}
@Override
@Deprecated
public void setKeyStorePassword(String keyStorePath, String password) throws IOException {
try {
setKeyStorePassword(keyStorePath, password.toCharArray());
} catch (KuraException e) {
throw new IOException(e);
}
}
@Override
public boolean isFrameworkSecure() {
return false;
}
private static Key generateKey() {
Key key = new SecretKeySpec(SECRET_KEY, ALGORITHM);
return key;
}
}