feat: add pgp encrypt support
This commit is contained in:
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user