/hazelcast/src/main/java/com/hazelcast/nio/CipherHelper.java

https://bitbucket.org/gabral6_gmailcom/hazelcast · Java · 253 lines · 204 code · 29 blank · 20 comment · 30 complexity · 380f1248decd829a16874cc47b8f148b MD5 · raw file

  1. /*
  2. * Copyright (c) 2008-2013, Hazelcast, Inc. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.hazelcast.nio;
  17. import com.hazelcast.config.AsymmetricEncryptionConfig;
  18. import com.hazelcast.config.SymmetricEncryptionConfig;
  19. import com.hazelcast.logging.ILogger;
  20. import com.hazelcast.logging.Logger;
  21. import javax.crypto.Cipher;
  22. import javax.crypto.SecretKey;
  23. import javax.crypto.SecretKeyFactory;
  24. import javax.crypto.spec.*;
  25. import java.nio.ByteBuffer;
  26. import java.security.*;
  27. import java.security.spec.AlgorithmParameterSpec;
  28. import java.security.spec.KeySpec;
  29. import java.util.logging.Level;
  30. final class CipherHelper {
  31. private static AsymmetricCipherBuilder asymmetricCipherBuilder = null;
  32. private static SymmetricCipherBuilder symmetricCipherBuilder = null;
  33. final static ILogger logger = Logger.getLogger(CipherHelper.class.getName());
  34. static {
  35. try {
  36. if (Boolean.getBoolean("hazelcast.security.bouncy.enabled")) {
  37. String provider = "org.bouncycastle.jce.provider.BouncyCastleProvider";
  38. Security.addProvider((Provider) Class.forName(provider).newInstance());
  39. }
  40. } catch (Exception e) {
  41. logger.log(Level.WARNING, e.getMessage(), e);
  42. }
  43. }
  44. @SuppressWarnings("SynchronizedMethod")
  45. public static synchronized Cipher createAsymmetricReaderCipher(IOService ioService, String remoteAlias) throws Exception {
  46. if (asymmetricCipherBuilder == null) {
  47. asymmetricCipherBuilder = new AsymmetricCipherBuilder(ioService);
  48. }
  49. return asymmetricCipherBuilder.getReaderCipher(remoteAlias);
  50. }
  51. @SuppressWarnings("SynchronizedMethod")
  52. public static synchronized Cipher createAsymmetricWriterCipher(IOService ioService) throws Exception {
  53. if (asymmetricCipherBuilder == null) {
  54. asymmetricCipherBuilder = new AsymmetricCipherBuilder(ioService);
  55. }
  56. return asymmetricCipherBuilder.getWriterCipher();
  57. }
  58. @SuppressWarnings("SynchronizedMethod")
  59. public static synchronized Cipher createSymmetricReaderCipher(IOService ioService) throws Exception {
  60. if (symmetricCipherBuilder == null) {
  61. symmetricCipherBuilder = new SymmetricCipherBuilder(ioService.getSymmetricEncryptionConfig());
  62. }
  63. return symmetricCipherBuilder.getReaderCipher(null);
  64. }
  65. @SuppressWarnings("SynchronizedMethod")
  66. public static synchronized Cipher createSymmetricWriterCipher(IOService ioService) throws Exception {
  67. if (symmetricCipherBuilder == null) {
  68. symmetricCipherBuilder = new SymmetricCipherBuilder(ioService.getSymmetricEncryptionConfig());
  69. }
  70. return symmetricCipherBuilder.getWriterCipher();
  71. }
  72. public static boolean isAsymmetricEncryptionEnabled(IOService ioService) {
  73. AsymmetricEncryptionConfig aec = ioService.getAsymmetricEncryptionConfig();
  74. return (aec != null && aec.isEnabled());
  75. }
  76. public static boolean isSymmetricEncryptionEnabled(IOService ioService) {
  77. SymmetricEncryptionConfig sec = ioService.getSymmetricEncryptionConfig();
  78. return (sec != null && sec.isEnabled());
  79. }
  80. public static String getKeyAlias(IOService ioService) {
  81. AsymmetricEncryptionConfig aec = ioService.getAsymmetricEncryptionConfig();
  82. return aec.getKeyAlias();
  83. }
  84. interface CipherBuilder {
  85. Cipher getWriterCipher() throws Exception;
  86. Cipher getReaderCipher(String param) throws Exception;
  87. boolean isAsymmetric();
  88. }
  89. static class AsymmetricCipherBuilder implements CipherBuilder {
  90. String algorithm = "RSA/NONE/PKCS1PADDING";
  91. KeyStore keyStore;
  92. private final IOService ioService;
  93. AsymmetricCipherBuilder(IOService ioService) {
  94. this.ioService = ioService;
  95. try {
  96. AsymmetricEncryptionConfig aec = ioService.getAsymmetricEncryptionConfig();
  97. algorithm = aec.getAlgorithm();
  98. keyStore = KeyStore.getInstance(aec.getStoreType());
  99. // get user password and file input stream
  100. char[] password = aec.getStorePassword().toCharArray();
  101. java.io.FileInputStream fis =
  102. new java.io.FileInputStream(aec.getStorePath());
  103. keyStore.load(fis, password);
  104. fis.close();
  105. } catch (Exception e) {
  106. logger.log(Level.WARNING, e.getMessage(), e);
  107. }
  108. }
  109. public Cipher getReaderCipher(String remoteAlias) throws Exception {
  110. java.security.cert.Certificate certificate = keyStore.getCertificate(remoteAlias);
  111. PublicKey publicKey = certificate.getPublicKey();
  112. Cipher cipher = Cipher.getInstance(algorithm);
  113. cipher.init(Cipher.DECRYPT_MODE, publicKey);
  114. return cipher;
  115. }
  116. public Cipher getWriterCipher() throws Exception {
  117. AsymmetricEncryptionConfig aec = ioService.getAsymmetricEncryptionConfig();
  118. Cipher cipher = Cipher.getInstance(algorithm);
  119. KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry)
  120. keyStore.getEntry(aec.getKeyAlias(), new KeyStore.PasswordProtection(aec.getKeyPassword().toCharArray()));
  121. PrivateKey privateKey = pkEntry.getPrivateKey();
  122. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
  123. return cipher;
  124. }
  125. public boolean isAsymmetric() {
  126. return true;
  127. }
  128. }
  129. static class SymmetricCipherBuilder implements CipherBuilder {
  130. final String algorithm;
  131. // 8-byte Salt
  132. final byte[] salt;
  133. final String passPhrase;
  134. final int iterationCount;
  135. byte[] keyBytes;
  136. SymmetricCipherBuilder(SymmetricEncryptionConfig sec) {
  137. algorithm = sec.getAlgorithm();
  138. passPhrase = sec.getPassword();
  139. salt = createSalt(sec.getSalt());
  140. iterationCount = sec.getIterationCount();
  141. keyBytes = sec.getKey();
  142. }
  143. byte[] createSalt(String saltStr) {
  144. long hash = 0;
  145. char chars[] = saltStr.toCharArray();
  146. for (char c : chars) {
  147. hash = 31 * hash + c;
  148. }
  149. byte[] theSalt = new byte[8];
  150. theSalt[0] = (byte) (hash >>> 56);
  151. theSalt[1] = (byte) (hash >>> 48);
  152. theSalt[2] = (byte) (hash >>> 40);
  153. theSalt[3] = (byte) (hash >>> 32);
  154. theSalt[4] = (byte) (hash >>> 24);
  155. theSalt[5] = (byte) (hash >>> 16);
  156. theSalt[6] = (byte) (hash >>> 8);
  157. theSalt[7] = (byte) (hash);
  158. return theSalt;
  159. }
  160. public Cipher create(boolean encryptMode) {
  161. try {
  162. int mode = (encryptMode) ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
  163. Cipher cipher = Cipher.getInstance(algorithm);
  164. String keyAlgorithm = algorithm;
  165. if (algorithm.indexOf('/') != -1) {
  166. keyAlgorithm = algorithm.substring(0, algorithm.indexOf('/'));
  167. }
  168. // 32-bit digest key=pass+salt
  169. ByteBuffer bbPass = ByteBuffer.allocate(32);
  170. MessageDigest md = MessageDigest.getInstance("MD5");
  171. bbPass.put(md.digest(passPhrase.getBytes()));
  172. md.reset();
  173. byte[] saltDigest = md.digest(salt);
  174. bbPass.put(saltDigest);
  175. boolean isCBC = algorithm.indexOf("/CBC/") != -1;
  176. SecretKey key = null;
  177. //CBC mode requires IvParameter with 8 byte input
  178. int ivLength = 8;
  179. AlgorithmParameterSpec paramSpec = null;
  180. if (keyBytes == null) {
  181. keyBytes = bbPass.array();
  182. }
  183. if (algorithm.startsWith("AES")) {
  184. ivLength = 16;
  185. key = new SecretKeySpec(keyBytes, "AES");
  186. } else if (algorithm.startsWith("Blowfish")) {
  187. key = new SecretKeySpec(keyBytes, "Blowfish");
  188. } else if (algorithm.startsWith("DESede")) {
  189. //requires at least 192 bits (24 bytes)
  190. KeySpec keySpec = new DESedeKeySpec(keyBytes);
  191. key = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
  192. } else if (algorithm.startsWith("DES")) {
  193. KeySpec keySpec = new DESKeySpec(keyBytes);
  194. key = SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
  195. } else if (algorithm.startsWith("PBEWith")) {
  196. paramSpec = new PBEParameterSpec(salt, iterationCount);
  197. KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
  198. key = SecretKeyFactory.getInstance(keyAlgorithm).generateSecret(keySpec);
  199. }
  200. if (isCBC) {
  201. byte[] iv = (ivLength == 8) ? salt : saltDigest;
  202. paramSpec = new IvParameterSpec(iv);
  203. }
  204. cipher.init(mode, key, paramSpec);
  205. return cipher;
  206. } catch (Throwable e) {
  207. throw new RuntimeException("unable to create Cipher:" + e.getMessage(), e);
  208. }
  209. }
  210. public Cipher getWriterCipher() {
  211. return create(true);
  212. }
  213. public Cipher getReaderCipher(String ignored) {
  214. return create(false);
  215. }
  216. public boolean isAsymmetric() {
  217. return false;
  218. }
  219. }
  220. public static void handleCipherException(Exception e, Connection connection) {
  221. logger.log(Level.WARNING, e.getMessage(), e);
  222. connection.close();
  223. }
  224. }