feat: v0.6.0, add age support
This commit is contained in:
15
README.md
15
README.md
@@ -2,6 +2,21 @@
|
||||
|
||||
Tiny encrypt implemented by Java
|
||||
|
||||
Config `~/.tinyencrypt_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"ageRecipient": "age1***",
|
||||
"cardCli": "/Users/hatterjiang/.cargo/bin/card-cli",
|
||||
"pgpEncryptPublicKeyPem": "-----BEGIN PUBLIC KEY-----\n***\n-----END PUBLIC KEY-----",
|
||||
"turnOffEnvelop": false,
|
||||
"defaultKeyName": "prod_ec_key",
|
||||
"localPrivateKeyPemChallenge": "***",
|
||||
"localPrivateKeyPemEncrypted": "***",
|
||||
"localPublicKeyPem": "-----BEGIN PUBLIC KEY-----\n***\n-----END PUBLIC KEY-----"
|
||||
}
|
||||
```
|
||||
|
||||
Debug logging:
|
||||
|
||||
```shell
|
||||
|
||||
@@ -12,22 +12,24 @@ File format:
|
||||
|
||||
Meta format:
|
||||
|
||||
| Field | Type | Comment |
|
||||
|---------|-----------|----------------------------------------------|
|
||||
| version | String | Constant value: `1.1` |
|
||||
| created | Long | Created time, Unix Epoch |
|
||||
| userAgent | String | User Agent, e.g. `TinyEncrypt v0.5.1@MacOS` |
|
||||
| comment | String | `optional` Plain text comment |
|
||||
| Field | Type | Comment |
|
||||
|------------------|-----------|----------------------------------------------|
|
||||
| version | String | Constant value: `1.1` |
|
||||
| created | Long | Created time, Unix Epoch |
|
||||
| userAgent | String | User Agent, e.g. `TinyEncrypt v0.5.1@MacOS` |
|
||||
| comment | String | `optional` Plain text comment |
|
||||
| encryptedComment | String | `optional` Encrypted comment |
|
||||
| encryptedMeta | String | `optional` Encrypted Meta Data |
|
||||
| pgpEnvelop | String | `deprecated` PGP Publickey Encrypted DataKey |
|
||||
| pgpFingerprint | String | `deprecated` Hex(Sha256(PGP Publickey)) |
|
||||
| envelop | String | `deprecated` KMS Encrypted DataKey |
|
||||
| envelops | Envelop[] | Envelop Array |
|
||||
| nonce | String | `base64` GCM Nonce |
|
||||
| fileLength | Long | File Length |
|
||||
| encryptedMeta | String | `optional` Encrypted Meta Data |
|
||||
| pgpEnvelop | String | `deprecated` PGP Publickey Encrypted DataKey |
|
||||
| pgpFingerprint | String | `deprecated` Hex(Sha256(PGP Publickey)) |
|
||||
| ageEnvelop | String | `deprecated` PGP Publickey Encrypted DataKey |
|
||||
| ageRecipient | String | `deprecated` Hex(Sha256(PGP Publickey)) |
|
||||
| envelop | String | `deprecated` KMS Encrypted DataKey |
|
||||
| envelops | Envelop[] | Envelop Array |
|
||||
| nonce | String | `base64` GCM Nonce |
|
||||
| fileLength | Long | File Length |
|
||||
| fileLastModified | Long | File Last Modified, Unix Epoch |
|
||||
| compress | Boolean | Compressed or Not |
|
||||
| compress | Boolean | Compressed or Not |
|
||||
|
||||
Envelop format:
|
||||
|
||||
|
||||
@@ -5,7 +5,16 @@ import picocli.CommandLine;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@CommandLine.Command(name = "tiny-encrypt", version = "tiny-encrypt v" + TinyEncryptConstant.VERSION)
|
||||
@CommandLine.Command(name = "tiny-encrypt", version = "tiny-encrypt v" + TinyEncryptConstant.VERSION, description = "\n" +
|
||||
"Samples:\n" +
|
||||
"Encrypt file: tiny-encrypt -e test.txt\n" +
|
||||
"Decrypt file: tiny-encrypt -d test.txt.tinyenc [--pgp|--age]\n" +
|
||||
"Decrypt and show file: tiny-encrypt -d --show test.txt.tinyenc [--pgp|--age]\n" +
|
||||
"Decrypt and edit file: tiny-encrypt -d --edit test.txt.tinyenc [--pgp|--age]\n" +
|
||||
"Show *.tinyenc file info: tiny-encrypt -I test.txt.tinyenc\n" +
|
||||
"Create encrypted file: tiny-encrypt --create test.txt\n" +
|
||||
"\n" +
|
||||
"Argument details:")
|
||||
public class TinyEncryptArgs {
|
||||
@CommandLine.Option(names = {"-e", "--encrypt"}, description = "Encrypt file")
|
||||
boolean encrypt = false;
|
||||
@@ -40,6 +49,9 @@ public class TinyEncryptArgs {
|
||||
@CommandLine.Option(names = {"--skip-envelop"}, description = "Skip envelop data key")
|
||||
boolean skipEnvelop = false;
|
||||
|
||||
@CommandLine.Option(names = {"--turn-on-envelop"}, description = "Turn on envelop data key")
|
||||
boolean turnOnEnvelop = false;
|
||||
|
||||
@CommandLine.Option(names = {"--require-sign"}, description = "Require signature when create data key")
|
||||
boolean requireSign = false;
|
||||
|
||||
@@ -55,6 +67,9 @@ public class TinyEncryptArgs {
|
||||
@CommandLine.Option(names = {"-P", "--pgp"}, description = "Decrypt use PGP")
|
||||
boolean pgp = false;
|
||||
|
||||
@CommandLine.Option(names = {"-A", "--age"}, description = "Decrypt use Age")
|
||||
boolean age = false;
|
||||
|
||||
@CommandLine.Option(names = {"--use-jce"}, description = "Use JCE")
|
||||
boolean useJce = false;
|
||||
|
||||
|
||||
@@ -82,8 +82,10 @@ public class TinyEncryptMain {
|
||||
}
|
||||
final boolean encryptOrDecryptSuccess;
|
||||
if (tinyEncryptArgs.encrypt) {
|
||||
final boolean turnOffEnvelop = config.getTurnOffEnvelop() != null && config.getTurnOffEnvelop();
|
||||
final boolean useEnvelop = tinyEncryptArgs.turnOnEnvelop || (!turnOffEnvelop && !tinyEncryptArgs.skipEnvelop);
|
||||
encryptOrDecryptSuccess = EncryptedFileUtil.encryptFile(config, tinyEncryptArgs.key, f,
|
||||
tinyEncryptArgs.compress, !tinyEncryptArgs.skipEnvelop, tinyEncryptArgs.requireSign,
|
||||
tinyEncryptArgs.compress, useEnvelop, tinyEncryptArgs.requireSign,
|
||||
tinyEncryptArgs.comment, tinyEncryptArgs.encryptedComment);
|
||||
} else {
|
||||
if (tinyEncryptArgs.showInWindow || tinyEncryptArgs.editInWindow) {
|
||||
|
||||
@@ -138,10 +138,12 @@ public class TinyEncryptMainUtil {
|
||||
.editable(true)
|
||||
.show().getResult();
|
||||
|
||||
final boolean turnOffEnvelop = config.getTurnOffEnvelop() != null && config.getTurnOffEnvelop();
|
||||
final boolean useEnvelop = tinyEncryptArgs.turnOnEnvelop || (!turnOffEnvelop && !tinyEncryptArgs.skipEnvelop);
|
||||
final byte[] bytes = editResult.getBytes(StandardCharsets.UTF_8);
|
||||
final TinyEncryptMeta meta = TinyEncryptMetaUtil.create(config, tinyEncryptArgs.key,
|
||||
tinyEncryptArgs.comment, tinyEncryptArgs.encryptedComment,
|
||||
!tinyEncryptArgs.skipEnvelop, tinyEncryptArgs.requireSign);
|
||||
useEnvelop, tinyEncryptArgs.requireSign);
|
||||
meta.setFileLength((long) bytes.length);
|
||||
meta.setCreated(System.currentTimeMillis());
|
||||
meta.setFileLastModified(System.currentTimeMillis());
|
||||
@@ -183,10 +185,14 @@ public class TinyEncryptMainUtil {
|
||||
sb.append(header("Enc file created")).append(new Date(meta.getCreated())).append("\n");
|
||||
sb.append(header("Envelops")).append("KMS: ").append(toYesOrNo(meta.getEnvelop()))
|
||||
.append(", PGP: ").append(toYesOrNo(meta.getPgpEnvelop()))
|
||||
.append(", Age: ").append(toYesOrNo(meta.getAgeEnvelop()))
|
||||
.append("\n");
|
||||
if (StringUtil.isNotBlank(meta.getPgpFingerprint())) {
|
||||
sb.append(header("PGP fingerprint")).append(meta.getPgpFingerprint()).append("\n");
|
||||
}
|
||||
if (StringUtil.isNotBlank(meta.getAgeRecipient())) {
|
||||
sb.append(header("Age recipient")).append(meta.getAgeRecipient()).append("\n");
|
||||
}
|
||||
if (StringUtil.isNotBlank(meta.getComment())) {
|
||||
sb.append(header("Comment")).append(meta.getComment()).append("\n");
|
||||
}
|
||||
|
||||
@@ -8,10 +8,14 @@ public class TinyEncryptConfig {
|
||||
private String localPrivateKeyPem;
|
||||
private String localPrivateKeyPemEncrypted;
|
||||
private String localPrivateKeyPemChallenge;
|
||||
private Boolean turnOffEnvelop;
|
||||
private String pgpEncryptPublicKeyPem;
|
||||
@Deprecated
|
||||
private String pgpDecryptCmd;
|
||||
private String cardCli;
|
||||
private String ageRecipient;
|
||||
// Optional, default "age" when `ageRecipient` presents
|
||||
private String ageCli;
|
||||
|
||||
public String getDefaultKeyName() {
|
||||
return defaultKeyName;
|
||||
@@ -53,6 +57,14 @@ public class TinyEncryptConfig {
|
||||
this.localPrivateKeyPemChallenge = localPrivateKeyPemChallenge;
|
||||
}
|
||||
|
||||
public Boolean getTurnOffEnvelop() {
|
||||
return turnOffEnvelop;
|
||||
}
|
||||
|
||||
public void setTurnOffEnvelop(Boolean turnOffEnvelop) {
|
||||
this.turnOffEnvelop = turnOffEnvelop;
|
||||
}
|
||||
|
||||
public String getPgpEncryptPublicKeyPem() {
|
||||
return pgpEncryptPublicKeyPem;
|
||||
}
|
||||
@@ -76,4 +88,20 @@ public class TinyEncryptConfig {
|
||||
public void setCardCli(String cardCli) {
|
||||
this.cardCli = cardCli;
|
||||
}
|
||||
|
||||
public String getAgeRecipient() {
|
||||
return ageRecipient;
|
||||
}
|
||||
|
||||
public void setAgeRecipient(String ageRecipient) {
|
||||
this.ageRecipient = ageRecipient;
|
||||
}
|
||||
|
||||
public String getAgeCli() {
|
||||
return ageCli;
|
||||
}
|
||||
|
||||
public void setAgeCli(String ageCli) {
|
||||
this.ageCli = ageCli;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.hatter.tools.tinyencrypt.config;
|
||||
|
||||
public class TinyEncryptConstant {
|
||||
public static final String VERSION = "0.5.3";
|
||||
public static final String VERSION = "0.6.0";
|
||||
|
||||
public static final String ENC_FILE_EXT = ".tinyenc";
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ public class TinyEncryptMeta {
|
||||
private String encryptedComment;
|
||||
private String pgpEnvelop;
|
||||
private String pgpFingerprint;
|
||||
private String ageEnvelop;
|
||||
private String ageRecipient;
|
||||
private String envelop;
|
||||
@JSONField(serialize = false)
|
||||
private byte[] dataKey;
|
||||
@@ -74,6 +76,22 @@ public class TinyEncryptMeta {
|
||||
this.pgpFingerprint = pgpFingerprint;
|
||||
}
|
||||
|
||||
public String getAgeEnvelop() {
|
||||
return ageEnvelop;
|
||||
}
|
||||
|
||||
public void setAgeEnvelop(String ageEnvelop) {
|
||||
this.ageEnvelop = ageEnvelop;
|
||||
}
|
||||
|
||||
public String getAgeRecipient() {
|
||||
return ageRecipient;
|
||||
}
|
||||
|
||||
public void setAgeRecipient(String ageRecipient) {
|
||||
this.ageRecipient = ageRecipient;
|
||||
}
|
||||
|
||||
public String getEnvelop() {
|
||||
return envelop;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ 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.AgeCliUtil;
|
||||
import me.hatter.tools.tinyencrypt.util.CardCliUtil;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
@@ -131,6 +132,12 @@ public class TinyEncryptMetaUtil {
|
||||
log.warn("PGP encrypt public key is not RSAPublicKey: " + pgpEncryptPublicKey.getClass());
|
||||
}
|
||||
}
|
||||
if (StringUtil.isNotBlank(config.getAgeRecipient())) {
|
||||
final String ageCli = StringUtil.def(config.getAgeCli(), "age");
|
||||
final String ageEnvelop = AgeCliUtil.encryptBytes(ageCli, config.getAgeRecipient(), dataKey);
|
||||
tinyEncryptMeta.setAgeEnvelop(ageEnvelop.trim());
|
||||
tinyEncryptMeta.setAgeRecipient(config.getAgeRecipient());
|
||||
}
|
||||
tinyEncryptMeta.setNonce(RandomTool.secureRandom().nextbytes(12));
|
||||
tinyEncryptMeta.setUserAgent("TinyEncrypt v" + TinyEncryptConstant.VERSION + "@" + OSUtil.getCurrentOS().name());
|
||||
tinyEncryptMeta.setComment(comment);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package me.hatter.tools.tinyencrypt.util;
|
||||
|
||||
import me.hatter.tools.commons.assertion.AssertUtil;
|
||||
import me.hatter.tools.commons.misc.Base64s;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AgeCliUtil {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(
|
||||
encryptBytes(
|
||||
"age",
|
||||
"age1yubikey1qtwna67eqmyu7q9s3mpf7lkkrqzdrnqazdfdjftmv2qercy0cdchc7jcpu5",
|
||||
"hello world".getBytes(StandardCharsets.UTF_8)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static String encryptBytes(String ageCli, String ageRecipient, byte[] bytes) {
|
||||
AssertUtil.notEmpty(ageCli, "Age-cli cannot be empty");
|
||||
AssertUtil.notEmpty(ageRecipient, "Age-recipient cannot be empty");
|
||||
AssertUtil.notNull(bytes, "Bytes cannot null");
|
||||
final ProcessBuilder pb = new ProcessBuilder(
|
||||
"sh",
|
||||
"-c", "echo " + Base64s.normal().encode(bytes) + " | " + ageCli + " -e -r " + ageRecipient + " -a");
|
||||
final Optional<String> outputsOpt = CardCliUtil.runProcess(pb);
|
||||
if (!outputsOpt.isPresent()) {
|
||||
throw new RuntimeException("Encrypt use age failed!");
|
||||
}
|
||||
return outputsOpt.get();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user