| /* |
| * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.internal.security; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| |
| import javax.crypto.Cipher; |
| import javax.crypto.CipherInputStream; |
| import javax.crypto.IllegalBlockSizeException; |
| import javax.crypto.SecretKey; |
| import javax.crypto.SecretKeyFactory; |
| import javax.crypto.spec.DESKeySpec; |
| import javax.crypto.spec.IvParameterSpec; |
| import javax.crypto.spec.SecretKeySpec; |
| |
| import org.eclipse.persistence.exceptions.ConversionException; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.internal.helper.Helper; |
| |
| /** |
| * EclipseLink reference implementation for password encryption. |
| * |
| * @author Guy Pelletier |
| */ |
| public class JCEEncryptor implements Securable { |
| |
| // Legacy DES ECB cipher used for backwards compatibility decryption only. |
| private static final String DES_ECB = "DES/ECB/PKCS5Padding"; |
| private final Cipher decryptCipherDES_ECB; |
| |
| // Legacy AES ECB cipher used for backwards compatibility decryption only. |
| private static final String AES_ECB = "AES/ECB/PKCS5Padding"; |
| private final Cipher decryptCipherAES_ECB; |
| |
| // All encryption is done through the AES CBC cipher. |
| private static final String AES_CBC = "AES/CBC/PKCS5Padding"; |
| private final Cipher encryptCipherAES_CBC; |
| private final Cipher decryptCipherAES_CBC; |
| |
| public JCEEncryptor() throws Exception { |
| /** |
| * We want to force the initialization of the cipher here. This is a fix |
| * for bug #2696486. |
| * JDev with JDK 1.3 in some cases will allow a JCE object to be created |
| * when it shouldn't. That is, JDev includes an incompletely configured JCE |
| * library for JDK 1.3, meaning JCE will not run properly in the VM. So, JDev |
| * allows you to create a JCEEncryptor object, but eventually throw's |
| * errors when trying to make JCE library calls from encryptPassword. |
| * |
| * Confusing??? Well, don't move this code before talking to Guy first! |
| */ |
| decryptCipherDES_ECB = Cipher.getInstance(DES_ECB); |
| decryptCipherDES_ECB.init(Cipher.DECRYPT_MODE, Synergizer.getDESMultitasker()); |
| |
| decryptCipherAES_ECB = Cipher.getInstance(AES_ECB); |
| decryptCipherAES_ECB.init(Cipher.DECRYPT_MODE, Synergizer.getAESMultitasker()); |
| |
| SecretKey sk = Synergizer.getAESCBCMultitasker(); |
| IvParameterSpec iv = Synergizer.getIvSpec(); |
| encryptCipherAES_CBC = Cipher.getInstance(AES_CBC); |
| encryptCipherAES_CBC.init(Cipher.ENCRYPT_MODE, sk, iv); |
| |
| decryptCipherAES_CBC = Cipher.getInstance(AES_CBC); |
| decryptCipherAES_CBC.init(Cipher.DECRYPT_MODE, sk, iv); |
| } |
| |
| /** |
| * Encrypts a string. Will throw a validation exception. |
| */ |
| @Override |
| public synchronized String encryptPassword(String password) { |
| try { |
| return Helper.buildHexStringFromBytes(encryptCipherAES_CBC.doFinal(password.getBytes("UTF-8"))); |
| } catch (Exception e) { |
| throw ValidationException.errorEncryptingPassword(e); |
| } |
| } |
| |
| /** |
| * Decrypts a string. Will throw a validation exception. |
| * Handles backwards compatibility for older encrypted strings. |
| */ |
| @Override |
| public synchronized String decryptPassword(String encryptedPswd) { |
| if (encryptedPswd == null) { |
| return null; |
| } |
| |
| String password = null; |
| byte[] bytePassword = new byte[0]; |
| |
| try { |
| bytePassword = Helper.buildBytesFromHexString(encryptedPswd); |
| // try AES/CBC first |
| password = new String(decryptCipherAES_CBC.doFinal(bytePassword), "UTF-8"); |
| } catch (ConversionException | IllegalBlockSizeException ce) { |
| // buildBytesFromHexString failed, assume clear text |
| password = encryptedPswd; |
| } catch (Exception e) { |
| ObjectInputStream oisAes = null; |
| try { |
| // try AES/ECB second |
| oisAes = new ObjectInputStream(new CipherInputStream(new ByteArrayInputStream(bytePassword), decryptCipherAES_ECB)); |
| password = (String)oisAes.readObject(); |
| } catch (Exception f) { |
| ObjectInputStream oisDes = null; |
| try { |
| // try DES/ECB third |
| oisDes = new ObjectInputStream(new CipherInputStream(new ByteArrayInputStream(bytePassword), decryptCipherDES_ECB)); |
| password = (String)oisDes.readObject(); |
| } catch (ArrayIndexOutOfBoundsException g) { |
| // JCE 1.2.1 couldn't decrypt it, assume clear text |
| password = encryptedPswd; |
| } catch (Exception h) { |
| if (h.getCause() instanceof IllegalBlockSizeException) { |
| // JCE couldn't decrypt it, assume clear text |
| password = encryptedPswd; |
| } else { |
| throw ValidationException.errorDecryptingPassword(h); |
| } |
| } finally { |
| if (oisDes != null) { |
| try { |
| oisDes.close(); |
| } catch (IOException e2) {} |
| } |
| } |
| } finally { |
| if (oisAes != null) { |
| try { |
| oisAes.close(); |
| } catch (IOException e1) {} |
| } |
| } |
| } |
| |
| return password; |
| } |
| |
| /** |
| * Returns multitaskers for the ciphers. :-) |
| */ |
| private static class Synergizer { |
| private static SecretKey getDESMultitasker() throws Exception { |
| SecretKeyFactory factory = SecretKeyFactory.getInstance("DES"); |
| return factory.generateSecret(new DESKeySpec(Helper.buildBytesFromHexString("E60B80C7AEC78038"))); |
| } |
| |
| private static SecretKey getAESMultitasker() throws Exception { |
| return new SecretKeySpec(Helper.buildBytesFromHexString("3E7CFEF156E712906E1F603B59463C67"), "AES"); |
| } |
| |
| private static SecretKey getAESCBCMultitasker() throws Exception { |
| return new SecretKeySpec(Helper.buildBytesFromHexString("2DB7354A48F1CA7B48ACA247540FC923"), "AES"); |
| } |
| |
| private static IvParameterSpec getIvSpec() { |
| byte[] b = new byte[] { |
| (byte) -26, (byte) 124, (byte) -99, (byte) 32, |
| (byte) -37, (byte) -58, (byte) -93, (byte) 100, |
| (byte) 126, (byte) -55, (byte) -21, (byte) 48, |
| (byte) -86, (byte) 97, (byte) 12, (byte) 113}; |
| return new IvParameterSpec(b); |
| } |
| } |
| } |