diff --git a/build.json b/build.json index 9cd3c2a..0c910ec 100644 --- a/build.json +++ b/build.json @@ -12,8 +12,9 @@ }, "repo": { "dependencies": [ - "me.hatter:commons:3.29", - "org.bouncycastle:bcall-jdk15on:1.60" + "me.hatter:commons:3.30", + "org.bouncycastle:bcprov-jdk15on:1.62", + "info.picocli:picocli:4.6.1" ], "testDependencies": [ "junit:junit:4.12" diff --git a/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptArgs.java b/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptArgs.java new file mode 100644 index 0000000..74902f2 --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptArgs.java @@ -0,0 +1,29 @@ +package me.hatter.tools.tinyencrypt; + +import picocli.CommandLine; + +import java.io.File; + +@CommandLine.Command(name = "tiny-encrypt", version = "tiny-encrypt v0.1.0") +public class TinyEncryptArgs { + @CommandLine.Option(names = {"-e", "--encrypt"}, description = "Encrypt file") + boolean encrypt = false; + + @CommandLine.Option(names = {"-d", "--decrypt"}, description = "Decrypt file") + boolean decrypt = false; + + @CommandLine.Option(names = {"-k", "--key"}, description = "Encrypt key") + String key; + + @CommandLine.Option(names = {"-c", "--comment"}, description = "Encrypt comment") + String comment; + + @CommandLine.Parameters(paramLabel = "FILE", description = "Encrypt or Decrypt files") + File[] files; + + @CommandLine.Option(names = {"-h", "--help"}, usageHelp = true, description = "Display a help message") + boolean helpRequested = false; + + @CommandLine.Option(names = {"-V", "--version"}, versionHelp = true, description = "Display a version message") + boolean versionRequested = false; +} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptMain.java b/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptMain.java index 5b84c34..29025ce 100644 --- a/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptMain.java +++ b/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptMain.java @@ -1,7 +1,60 @@ package me.hatter.tools.tinyencrypt; -public class TinyEncryptMain { - public static void main(String[] args) { +import me.hatter.tools.commons.exception.JumpOutException; +import me.hatter.tools.commons.log.LogConfig; +import me.hatter.tools.commons.log.LogTool; +import me.hatter.tools.commons.log.LogTools; +import me.hatter.tools.tinyencrypt.encrypt.EncryptedFileUtil; +import me.hatter.tools.tinyencrypt.util.BCUtil; +import picocli.CommandLine; +import java.io.File; + +public class TinyEncryptMain { + private static final LogTool log; + + static { + BCUtil.init(); + LogConfig.initMuteInfoMode(); + log = LogTools.getLogTool(TinyEncryptMain.class); + } + + public static void main(String[] args) { + TinyEncryptArgs tinyEncryptArgs = new TinyEncryptArgs(); + CommandLine cmd = new CommandLine(tinyEncryptArgs); + cmd.parseArgs(args); + + if (cmd.isUsageHelpRequested()) { + cmd.usage(cmd.getOut()); + return; + } else if (cmd.isVersionHelpRequested()) { + cmd.printVersionHelp(cmd.getOut()); + return; + } + + boolean isEncrypt = tinyEncryptArgs.encrypt; + boolean isDecrypt = tinyEncryptArgs.decrypt; + if (isEncrypt && isDecrypt) { + log.error("Encrypt and decrypt flag cannot both assigned."); + return; + } + if ((!isDecrypt) && (!isEncrypt)) { + log.error("Encrypt and decrypt flag must assign one."); + return; + } + + if ((tinyEncryptArgs.files == null) || (tinyEncryptArgs.files.length == 0)) { + log.error("FILE is not assigned."); + return; + } + + try { + for (File f : tinyEncryptArgs.files) { + EncryptedFileUtil.encryptFile(tinyEncryptArgs.key, f, tinyEncryptArgs.comment); + } + } catch (JumpOutException joe) { + log.error(joe.getMessage()); + log.debug(joe.getMessage(), joe); + } } } diff --git a/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFile.java b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFile.java deleted file mode 100644 index 4804500..0000000 --- a/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFile.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.hatter.tools.tinyencrypt.encrypt; - -import me.hatter.tools.commons.security.crypt.CryptOutputStream; -import me.hatter.tools.commons.tlv.TlvUtil; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.security.Security; - -public class EncryptedFile extends FilterOutputStream { - static { - Security.addProvider(new BouncyCastleProvider()); - } -// private TinyEncryptMeta tinyEncryptMeta; - - public static void main(String[] args) throws IOException { - DataOutputStream dos = new DataOutputStream(new FileOutputStream("aaa.enc")); - TinyEncryptMeta meta = TinyEncryptMetaUtil.create("prod_ec_key", "..."); - EncryptedFile file = new EncryptedFile(dos, meta); - file.write("Hello World".getBytes(StandardCharsets.UTF_8)); - file.close(); - } - - public EncryptedFile(DataOutputStream rawOut, TinyEncryptMeta tinyEncryptMeta) throws IOException { - super(getCryptoOutputStream(rawOut, tinyEncryptMeta)); -// this.tinyEncryptMeta = tinyEncryptMeta; - TlvUtil.writeTlv(rawOut, TlvUtil.create(0, TinyEncryptMetaUtil.toString(tinyEncryptMeta))); - rawOut.flush(); - } - - private static OutputStream getCryptoOutputStream(OutputStream out, TinyEncryptMeta tinyEncryptMeta) { - return CryptOutputStream.gcmEncrypt(out, tinyEncryptMeta.getDataKey(), tinyEncryptMeta.getNonce()); - } -} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFileUtil.java b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFileUtil.java new file mode 100644 index 0000000..e079e3b --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFileUtil.java @@ -0,0 +1,58 @@ +package me.hatter.tools.tinyencrypt.encrypt; + +import me.hatter.tools.commons.io.CopyRollCounter; +import me.hatter.tools.commons.io.IOUtil; +import me.hatter.tools.commons.log.LogTool; +import me.hatter.tools.commons.log.LogTools; +import me.hatter.tools.commons.security.crypt.CryptOutputStream; +import me.hatter.tools.commons.tlv.TlvUtil; +import me.hatter.tools.tinyencrypt.util.BCUtil; + +import java.io.*; + +public class EncryptedFileUtil { + private static final LogTool log = LogTools.getLogTool(EncryptedFileUtil.class); + + static { + BCUtil.init(); + } + + public static void encryptFile(String keyName, File file, String comment) { + File encFile = getEncryptFile(file); + if (encFile.exists()) { + log.warn("File exists, skip: " + encFile); + return; + } + try { + try (FileInputStream fis = new FileInputStream(file)) { + try (FileOutputStream fos = new FileOutputStream(encFile)) { + TinyEncryptMeta meta = TinyEncryptMetaUtil.create(keyName, comment); + TlvUtil.writeTlv(fos, TlvUtil.create(1, TinyEncryptMetaUtil.toString(meta))); + fos.flush(); + try (OutputStream newOs = getCryptoOutputStream(fos, meta)) { + IOUtil.copy(fis, newOs, new CopyRollCounter() { + @Override + public void count(long count, long length) { + // TODO ... + } + }); + } + } + } + log.info("Encrypt file success: " + file); + } catch (Exception e) { + log.error("Encrypt file filed: " + file + ", reason: " + e.getMessage()); + log.debug("Encrypt file filed: " + file + ", reason: " + e.getMessage(), e); + } + } + + public static File getEncryptFile(File file) { + File absFile = file.getAbsoluteFile(); + File encFile = new File(absFile.getParent(), absFile.getName() + ".tinyenc"); + return encFile; + } + + private static OutputStream getCryptoOutputStream(OutputStream out, TinyEncryptMeta tinyEncryptMeta) { + return CryptOutputStream.gcmEncrypt(out, tinyEncryptMeta.getDataKey(), tinyEncryptMeta.getNonce()); + } +} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMetaUtil.java b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMetaUtil.java index b67dc4d..0c6972a 100644 --- a/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMetaUtil.java +++ b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMetaUtil.java @@ -3,6 +3,9 @@ package me.hatter.tools.tinyencrypt.encrypt; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import me.hatter.tools.commons.bytes.Bytes; +import me.hatter.tools.commons.exception.JumpOutException; +import me.hatter.tools.commons.log.LogTool; +import me.hatter.tools.commons.log.LogTools; import me.hatter.tools.commons.network.HttpRequest; import me.hatter.tools.commons.os.OSUtil; import me.hatter.tools.commons.security.random.RandomTool; @@ -13,6 +16,7 @@ import java.util.Base64; import java.util.List; public class TinyEncryptMetaUtil { + private static final LogTool log = LogTools.getLogTool(TinyEncryptMetaUtil.class); private static final String KMS_GET_DATA_KEY = "https://hatter.ink/kms/get_data_key.json"; public static String toString(TinyEncryptMeta tinyEncryptMeta) { @@ -26,11 +30,11 @@ public class TinyEncryptMetaUtil { public static TinyEncryptMeta create(String name, String comment) { List keyValues = new ArrayList<>(); keyValues.add(new HttpRequest.KeyValue("name", name)); + log.info("Get data key from kms, key name: " + name); Bytes response = HttpRequest.fromUrl(KMS_GET_DATA_KEY).post(keyValues); - JSONObject responseObject = response.asJSON(); if (responseObject.getIntValue("status") != 200) { - throw new RuntimeException("Get data key from kms error, status: " + throw new JumpOutException("Get data key from kms error, status: " + responseObject.getIntValue("status") + "detail: " + responseObject ); @@ -49,10 +53,4 @@ public class TinyEncryptMetaUtil { tinyEncryptMeta.setComment("test"); return tinyEncryptMeta; } - - public static void main(String[] args) { - TinyEncryptMeta tinyEncryptMeta = create("prod_ec_key", ""); - - System.out.println(JSON.toJSONString(tinyEncryptMeta, true)); - } } diff --git a/src/main/java/me/hatter/tools/tinyencrypt/util/BCUtil.java b/src/main/java/me/hatter/tools/tinyencrypt/util/BCUtil.java new file mode 100644 index 0000000..1b0353b --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/util/BCUtil.java @@ -0,0 +1,14 @@ +package me.hatter.tools.tinyencrypt.util; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import java.security.Security; + +public class BCUtil { + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static void init() { + } +}