feat: add pgp encrypt support

This commit is contained in:
2021-07-18 15:20:37 +08:00
parent 96a93bb29d
commit ed11f541ba
7 changed files with 119 additions and 12 deletions

View File

@@ -37,6 +37,9 @@ public class TinyEncryptArgs {
@CommandLine.Option(names = {"-I", "--info"}, description = "Encrypt file info")
boolean fileInfo = false;
@CommandLine.Option(names = {"-P", "--pgp"}, description = "Decrypt use PGP")
boolean pgp = false;
@CommandLine.Parameters(paramLabel = "FILE", description = "Encrypt or Decrypt files")
File[] files;

View File

@@ -105,6 +105,11 @@ public class TinyEncryptMain {
sb.append("Enc file created: ")
.append(new Date(meta.getCreated()))
.append("\n");
if (StringUtil.isNotBlank(meta.getPgpEnvelop())) {
sb.append("PGP envelop: YES\n");
} else {
sb.append("PGP envelop: NO\n");
}
sb.append("Agent: ").append(meta.getUserAgent());
if (StringUtil.isNotBlank(meta.getComment())) {
sb.append("Comment: ").append(meta.getComment()).append("\n");
@@ -189,10 +194,10 @@ public class TinyEncryptMain {
result = EncryptedFileUtil.encryptFile(config, tinyEncryptArgs.key, f, tinyEncryptArgs.compress, tinyEncryptArgs.comment);
} else {
if (tinyEncryptArgs.showInWindow) {
EncryptedFileUtil.decryptInWindow(config, f);
EncryptedFileUtil.decryptInWindow(config, f, tinyEncryptArgs.pgp);
result = false; // do not delete file
} else if (tinyEncryptArgs.digest) {
Bytes sha256 = EncryptedFileUtil.decryptAndDigest(config, f);
Bytes sha256 = EncryptedFileUtil.decryptAndDigest(config, f, tinyEncryptArgs.pgp);
if (sha256 != null) {
log.info(sha256.asHex() + " - " + f);
File clearTextFile = EncryptedFileUtil.getDecryptFile(f);
@@ -208,7 +213,7 @@ public class TinyEncryptMain {
}
result = false; // do not delete file
} else {
result = EncryptedFileUtil.decryptFile(config, f);
result = EncryptedFileUtil.decryptFile(config, f, tinyEncryptArgs.pgp);
}
}
if (result && tinyEncryptArgs.removeFile) {

View File

@@ -4,6 +4,8 @@ public class TinyEncryptConfig {
private String defaultKeyName;
private String localPublicKeyPem;
private String localPrivateKeyPem;
private String pgpEncryptPublicKeyPem;
private String pgpDecryptCmd;
public String getDefaultKeyName() {
return defaultKeyName;
@@ -28,4 +30,20 @@ public class TinyEncryptConfig {
public void setLocalPrivateKeyPem(String localPrivateKeyPem) {
this.localPrivateKeyPem = localPrivateKeyPem;
}
public String getPgpEncryptPublicKeyPem() {
return pgpEncryptPublicKeyPem;
}
public void setPgpEncryptPublicKeyPem(String pgpEncryptPublicKeyPem) {
this.pgpEncryptPublicKeyPem = pgpEncryptPublicKeyPem;
}
public String getPgpDecryptCmd() {
return pgpDecryptCmd;
}
public void setPgpDecryptCmd(String pgpDecryptCmd) {
this.pgpDecryptCmd = pgpDecryptCmd;
}
}

View File

@@ -1,7 +1,7 @@
package me.hatter.tools.tinyencrypt.config;
public class TinyEncryptConstant {
public static final String VERSION = "0.3.6";
public static final String VERSION = "0.3.7";
public static final String ENC_FILE_EXT = ".tinyenc";
}

View File

@@ -1,15 +1,19 @@
package me.hatter.tools.tinyencrypt.encrypt;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import me.hatter.tools.commons.assertion.AssertUtil;
import me.hatter.tools.commons.bytes.Bytes;
import me.hatter.tools.commons.io.DefaultRollCounter;
import me.hatter.tools.commons.io.DigestOutputStream;
import me.hatter.tools.commons.io.IOUtil;
import me.hatter.tools.commons.io.RFile;
import me.hatter.tools.commons.log.LogTool;
import me.hatter.tools.commons.log.LogTools;
import me.hatter.tools.commons.security.crypt.CryptInputStream;
import me.hatter.tools.commons.security.crypt.CryptOutputStream;
import me.hatter.tools.commons.security.digest.Digests;
import me.hatter.tools.commons.string.StringUtil;
import me.hatter.tools.commons.tlv.Tlv;
import me.hatter.tools.commons.tlv.TlvUtil;
import me.hatter.tools.tinyencrypt.config.TinyEncryptConfig;
@@ -25,7 +29,7 @@ import java.util.zip.GZIPOutputStream;
public class EncryptedFileUtil {
private static final LogTool log = LogTools.getLogTool(EncryptedFileUtil.class);
public static boolean decryptToOutputStream(TinyEncryptConfig config, File file, OutputStream os) {
public static boolean decryptToOutputStream(TinyEncryptConfig config, File file, OutputStream os, boolean pgp) {
if (getDecryptFile(file) == null) {
log.warn("File is not tinyenc file, skip: " + file);
return false;
@@ -34,7 +38,50 @@ public class EncryptedFileUtil {
try (FileInputStream fis = new FileInputStream(file)) {
Tlv tlv = TlvUtil.readTlv(fis);
TinyEncryptMeta meta = tlv.getValueAsBytes().asJSONObject(TinyEncryptMeta.class);
byte[] dataKey = TinyEncryptMetaUtil.decryptDataKey(config, meta);
byte[] dataKey;
if (pgp) {
if (StringUtil.isBlank(meta.getPgpEnvelop())) {
log.error("File is not encrypted with PGP envelop");
return false;
}
if (StringUtil.isBlank(config.getPgpDecryptCmd())) {
log.error("PGP decrypt cmd is not configed");
return false;
}
if (RFile.from(config.getPgpDecryptCmd()).isNotFile()) {
log.error("PGP decrypt cmd is miss configed");
return false;
}
System.out.print("Input PGP PIN: ");
char[] pin = System.console().readPassword();
if (pin.length < 6) {
log.error("PIN must have 6 letters");
return false;
}
ProcessBuilder pb = new ProcessBuilder(
config.getPgpDecryptCmd(),
"pgp-card-decrypt",
"--cipher-base64", meta.getPgpEnvelop(),
"--pass", new String(pin),
"--json");
log.info("Start: " + config.getPgpDecryptCmd());
log.debug("Start process: " + pb.command());
Process p = pb.start();
p.waitFor();
try {
byte[] jsonBytes = IOUtil.readToBytes(p.getInputStream());
String jsonStr = new String(jsonBytes, StandardCharsets.UTF_8);
JSONObject jo = JSON.parseObject(jsonStr);
dataKey = Bytes.fromHex(jo.getString("text_hex")).bytes();
} catch (Exception e) {
log.error("Error in parse pgp-card-decrypt: " + e, e);
log.error("err out: " + Bytes.from(IOUtil.readToBytes(p.getErrorStream())));
return false;
}
} else {
dataKey = TinyEncryptMetaUtil.decryptDataKey(config, meta);
}
meta.setDataKey(dataKey);
try (InputStream newIs = getDecryptInputStream(fis, meta)) {
boolean isCompressed = (meta.getCompress() != null) && meta.getCompress();
@@ -56,24 +103,24 @@ public class EncryptedFileUtil {
}
}
public static Bytes decryptAndDigest(TinyEncryptConfig config, File file) {
public static Bytes decryptAndDigest(TinyEncryptConfig config, File file, boolean pgp) {
DigestOutputStream outputStream = new DigestOutputStream(new NilOutputStream(), Digests.sha256());
if (!decryptToOutputStream(config, file, outputStream)) {
if (!decryptToOutputStream(config, file, outputStream, pgp)) {
return null;
}
return outputStream.digest();
}
public static void decryptInWindow(TinyEncryptConfig config, File file) {
public static void decryptInWindow(TinyEncryptConfig config, File file, boolean pgp) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
decryptToOutputStream(config, file, baos);
decryptToOutputStream(config, file, baos, pgp);
SwingWindow.create("Decrypted file: " + file.getName())
.message("File: " + file)
.text(new String(baos.toByteArray(), StandardCharsets.UTF_8))
.show().getResult();
}
public static boolean decryptFile(TinyEncryptConfig config, File file) {
public static boolean decryptFile(TinyEncryptConfig config, File file, boolean pgp) {
File decFile = getDecryptFile(file);
if (decFile == null) {
log.warn("File is not tinyenc file, skip: " + decFile);
@@ -84,10 +131,20 @@ public class EncryptedFileUtil {
return false;
}
try {
boolean decryptResult;
try (FileOutputStream fos = new FileOutputStream(decFile)) {
return decryptToOutputStream(config, file, fos);
decryptResult = decryptToOutputStream(config, file, fos, pgp);
}
if (!decryptResult) {
if (decFile.length() == 0) {
decFile.delete();
}
}
return decryptResult;
} catch (Exception e) {
if (decFile.length() == 0) {
decFile.delete();
}
log.error("Decrypt file filed: " + file + ", reason: " + e.getMessage());
log.debug("Decrypt file filed: " + file + ", reason: " + e.getMessage(), e);
return false;

View File

@@ -7,6 +7,7 @@ public class TinyEncryptMeta {
private long created;
private String userAgent;
private String comment;
private String pgpEnvelop;
private String envelop;
@JSONField(serialize = false)
private byte[] dataKey;
@@ -47,6 +48,14 @@ public class TinyEncryptMeta {
this.comment = comment;
}
public String getPgpEnvelop() {
return pgpEnvelop;
}
public void setPgpEnvelop(String pgpEnvelop) {
this.pgpEnvelop = pgpEnvelop;
}
public String getEnvelop() {
return envelop;
}

View File

@@ -10,12 +10,15 @@ import me.hatter.tools.commons.network.HttpRequest;
import me.hatter.tools.commons.os.OSUtil;
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 java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
@@ -61,6 +64,10 @@ public class TinyEncryptMetaUtil {
public static TinyEncryptMeta create(TinyEncryptConfig config, String comment) {
PublicKey publicKey = KeyUtil.parsePublicKeyPEM(config.getLocalPublicKeyPem());
PrivateKey privateKey = KeyUtil.parsePrivateKeyPEM(config.getLocalPrivateKeyPem());
PublicKey pgpEncryptPublicKey = null;
if (StringUtil.isNotBlank(config.getPgpEncryptPublicKeyPem())) {
pgpEncryptPublicKey = KeyUtil.parsePublicKeyPEM(config.getPgpEncryptPublicKeyPem());
}
String name = config.getDefaultKeyName();
String timestamp = String.valueOf(System.currentTimeMillis());
@@ -90,6 +97,14 @@ public class TinyEncryptMetaUtil {
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));
} 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);