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") @CommandLine.Option(names = {"-I", "--info"}, description = "Encrypt file info")
boolean fileInfo = false; boolean fileInfo = false;
@CommandLine.Option(names = {"-P", "--pgp"}, description = "Decrypt use PGP")
boolean pgp = false;
@CommandLine.Parameters(paramLabel = "FILE", description = "Encrypt or Decrypt files") @CommandLine.Parameters(paramLabel = "FILE", description = "Encrypt or Decrypt files")
File[] files; File[] files;

View File

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

View File

@@ -4,6 +4,8 @@ public class TinyEncryptConfig {
private String defaultKeyName; private String defaultKeyName;
private String localPublicKeyPem; private String localPublicKeyPem;
private String localPrivateKeyPem; private String localPrivateKeyPem;
private String pgpEncryptPublicKeyPem;
private String pgpDecryptCmd;
public String getDefaultKeyName() { public String getDefaultKeyName() {
return defaultKeyName; return defaultKeyName;
@@ -28,4 +30,20 @@ public class TinyEncryptConfig {
public void setLocalPrivateKeyPem(String localPrivateKeyPem) { public void setLocalPrivateKeyPem(String localPrivateKeyPem) {
this.localPrivateKeyPem = 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; package me.hatter.tools.tinyencrypt.config;
public class TinyEncryptConstant { 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"; public static final String ENC_FILE_EXT = ".tinyenc";
} }

View File

@@ -1,15 +1,19 @@
package me.hatter.tools.tinyencrypt.encrypt; 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.assertion.AssertUtil;
import me.hatter.tools.commons.bytes.Bytes; import me.hatter.tools.commons.bytes.Bytes;
import me.hatter.tools.commons.io.DefaultRollCounter; import me.hatter.tools.commons.io.DefaultRollCounter;
import me.hatter.tools.commons.io.DigestOutputStream; import me.hatter.tools.commons.io.DigestOutputStream;
import me.hatter.tools.commons.io.IOUtil; 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.LogTool;
import me.hatter.tools.commons.log.LogTools; import me.hatter.tools.commons.log.LogTools;
import me.hatter.tools.commons.security.crypt.CryptInputStream; import me.hatter.tools.commons.security.crypt.CryptInputStream;
import me.hatter.tools.commons.security.crypt.CryptOutputStream; import me.hatter.tools.commons.security.crypt.CryptOutputStream;
import me.hatter.tools.commons.security.digest.Digests; 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.Tlv;
import me.hatter.tools.commons.tlv.TlvUtil; import me.hatter.tools.commons.tlv.TlvUtil;
import me.hatter.tools.tinyencrypt.config.TinyEncryptConfig; import me.hatter.tools.tinyencrypt.config.TinyEncryptConfig;
@@ -25,7 +29,7 @@ import java.util.zip.GZIPOutputStream;
public class EncryptedFileUtil { public class EncryptedFileUtil {
private static final LogTool log = LogTools.getLogTool(EncryptedFileUtil.class); 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) { if (getDecryptFile(file) == null) {
log.warn("File is not tinyenc file, skip: " + file); log.warn("File is not tinyenc file, skip: " + file);
return false; return false;
@@ -34,7 +38,50 @@ public class EncryptedFileUtil {
try (FileInputStream fis = new FileInputStream(file)) { try (FileInputStream fis = new FileInputStream(file)) {
Tlv tlv = TlvUtil.readTlv(fis); Tlv tlv = TlvUtil.readTlv(fis);
TinyEncryptMeta meta = tlv.getValueAsBytes().asJSONObject(TinyEncryptMeta.class); 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); meta.setDataKey(dataKey);
try (InputStream newIs = getDecryptInputStream(fis, meta)) { try (InputStream newIs = getDecryptInputStream(fis, meta)) {
boolean isCompressed = (meta.getCompress() != null) && meta.getCompress(); 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()); DigestOutputStream outputStream = new DigestOutputStream(new NilOutputStream(), Digests.sha256());
if (!decryptToOutputStream(config, file, outputStream)) { if (!decryptToOutputStream(config, file, outputStream, pgp)) {
return null; return null;
} }
return outputStream.digest(); 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(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
decryptToOutputStream(config, file, baos); decryptToOutputStream(config, file, baos, pgp);
SwingWindow.create("Decrypted file: " + file.getName()) SwingWindow.create("Decrypted file: " + file.getName())
.message("File: " + file) .message("File: " + file)
.text(new String(baos.toByteArray(), StandardCharsets.UTF_8)) .text(new String(baos.toByteArray(), StandardCharsets.UTF_8))
.show().getResult(); .show().getResult();
} }
public static boolean decryptFile(TinyEncryptConfig config, File file) { public static boolean decryptFile(TinyEncryptConfig config, File file, boolean pgp) {
File decFile = getDecryptFile(file); File decFile = getDecryptFile(file);
if (decFile == null) { if (decFile == null) {
log.warn("File is not tinyenc file, skip: " + decFile); log.warn("File is not tinyenc file, skip: " + decFile);
@@ -84,10 +131,20 @@ public class EncryptedFileUtil {
return false; return false;
} }
try { try {
boolean decryptResult;
try (FileOutputStream fos = new FileOutputStream(decFile)) { 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) { } catch (Exception e) {
if (decFile.length() == 0) {
decFile.delete();
}
log.error("Decrypt file filed: " + file + ", reason: " + e.getMessage()); log.error("Decrypt file filed: " + file + ", reason: " + e.getMessage());
log.debug("Decrypt file filed: " + file + ", reason: " + e.getMessage(), e); log.debug("Decrypt file filed: " + file + ", reason: " + e.getMessage(), e);
return false; return false;

View File

@@ -7,6 +7,7 @@ public class TinyEncryptMeta {
private long created; private long created;
private String userAgent; private String userAgent;
private String comment; private String comment;
private String pgpEnvelop;
private String envelop; private String envelop;
@JSONField(serialize = false) @JSONField(serialize = false)
private byte[] dataKey; private byte[] dataKey;
@@ -47,6 +48,14 @@ public class TinyEncryptMeta {
this.comment = comment; this.comment = comment;
} }
public String getPgpEnvelop() {
return pgpEnvelop;
}
public void setPgpEnvelop(String pgpEnvelop) {
this.pgpEnvelop = pgpEnvelop;
}
public String getEnvelop() { public String getEnvelop() {
return envelop; 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.os.OSUtil;
import me.hatter.tools.commons.security.key.KeyUtil; import me.hatter.tools.commons.security.key.KeyUtil;
import me.hatter.tools.commons.security.random.RandomTool; 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.security.sign.Signatures;
import me.hatter.tools.commons.string.StringUtil;
import me.hatter.tools.tinyencrypt.config.TinyEncryptConfig; import me.hatter.tools.tinyencrypt.config.TinyEncryptConfig;
import me.hatter.tools.tinyencrypt.config.TinyEncryptConstant; import me.hatter.tools.tinyencrypt.config.TinyEncryptConstant;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.List; import java.util.List;
@@ -61,6 +64,10 @@ public class TinyEncryptMetaUtil {
public static TinyEncryptMeta create(TinyEncryptConfig config, String comment) { public static TinyEncryptMeta create(TinyEncryptConfig config, String comment) {
PublicKey publicKey = KeyUtil.parsePublicKeyPEM(config.getLocalPublicKeyPem()); PublicKey publicKey = KeyUtil.parsePublicKeyPEM(config.getLocalPublicKeyPem());
PrivateKey privateKey = KeyUtil.parsePrivateKeyPEM(config.getLocalPrivateKeyPem()); PrivateKey privateKey = KeyUtil.parsePrivateKeyPEM(config.getLocalPrivateKeyPem());
PublicKey pgpEncryptPublicKey = null;
if (StringUtil.isNotBlank(config.getPgpEncryptPublicKeyPem())) {
pgpEncryptPublicKey = KeyUtil.parsePublicKeyPEM(config.getPgpEncryptPublicKeyPem());
}
String name = config.getDefaultKeyName(); String name = config.getDefaultKeyName();
String timestamp = String.valueOf(System.currentTimeMillis()); String timestamp = String.valueOf(System.currentTimeMillis());
@@ -90,6 +97,14 @@ public class TinyEncryptMetaUtil {
tinyEncryptMeta.setCreated(System.currentTimeMillis()); tinyEncryptMeta.setCreated(System.currentTimeMillis());
tinyEncryptMeta.setDataKey(dataKey); tinyEncryptMeta.setDataKey(dataKey);
tinyEncryptMeta.setEnvelop(envelop); 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.setNonce(RandomTool.secureRandom().nextbytes(12));
tinyEncryptMeta.setUserAgent("TinyEncrypt v" + TinyEncryptConstant.VERSION + "@" + OSUtil.getCurrentOS().name()); tinyEncryptMeta.setUserAgent("TinyEncrypt v" + TinyEncryptConstant.VERSION + "@" + OSUtil.getCurrentOS().name());
tinyEncryptMeta.setComment(comment); tinyEncryptMeta.setComment(comment);