/******************************************************************************* * 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; } }