147 lines
7.2 KiB
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;
|
|
}
|
|
}
|