Files
tiny-encrypt-java/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMetaUtil.java

147 lines
7.2 KiB
Java

package me.hatter.tools.tinyencrypt.encrypt;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import me.hatter.tools.commons.bytes.Bytes;
import me.hatter.tools.commons.exception.JumpOutException;
import me.hatter.tools.commons.log.LogTool;
import me.hatter.tools.commons.log.LogTools;
import me.hatter.tools.commons.network.HttpRequest;
import me.hatter.tools.commons.os.OSUtil;
import me.hatter.tools.commons.security.crypt.AESCryptTool;
import me.hatter.tools.commons.security.digest.Digests;
import me.hatter.tools.commons.security.key.KeyUtil;
import me.hatter.tools.commons.security.random.RandomTool;
import me.hatter.tools.commons.security.rsa.RSAUtil;
import me.hatter.tools.commons.security.sign.Signatures;
import me.hatter.tools.commons.string.StringUtil;
import me.hatter.tools.tinyencrypt.config.TinyEncryptConfig;
import me.hatter.tools.tinyencrypt.config.TinyEncryptConstant;
import me.hatter.tools.tinyencrypt.util.CardCliUtil;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
public class TinyEncryptMetaUtil {
private static final LogTool log = LogTools.getLogTool(TinyEncryptMetaUtil.class);
private static final String KMS_GET_DATA_KEY = "https://hatter.ink/kms/get_data_key.json";
private static final String KMS_DECRYPT_DATA_KEY = "https://hatter.ink/kms/decrypt_data_key_with_sign.json";
public static String toString(TinyEncryptMeta tinyEncryptMeta) {
return JSON.toJSONString(tinyEncryptMeta);
}
public static TinyEncryptMeta parse(String meta) {
return JSON.parseObject(meta, TinyEncryptMeta.class);
}
public static void requireLocalPrivateKeyPem(TinyEncryptConfig config) {
if (StringUtil.isNotEmpty(config.getLocalPrivateKeyPem())) {
return;
}
if (StringUtil.isEmpty(config.getLocalPrivateKeyPemChallenge())
|| StringUtil.isEmpty(config.getLocalPrivateKeyPemEncrypted())) {
throw new JumpOutException("Cannot prepare local private key pem!");
}
Optional<byte[]> keyOpt = CardCliUtil.getChall(config.getCardCli(), config.getLocalPrivateKeyPemChallenge());
if (!keyOpt.isPresent()) {
throw new JumpOutException();
}
byte[] key = keyOpt.get();
String localPrivateKeyPem = AESCryptTool.gcmDecrypt(key)
.from(Bytes.fromBase64(config.getLocalPrivateKeyPemEncrypted())).toBytes().string();
log.info("Decrypt local private key success!");
config.setLocalPrivateKeyPem(localPrivateKeyPem);
}
public static byte[] decryptDataKey(TinyEncryptConfig config, TinyEncryptMeta meta) {
requireLocalPrivateKeyPem(config);
PrivateKey privateKey = KeyUtil.parsePrivateKeyPEM(config.getLocalPrivateKeyPem());
String envelop = meta.getEnvelop();
String timestamp = String.valueOf(System.currentTimeMillis());
String toBeSigned = envelop + "|" + timestamp;
Bytes sign = Signatures.sha256(privateKey).sign(toBeSigned);
List<HttpRequest.KeyValue> keyValues = new ArrayList<>();
keyValues.add(new HttpRequest.KeyValue("envelop", envelop));
keyValues.add(new HttpRequest.KeyValue("timestamp", timestamp));
keyValues.add(new HttpRequest.KeyValue("signature", sign.asBase64()));
log.info("Decrypt data key ...");
Bytes response = HttpRequest.fromUrl(KMS_DECRYPT_DATA_KEY).post(keyValues);
JSONObject responseObject = response.asJSON();
if (responseObject.getIntValue("status") != 200) {
throw new JumpOutException("Get data key from kms error, status: "
+ responseObject.getIntValue("status")
+ ", detail: " + responseObject
);
}
JSONObject responseData = responseObject.getJSONObject("data");
return Base64.getDecoder().decode(responseData.getString("dataKey"));
}
public static TinyEncryptMeta create(TinyEncryptConfig config, String comment, boolean requireSignature) {
PrivateKey privateKey = null;
if (requireSignature) {
requireLocalPrivateKeyPem(config);
privateKey = KeyUtil.parsePrivateKeyPEM(config.getLocalPrivateKeyPem());
}
PublicKey publicKey = KeyUtil.parsePublicKeyPEM(config.getLocalPublicKeyPem());
PublicKey pgpEncryptPublicKey = null;
if (StringUtil.isNotBlank(config.getPgpEncryptPublicKeyPem())) {
pgpEncryptPublicKey = KeyUtil.parsePublicKeyPEM(config.getPgpEncryptPublicKeyPem());
}
String name = config.getDefaultKeyName();
String timestamp = String.valueOf(System.currentTimeMillis());
String toBeSigned = name + "|" + timestamp;
Bytes sign = (privateKey == null) ? null : Signatures.sha256(privateKey).sign(toBeSigned);
List<HttpRequest.KeyValue> keyValues = new ArrayList<>();
keyValues.add(new HttpRequest.KeyValue("name", name));
keyValues.add(new HttpRequest.KeyValue("timestamp", timestamp));
keyValues.add(new HttpRequest.KeyValue("dataKeyPublicKey", KeyUtil.serializePublicKeyToPEM(publicKey)));
if (sign == null) {
keyValues.add(new HttpRequest.KeyValue("skipDataKeyRequestSignVerify", "true"));
} else {
keyValues.add(new HttpRequest.KeyValue("dataKeyRequestSign", sign.asBase64()));
}
log.info("Get data key from kms, key name: " + name + ", with sign: " + (sign != null) + " ...");
Bytes response = HttpRequest.fromUrl(KMS_GET_DATA_KEY).post(keyValues);
JSONObject responseObject = response.asJSON();
if (responseObject.getIntValue("status") != 200) {
throw new JumpOutException("Get data key from kms error, status: "
+ responseObject.getIntValue("status")
+ ", detail: " + responseObject
);
}
JSONObject responseData = responseObject.getJSONObject("data");
byte[] dataKey = Base64.getDecoder().decode(responseData.getString("dataKey"));
String envelop = responseData.getString("envelopJwe");
TinyEncryptMeta tinyEncryptMeta = new TinyEncryptMeta();
tinyEncryptMeta.setVersion("1.0");
tinyEncryptMeta.setCreated(System.currentTimeMillis());
tinyEncryptMeta.setDataKey(dataKey);
tinyEncryptMeta.setEnvelop(envelop);
if (pgpEncryptPublicKey != null) {
if (pgpEncryptPublicKey instanceof RSAPublicKey) {
byte[] pgpEnvelop = RSAUtil.encrypt((RSAPublicKey) pgpEncryptPublicKey, dataKey);
tinyEncryptMeta.setPgpEnvelop(Base64.getEncoder().encodeToString(pgpEnvelop));
tinyEncryptMeta.setPgpFingerprint(Digests.sha256().digest(pgpEncryptPublicKey.getEncoded()).asHex());
} else {
log.warn("PGP encrypt public key is not RSAPublicKey: " + pgpEncryptPublicKey.getClass());
}
}
tinyEncryptMeta.setNonce(RandomTool.secureRandom().nextbytes(12));
tinyEncryptMeta.setUserAgent("TinyEncrypt v" + TinyEncryptConstant.VERSION + "@" + OSUtil.getCurrentOS().name());
tinyEncryptMeta.setComment(comment);
return tinyEncryptMeta;
}
}