package me.hatter.tools.tinyencrypt.util; import me.hatter.tools.commons.assertion.AssertUtil; import me.hatter.tools.commons.io.RFile; import me.hatter.tools.commons.log.LogTool; import me.hatter.tools.commons.log.LogTools; import me.hatter.tools.commons.misc.Base64s; import java.util.Optional; public class AgeCliUtil { private static final LogTool log = LogTools.getLogTool(AgeCliUtil.class); public static void main(String[] args) throws Exception { System.out.println( decryptBytes("age", "age1yubikey1qtwna67eqmyu7q9s3mpf7lkkrqzdrnqazdfdjftmv2qercy0cdchc7jcpu5", "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFNWDVVUSBBNGMwMTl3\neXhQSXQ1dnhxSzFUNk4rNlJ6QkJCcnJqazZzTVAzcS9sSktyRQpBOXNQSVFudDBQ\nd2kyMFpySGxIUS8yemVwdkJxdEpoZHl3NTJuUzFaTjJJCi0tLSBwbW45LzUyem9j\nZWpROUFRT2huVDArS2hjaWFBeVd4S0xaTzh5eFo5QlhRCo2zDaVK/7YuOShVT0iw\n43LFOEP3T9v53YKhuUqjmm+6af93U2H/ppZVZluXrYPpR6+WHL4vdxflExP4yH1e\n8C+hFKSSdvxQe7cE8lHG\n-----END AGE ENCRYPTED FILE-----", Optional.empty()) ); } public static Optional decryptBytes(String ageCli, String ageRecipient, String ageEnvelop, Optional serialOpt) { AssertUtil.notEmpty(ageCli, "Age-cli cannot be empty"); AssertUtil.notEmpty(ageRecipient, "Age-recipient cannot be empty"); AssertUtil.isTrue(ageRecipient.matches("^[a-zA-Z0-9]+$"), "Age-recipient illegal"); AssertUtil.notEmpty(ageEnvelop, "Age-envelop cannot be empty"); AssertUtil.isFalse(ageEnvelop.contains("'"), "Age-envelop cannot contains `'`"); AssertUtil.isTrue(ageEnvelop.matches("^[\\-/+=\\n\\r\\sa-zA-Z0-9]+$"), "Age-envelop illegal"); final RFile recipientAgeFileWithoutSerial = RFile.fromUserHome(".tinyencrypt/" + ageRecipient + ".age"); final RFile recipientAgeFileWithSerial = serialOpt.map(s -> RFile.fromUserHome(".tinyencrypt/" + ageRecipient + ".age." + s)).orElse(null); final RFile recipientAgeFile = ((recipientAgeFileWithSerial != null) && recipientAgeFileWithSerial.isFile()) ? recipientAgeFileWithSerial : recipientAgeFileWithoutSerial; AssertUtil.isTrue(recipientAgeFile.isFile(), "Age key file required"); final ProcessBuilder pb = new ProcessBuilder( "sh", "-c", "echo '" + ageEnvelop + "' | " + ageCli + " -d -i " + recipientAgeFile.file().getAbsolutePath()); pb.redirectInput(ProcessBuilder.Redirect.PIPE); pb.redirectError(ProcessBuilder.Redirect.PIPE); return CardCliUtil.runProcess(pb).map(b -> Base64s.normal().decode(b.trim())); } 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 be null"); final ProcessBuilder pb = new ProcessBuilder( "sh", "-c", "echo " + Base64s.normal().encode(bytes) + " | " + ageCli + " -e -r " + ageRecipient + " -a"); final Optional outputsOpt = CardCliUtil.runProcess(pb); if (!outputsOpt.isPresent()) { throw new RuntimeException("Encrypt use age failed!"); } return outputsOpt.get(); } public static Optional readUserPin() { System.out.print("Input PIV user PIN: "); final char[] pin = System.console().readPassword(); if (pin.length < 6) { log.error("User PIN must have 6 letters"); return Optional.empty(); } return Optional.of(new String(pin)); } }