EncryptedPrivateKeyResolver.java
/*
* @copyright defined in LICENSE.txt
*/
package hera.spec.resolver;
import static hera.util.CryptoUtils.encryptToAesGcm;
import hera.annotation.ApiAudience;
import hera.annotation.ApiStability;
import hera.api.model.BytesValue;
import hera.api.model.EncryptedPrivateKey;
import hera.util.BytesValueUtils;
import hera.util.CryptoUtils;
import hera.util.Sha256Utils;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import org.bouncycastle.crypto.InvalidCipherTextException;
@ApiAudience.Private
@ApiStability.Unstable
public class EncryptedPrivateKeyResolver {
protected static final String CHAR_SET = "UTF-8";
/**
* Encrypt private key with a {@code password}.
*
* @param privateKeyBytes a raw private key
* @param password a password to encrypt
* @return encrypted private key
* @throws InvalidCipherTextException on encryption failure
* @throws UnsupportedEncodingException on password encoding failure
*/
public static EncryptedPrivateKey encrypt(final BytesValue privateKeyBytes, final String password)
throws InvalidCipherTextException, UnsupportedEncodingException {
final byte[] rawPassword = password.getBytes(CHAR_SET);
final byte[] hashedPassword = Sha256Utils.digest(rawPassword);
final byte[] encryptKey = Sha256Utils.digest(rawPassword, hashedPassword);
final byte[] nonce = calculateNonce(hashedPassword);
final byte[] encrypted = encryptToAesGcm(privateKeyBytes.getValue(), encryptKey, nonce);
final BytesValue encryptedBytesValue =
new BytesValue(BytesValueUtils.append(encrypted, EncryptedPrivateKeySpec.PREFIX));
return new EncryptedPrivateKey(encryptedBytesValue);
}
/**
* Decrypt encrypted private key with a {@code password}.
*
* @param encryptedPrivateKey an encrypted private key
* @param password a password to decrypt
* @return a decrypted private key
* @throws InvalidCipherTextException on decryption failure
* @throws UnsupportedEncodingException on password encoding failure
*/
public static BytesValue decrypt(final EncryptedPrivateKey encryptedPrivateKey,
final String password)
throws InvalidCipherTextException, UnsupportedEncodingException {
final byte[] rawEncrypted = encryptedPrivateKey.getBytesValue().getValue();
final byte[] rawPassword = password.getBytes(CHAR_SET);
final byte[] withoutVersion = BytesValueUtils.trimPrefix(rawEncrypted);
final byte[] hashedPassword = Sha256Utils.digest(rawPassword);
final byte[] decryptKey = Sha256Utils.digest(rawPassword, hashedPassword);
final byte[] nonce = calculateNonce(hashedPassword);
final byte[] rawPrivateKey = CryptoUtils.decryptFromAesGcm(withoutVersion, decryptKey, nonce);
return new BytesValue(rawPrivateKey);
}
protected static byte[] calculateNonce(final byte[] hashedPassword) {
return Arrays.copyOfRange(hashedPassword, 4, 16);
}
}