com.sun.faces.util.ByteArrayGuardAESCTR Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jakarta.faces Show documentation
Show all versions of jakarta.faces Show documentation
EE4J Compatible Implementation for Jakarta Faces API
The newest version!
/*
* Copyright (c) 1997, 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.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.faces.util;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.SortedMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import jakarta.faces.FacesException;
/**
*
* This utility class is to provide both encryption and decryption Ciphers
to
* ResponseStateManager
implementations wishing to provide encryption support.
*
*
*
* The algorithm used to encrypt byte array is AES with CBC.
*
*
*
* Original author Inderjeet Singh, J2EE Blue Prints Team. Modified to suit Faces needs.
*
*/
public final class ByteArrayGuardAESCTR {
// Log instance for this class
private static final Logger LOGGER = FacesLogger.RENDERKIT.getLogger();
private static final int KEY_LENGTH = 128;
private static final String KEY_ALGORITHM = "AES";
private static final String CIPHER_CODE = "AES/CTR/NoPadding";
private SecretKey sk;
private Charset utf8;
// ------------------------------------------------------------ Constructors
public ByteArrayGuardAESCTR() {
try {
setupKeyAndCharset();
} catch (Exception e) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, "Unexpected exception initializing encryption." + " No encryption will be performed.", e);
}
System.err.println("ERROR: Initializing Ciphers");
}
}
// ---------------------------------------------------------- Public Methods
/**
* This method: Encrypts bytes using a cipher. Generates MAC for intialization vector of the cipher Generates MAC for
* encrypted data Returns a byte array consisting of the following concatenated together: |MAC for cnrypted Data | MAC
* for Init Vector | Encrypted Data |
*
* @param value The value to be encrypted.
* @return the encrypted value.
*/
public String encrypt(String value) {
String securedata = null;
byte[] bytes = value.getBytes(utf8);
try {
SecureRandom rand = new SecureRandom();
byte[] iv = new byte[16];
rand.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher encryptCipher = Cipher.getInstance(CIPHER_CODE);
encryptCipher.init(Cipher.ENCRYPT_MODE, sk, ivspec);
// encrypt the plaintext
byte[] encdata = encryptCipher.doFinal(bytes);
byte[] temp = concatBytes(iv, encdata);
// Base64 encode the encrypted bytes
securedata = Base64.getEncoder().encodeToString(temp);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException
| BadPaddingException e) {
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, "Unexpected exception initializing encryption." + " No encryption will be performed.", e);
}
return null;
}
return securedata;
}
public String decrypt(String value) throws InvalidKeyException {
byte[] bytes = Base64.getDecoder().decode(value);
try {
byte[] iv = new byte[16];
if (bytes.length < iv.length) {
throw new InvalidKeyException("Invalid characters in decrypted value");
}
System.arraycopy(bytes, 0, iv, 0, iv.length);
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher decryptCipher = Cipher.getInstance(CIPHER_CODE);
decryptCipher.init(Cipher.DECRYPT_MODE, sk, ivspec);
byte[] encBytes = new byte[bytes.length - 16];
System.arraycopy(bytes, 16, encBytes, 0, encBytes.length);
byte[] plaindata = decryptCipher.doFinal(encBytes);
for (byte cur : plaindata) {
// Values < 0 cause the conversion to text to fail.
if (cur < 0 || cur > Byte.MAX_VALUE) {
throw new InvalidKeyException("Invalid characters in decrypted value");
}
}
return new String(plaindata, utf8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | IllegalBlockSizeException
| BadPaddingException nsae) {
throw new InvalidKeyException(nsae);
}
}
// --------------------------------------------------------- Private Methods
private void setupKeyAndCharset() {
try {
InitialContext context = new InitialContext();
String encodedKeyArray = (String) context.lookup("java:comp/env/faces/FlashSecretKey");
if (null != encodedKeyArray) {
byte[] keyArray = Base64.getDecoder().decode(encodedKeyArray);
if (keyArray.length < 16) {
throw new FacesException("key must be at least 16 bytes long.");
}
sk = new SecretKeySpec(keyArray, KEY_ALGORITHM);
}
} catch (NamingException exception) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Unable to find the encoded key.", exception);
}
} catch (FacesException e) {
throw new FacesException(e);
}
if (null == sk) {
try {
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
kg.init(KEY_LENGTH); // 256 if you're using the Unlimited Policy Files
sk = kg.generateKey();
} catch (Exception e) {
throw new FacesException(e);
}
}
SortedMap availableCharsets = Charset.availableCharsets();
if (availableCharsets.containsKey("UTF-8")) {
utf8 = availableCharsets.get("UTF-8");
} else if (availableCharsets.containsKey("UTF8")) {
utf8 = availableCharsets.get("UTF8");
} else {
throw new FacesException("Unable to get UTF-8 Charset.");
}
}
/**
* This method concatenates two byte arrays
*
* @return a byte array of array1||array2
* @param array1 first byte array to be concatenated
* @param array2 second byte array to be concatenated
*/
private static byte[] concatBytes(byte[] array1, byte[] array2) {
byte[] cBytes = new byte[array1.length + array2.length];
try {
System.arraycopy(array1, 0, cBytes, 0, array1.length);
System.arraycopy(array2, 0, cBytes, array1.length, array2.length);
} catch (Exception e) {
throw new FacesException(e);
}
return cBytes;
}
}