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
|
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:
|
Debug logging:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|||||||
@@ -12,22 +12,24 @@ File format:
|
|||||||
|
|
||||||
Meta format:
|
Meta format:
|
||||||
|
|
||||||
| Field | Type | Comment |
|
| Field | Type | Comment |
|
||||||
|---------|-----------|----------------------------------------------|
|
|------------------|-----------|----------------------------------------------|
|
||||||
| version | String | Constant value: `1.1` |
|
| version | String | Constant value: `1.1` |
|
||||||
| created | Long | Created time, Unix Epoch |
|
| created | Long | Created time, Unix Epoch |
|
||||||
| userAgent | String | User Agent, e.g. `TinyEncrypt v0.5.1@MacOS` |
|
| userAgent | String | User Agent, e.g. `TinyEncrypt v0.5.1@MacOS` |
|
||||||
| comment | String | `optional` Plain text comment |
|
| comment | String | `optional` Plain text comment |
|
||||||
| encryptedComment | String | `optional` Encrypted comment |
|
| encryptedComment | String | `optional` Encrypted comment |
|
||||||
| encryptedMeta | String | `optional` Encrypted Meta Data |
|
| encryptedMeta | String | `optional` Encrypted Meta Data |
|
||||||
| pgpEnvelop | String | `deprecated` PGP Publickey Encrypted DataKey |
|
| pgpEnvelop | String | `deprecated` PGP Publickey Encrypted DataKey |
|
||||||
| pgpFingerprint | String | `deprecated` Hex(Sha256(PGP Publickey)) |
|
| pgpFingerprint | String | `deprecated` Hex(Sha256(PGP Publickey)) |
|
||||||
| envelop | String | `deprecated` KMS Encrypted DataKey |
|
| ageEnvelop | String | `deprecated` PGP Publickey Encrypted DataKey |
|
||||||
| envelops | Envelop[] | Envelop Array |
|
| ageRecipient | String | `deprecated` Hex(Sha256(PGP Publickey)) |
|
||||||
| nonce | String | `base64` GCM Nonce |
|
| envelop | String | `deprecated` KMS Encrypted DataKey |
|
||||||
| fileLength | Long | File Length |
|
| envelops | Envelop[] | Envelop Array |
|
||||||
|
| nonce | String | `base64` GCM Nonce |
|
||||||
|
| fileLength | Long | File Length |
|
||||||
| fileLastModified | Long | File Last Modified, Unix Epoch |
|
| fileLastModified | Long | File Last Modified, Unix Epoch |
|
||||||
| compress | Boolean | Compressed or Not |
|
| compress | Boolean | Compressed or Not |
|
||||||
|
|
||||||
Envelop format:
|
Envelop format:
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,16 @@ import picocli.CommandLine;
|
|||||||
|
|
||||||
import java.io.File;
|
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 {
|
public class TinyEncryptArgs {
|
||||||
@CommandLine.Option(names = {"-e", "--encrypt"}, description = "Encrypt file")
|
@CommandLine.Option(names = {"-e", "--encrypt"}, description = "Encrypt file")
|
||||||
boolean encrypt = false;
|
boolean encrypt = false;
|
||||||
@@ -40,6 +49,9 @@ public class TinyEncryptArgs {
|
|||||||
@CommandLine.Option(names = {"--skip-envelop"}, description = "Skip envelop data key")
|
@CommandLine.Option(names = {"--skip-envelop"}, description = "Skip envelop data key")
|
||||||
boolean skipEnvelop = false;
|
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")
|
@CommandLine.Option(names = {"--require-sign"}, description = "Require signature when create data key")
|
||||||
boolean requireSign = false;
|
boolean requireSign = false;
|
||||||
|
|
||||||
@@ -55,6 +67,9 @@ public class TinyEncryptArgs {
|
|||||||
@CommandLine.Option(names = {"-P", "--pgp"}, description = "Decrypt use PGP")
|
@CommandLine.Option(names = {"-P", "--pgp"}, description = "Decrypt use PGP")
|
||||||
boolean pgp = false;
|
boolean pgp = false;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"-A", "--age"}, description = "Decrypt use Age")
|
||||||
|
boolean age = false;
|
||||||
|
|
||||||
@CommandLine.Option(names = {"--use-jce"}, description = "Use JCE")
|
@CommandLine.Option(names = {"--use-jce"}, description = "Use JCE")
|
||||||
boolean useJce = false;
|
boolean useJce = false;
|
||||||
|
|
||||||
|
|||||||
@@ -82,8 +82,10 @@ public class TinyEncryptMain {
|
|||||||
}
|
}
|
||||||
final boolean encryptOrDecryptSuccess;
|
final boolean encryptOrDecryptSuccess;
|
||||||
if (tinyEncryptArgs.encrypt) {
|
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,
|
encryptOrDecryptSuccess = EncryptedFileUtil.encryptFile(config, tinyEncryptArgs.key, f,
|
||||||
tinyEncryptArgs.compress, !tinyEncryptArgs.skipEnvelop, tinyEncryptArgs.requireSign,
|
tinyEncryptArgs.compress, useEnvelop, tinyEncryptArgs.requireSign,
|
||||||
tinyEncryptArgs.comment, tinyEncryptArgs.encryptedComment);
|
tinyEncryptArgs.comment, tinyEncryptArgs.encryptedComment);
|
||||||
} else {
|
} else {
|
||||||
if (tinyEncryptArgs.showInWindow || tinyEncryptArgs.editInWindow) {
|
if (tinyEncryptArgs.showInWindow || tinyEncryptArgs.editInWindow) {
|
||||||
|
|||||||
@@ -138,10 +138,12 @@ public class TinyEncryptMainUtil {
|
|||||||
.editable(true)
|
.editable(true)
|
||||||
.show().getResult();
|
.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 byte[] bytes = editResult.getBytes(StandardCharsets.UTF_8);
|
||||||
final TinyEncryptMeta meta = TinyEncryptMetaUtil.create(config, tinyEncryptArgs.key,
|
final TinyEncryptMeta meta = TinyEncryptMetaUtil.create(config, tinyEncryptArgs.key,
|
||||||
tinyEncryptArgs.comment, tinyEncryptArgs.encryptedComment,
|
tinyEncryptArgs.comment, tinyEncryptArgs.encryptedComment,
|
||||||
!tinyEncryptArgs.skipEnvelop, tinyEncryptArgs.requireSign);
|
useEnvelop, tinyEncryptArgs.requireSign);
|
||||||
meta.setFileLength((long) bytes.length);
|
meta.setFileLength((long) bytes.length);
|
||||||
meta.setCreated(System.currentTimeMillis());
|
meta.setCreated(System.currentTimeMillis());
|
||||||
meta.setFileLastModified(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("Enc file created")).append(new Date(meta.getCreated())).append("\n");
|
||||||
sb.append(header("Envelops")).append("KMS: ").append(toYesOrNo(meta.getEnvelop()))
|
sb.append(header("Envelops")).append("KMS: ").append(toYesOrNo(meta.getEnvelop()))
|
||||||
.append(", PGP: ").append(toYesOrNo(meta.getPgpEnvelop()))
|
.append(", PGP: ").append(toYesOrNo(meta.getPgpEnvelop()))
|
||||||
|
.append(", Age: ").append(toYesOrNo(meta.getAgeEnvelop()))
|
||||||
.append("\n");
|
.append("\n");
|
||||||
if (StringUtil.isNotBlank(meta.getPgpFingerprint())) {
|
if (StringUtil.isNotBlank(meta.getPgpFingerprint())) {
|
||||||
sb.append(header("PGP fingerprint")).append(meta.getPgpFingerprint()).append("\n");
|
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())) {
|
if (StringUtil.isNotBlank(meta.getComment())) {
|
||||||
sb.append(header("Comment")).append(meta.getComment()).append("\n");
|
sb.append(header("Comment")).append(meta.getComment()).append("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,14 @@ public class TinyEncryptConfig {
|
|||||||
private String localPrivateKeyPem;
|
private String localPrivateKeyPem;
|
||||||
private String localPrivateKeyPemEncrypted;
|
private String localPrivateKeyPemEncrypted;
|
||||||
private String localPrivateKeyPemChallenge;
|
private String localPrivateKeyPemChallenge;
|
||||||
|
private Boolean turnOffEnvelop;
|
||||||
private String pgpEncryptPublicKeyPem;
|
private String pgpEncryptPublicKeyPem;
|
||||||
@Deprecated
|
@Deprecated
|
||||||
private String pgpDecryptCmd;
|
private String pgpDecryptCmd;
|
||||||
private String cardCli;
|
private String cardCli;
|
||||||
|
private String ageRecipient;
|
||||||
|
// Optional, default "age" when `ageRecipient` presents
|
||||||
|
private String ageCli;
|
||||||
|
|
||||||
public String getDefaultKeyName() {
|
public String getDefaultKeyName() {
|
||||||
return defaultKeyName;
|
return defaultKeyName;
|
||||||
@@ -53,6 +57,14 @@ public class TinyEncryptConfig {
|
|||||||
this.localPrivateKeyPemChallenge = localPrivateKeyPemChallenge;
|
this.localPrivateKeyPemChallenge = localPrivateKeyPemChallenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getTurnOffEnvelop() {
|
||||||
|
return turnOffEnvelop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTurnOffEnvelop(Boolean turnOffEnvelop) {
|
||||||
|
this.turnOffEnvelop = turnOffEnvelop;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPgpEncryptPublicKeyPem() {
|
public String getPgpEncryptPublicKeyPem() {
|
||||||
return pgpEncryptPublicKeyPem;
|
return pgpEncryptPublicKeyPem;
|
||||||
}
|
}
|
||||||
@@ -76,4 +88,20 @@ public class TinyEncryptConfig {
|
|||||||
public void setCardCli(String cardCli) {
|
public void setCardCli(String cardCli) {
|
||||||
this.cardCli = 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;
|
package me.hatter.tools.tinyencrypt.config;
|
||||||
|
|
||||||
public class TinyEncryptConstant {
|
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";
|
public static final String ENC_FILE_EXT = ".tinyenc";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ public class TinyEncryptMeta {
|
|||||||
private String encryptedComment;
|
private String encryptedComment;
|
||||||
private String pgpEnvelop;
|
private String pgpEnvelop;
|
||||||
private String pgpFingerprint;
|
private String pgpFingerprint;
|
||||||
|
private String ageEnvelop;
|
||||||
|
private String ageRecipient;
|
||||||
private String envelop;
|
private String envelop;
|
||||||
@JSONField(serialize = false)
|
@JSONField(serialize = false)
|
||||||
private byte[] dataKey;
|
private byte[] dataKey;
|
||||||
@@ -74,6 +76,22 @@ public class TinyEncryptMeta {
|
|||||||
this.pgpFingerprint = pgpFingerprint;
|
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() {
|
public String getEnvelop() {
|
||||||
return envelop;
|
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.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 me.hatter.tools.tinyencrypt.util.AgeCliUtil;
|
||||||
import me.hatter.tools.tinyencrypt.util.CardCliUtil;
|
import me.hatter.tools.tinyencrypt.util.CardCliUtil;
|
||||||
|
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
@@ -131,6 +132,12 @@ public class TinyEncryptMetaUtil {
|
|||||||
log.warn("PGP encrypt public key is not RSAPublicKey: " + pgpEncryptPublicKey.getClass());
|
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.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);
|
||||||
|
|||||||
@@ -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