blob: c4ccabddf3b4d3dfeec6de8c7cb1d37ef69be651 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.geronimo.microprofile.extensions.config.converter.secure;
import static java.util.Locale.ROOT;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
// from plexus-cipher - reformatted for this project rules
class PBECipher {
private static final int SPICE_SIZE = 16;
private static final int SALT_SIZE = 8;
private static final int CHUNK_SIZE = 16;
private static final String DIGEST_ALG = "SHA-256";
private static final String KEY_ALG = "AES";
private static final String CIPHER_ALG = "AES/CBC/PKCS5Padding";
private final MessageDigest digester;
private final SecureRandom secureRandom;
PBECipher() {
try {
digester = MessageDigest.getInstance(DIGEST_ALG);
final boolean onLinux = System.getProperty("os.name", "blah").toLowerCase(ROOT).contains("linux");
if (!onLinux) {
secureRandom = new SecureRandom();
} else {
secureRandom = null;
}
} catch (final NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
}
private byte[] getSalt(final int numBytes) {
if (secureRandom != null) {
secureRandom.setSeed(System.currentTimeMillis());
return secureRandom.generateSeed(numBytes);
}
final byte[] res = new byte[numBytes];
new Random(System.currentTimeMillis()).nextBytes(res);
return res;
}
String encrypt64(final String clearText, final byte[] password) {
try {
final byte[] clearBytes = clearText.getBytes(StandardCharsets.UTF_8);
final byte[] salt = getSalt(SALT_SIZE);
if (secureRandom != null) {
new SecureRandom().nextBytes(salt);
}
final Cipher cipher = createCipher(password, salt, Cipher.ENCRYPT_MODE);
final byte[] encryptedBytes = cipher.doFinal(clearBytes);
final int len = encryptedBytes.length;
final byte padLen = (byte) (CHUNK_SIZE - (SALT_SIZE + len + 1) % CHUNK_SIZE);
final int totalLen = SALT_SIZE + len + padLen + 1;
final byte[] allEncryptedBytes = getSalt(totalLen);
System.arraycopy(salt, 0, allEncryptedBytes, 0, SALT_SIZE);
allEncryptedBytes[SALT_SIZE] = padLen;
System.arraycopy(encryptedBytes, 0, allEncryptedBytes, SALT_SIZE + 1, len);
final byte[] encryptedTextBytes = Base64.getEncoder().encode(allEncryptedBytes);
return new String(encryptedTextBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
String decrypt64(final String encryptedText, final byte[] password) {
try {
final byte[] allEncryptedBytes = Base64.getDecoder().decode(encryptedText.getBytes());
final int totalLen = allEncryptedBytes.length;
final byte[] salt = new byte[SALT_SIZE];
System.arraycopy(allEncryptedBytes, 0, salt, 0, SALT_SIZE);
final byte padLen = allEncryptedBytes[SALT_SIZE];
final byte[] encryptedBytes = new byte[totalLen - SALT_SIZE - 1 - padLen];
System.arraycopy(allEncryptedBytes, SALT_SIZE + 1, encryptedBytes, 0, encryptedBytes.length);
final Cipher cipher = createCipher(password, salt, Cipher.DECRYPT_MODE);
final byte[] clearBytes = cipher.doFinal(encryptedBytes);
return new String(clearBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
private Cipher createCipher(final byte[] pwdAsBytes, final byte[] inputSalt, final int mode)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException {
digester.reset();
final byte[] keyAndIv = new byte[SPICE_SIZE * 2];
final byte[] salt;
if (inputSalt == null || inputSalt.length == 0) {
salt = null; // no salt, likely bad
} else {
salt = inputSalt;
}
byte[] result;
int currentPos = 0;
while (currentPos < keyAndIv.length) {
digester.update(pwdAsBytes);
if (salt != null) {
digester.update(salt, 0, 8);
}
result = digester.digest();
final int stillNeed = keyAndIv.length - currentPos;
if (result.length > stillNeed) {
final byte[] b = new byte[stillNeed];
System.arraycopy(result, 0, b, 0, b.length);
result = b;
}
System.arraycopy(result, 0, keyAndIv, currentPos, result.length);
currentPos += result.length;
if (currentPos < keyAndIv.length) {
digester.reset();
digester.update(result);
}
}
final byte[] key = new byte[SPICE_SIZE];
final byte[] iv = new byte[SPICE_SIZE];
System.arraycopy(keyAndIv, 0, key, 0, key.length);
System.arraycopy(keyAndIv, key.length, iv, 0, iv.length);
final Cipher cipher = Cipher.getInstance(CIPHER_ALG);
cipher.init(mode, new SecretKeySpec(key, KEY_ALG), new IvParameterSpec(iv));
return cipher;
}
}