diff --git a/.gitignore b/.gitignore index d99a1b1..5def27e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,53 +1,11 @@ -# ---> Java -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -# ---> macOS -# General +out/ +build +classes .DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - +.gradle +.classpath +.project +.settings +*.iml +*.ipr +*.iws diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..abcb4ab --- /dev/null +++ b/build.gradle @@ -0,0 +1,92 @@ +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' + +def JsonSlurper = Class.forName('groovy.json.JsonSlurper'); +def buildJSON = JsonSlurper.newInstance().parseText(new File("build.json").text) + +if (buildJSON.application) { apply plugin: 'application' } + +def baseProjectName = buildJSON?.project?.name ?: '__project_name__'; +def shellCommandName = baseProjectName +def eclipseProjectName = baseProjectName +def eclipseProjectComment = buildJSON?.project?.comment ?: '__project_name_comment__' +def jarManifestMainClass = buildJSON?.project?.main ?: 'SampleMain' + +if (buildJSON.application) { mainClassName = jarManifestMainClass } +archivesBaseName = buildJSON?.project?.archiveName ?: baseProjectName +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +def addRepo = new File(System.getProperty("user.home"), ".build_add.repo") + +repositories { + mavenLocal() + // mavenCentral() + maven() { url 'https://maven.aliyun.com/repository/central' } + if (addRepo.exists()) { maven() { url addRepo.text.trim() } } +} + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +// '-x test' skip unit test +defaultTasks 'build' + +buildscript { + repositories { + mavenLocal() + maven() { url 'https://maven.aliyun.com/repository/central' } + mavenCentral() + jcenter() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.11.RELEASE") + } +} +apply plugin: 'org.springframework.boot' +springBoot { + mainClass = jarManifestMainClass +} + +dependencies { + compile files(fileTree(dir: 'lib', includes: ['*.jar'], excludes: ['*-sources.jar', '*-javadoc.jar'])) + + if (buildJSON.repo != null && buildJSON.repo.dependencies != null) { + buildJSON.repo.dependencies.each { + compile("${it}") + } + } + if (buildJSON.repo != null && buildJSON.repo.testDependencies != null) { + buildJSON.repo.testDependencies.each { + testCompile("${it}") + } + } +} + +eclipse { + project { + name = eclipseProjectName + comment = eclipseProjectComment + } + classpath { + defaultOutputDir = file('classes') + downloadSources = true + file { + whenMerged { classpath -> + classpath.entries.findAll { it.kind=='lib' }.each { + if ((it.path != null) && (it.sourcePath == null) && file(it.path.replace(".jar", "-sources.jar")).exists()) { + it.sourcePath = getFileReferenceFactory().fromPath(it.path.replace(".jar", "-sources.jar")) + } + } + } + } + } +} + +eclipseJdt << { + File f = file('.settings/org.eclipse.core.resources.prefs') + f.write('eclipse.preferences.version=1\n') + f.append('encoding/=utf-8') +} diff --git a/build.json b/build.json new file mode 100644 index 0000000..72bcc37 --- /dev/null +++ b/build.json @@ -0,0 +1,22 @@ +{ + "project": { + "name": "tiny-encrypt-java", + "main": "me.hatter.tools.tinyencrypt.TinyEncryptMain", + "archiveName": "tiny-encrypt" + }, + "application": false, + "java": "1.8", + "builder": { + "name": "gradle", + "version": "3.1" + }, + "repo": { + "dependencies": [ + "me.hatter:commons:3.0", + "org.bouncycastle:bcall-jdk15on:1.60" + ], + "testDependencies": [ + "junit:junit:4.12" + ] + } +} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptMain.java b/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptMain.java new file mode 100644 index 0000000..5b84c34 --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/TinyEncryptMain.java @@ -0,0 +1,7 @@ +package me.hatter.tools.tinyencrypt; + +public class TinyEncryptMain { + public static void main(String[] args) { + + } +} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/config/TinyEncryptConfig.java b/src/main/java/me/hatter/tools/tinyencrypt/config/TinyEncryptConfig.java new file mode 100644 index 0000000..6391ccd --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/config/TinyEncryptConfig.java @@ -0,0 +1,13 @@ +package me.hatter.tools.tinyencrypt.config; + +public class TinyEncryptConfig { + private String defaultKeyName; + + public String getDefaultKeyName() { + return defaultKeyName; + } + + public void setDefaultKeyName(String defaultKeyName) { + this.defaultKeyName = defaultKeyName; + } +} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/config/TinyEncryptConstant.java b/src/main/java/me/hatter/tools/tinyencrypt/config/TinyEncryptConstant.java new file mode 100644 index 0000000..4ad6d59 --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/config/TinyEncryptConstant.java @@ -0,0 +1,5 @@ +package me.hatter.tools.tinyencrypt.config; + +public class TinyEncryptConstant { + public static final String VERSION = "0.1.0"; +} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFile.java b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFile.java new file mode 100644 index 0000000..42e4418 --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/EncryptedFile.java @@ -0,0 +1,34 @@ +package me.hatter.tools.tinyencrypt.encrypt; + +import me.hatter.tools.commons.security.crypt.CryptOutputStream; +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.write(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/TinyEncryptMeta.java b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMeta.java new file mode 100644 index 0000000..4d4581e --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMeta.java @@ -0,0 +1,70 @@ +package me.hatter.tools.tinyencrypt.encrypt; + +import com.alibaba.fastjson.annotation.JSONField; + +public class TinyEncryptMeta { + private String version; + private long created; + private String userAgent; + private String comment; + private String envelop; + @JSONField(serialize = false) + private byte[] dataKey; + private byte[] nonce; + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public long getCreated() { + return created; + } + + public void setCreated(long created) { + this.created = created; + } + + public String getUserAgent() { + return userAgent; + } + + public void setUserAgent(String userAgent) { + this.userAgent = userAgent; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getEnvelop() { + return envelop; + } + + public void setEnvelop(String envelop) { + this.envelop = envelop; + } + + public byte[] getDataKey() { + return dataKey; + } + + public void setDataKey(byte[] dataKey) { + this.dataKey = dataKey; + } + + public byte[] getNonce() { + return nonce; + } + + public void setNonce(byte[] nonce) { + this.nonce = nonce; + } +} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMetaUtil.java b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMetaUtil.java new file mode 100644 index 0000000..b67dc4d --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TinyEncryptMetaUtil.java @@ -0,0 +1,58 @@ +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.network.HttpRequest; +import me.hatter.tools.commons.os.OSUtil; +import me.hatter.tools.commons.security.random.RandomTool; +import me.hatter.tools.tinyencrypt.config.TinyEncryptConstant; + +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +public class TinyEncryptMetaUtil { + private static final String KMS_GET_DATA_KEY = "https://hatter.ink/kms/get_data_key.json"; + + public static String toString(TinyEncryptMeta tinyEncryptMeta) { + return JSON.toJSONString(tinyEncryptMeta); + } + + public static TinyEncryptMeta parse(String meta) { + return JSON.parseObject(meta, TinyEncryptMeta.class); + } + + public static TinyEncryptMeta create(String name, String comment) { + List keyValues = new ArrayList<>(); + keyValues.add(new HttpRequest.KeyValue("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: " + + responseObject.getIntValue("status") + + "detail: " + responseObject + ); + } + JSONObject responseData = responseObject.getJSONObject("data"); + byte[] dataKey = Base64.getDecoder().decode(responseData.getString("dataKey")); + String envelop = responseData.getString("envelopJwe"); + + TinyEncryptMeta tinyEncryptMeta = new TinyEncryptMeta(); + tinyEncryptMeta.setVersion("1.0"); + tinyEncryptMeta.setCreated(System.currentTimeMillis()); + tinyEncryptMeta.setDataKey(dataKey); + tinyEncryptMeta.setEnvelop(envelop); + tinyEncryptMeta.setNonce(RandomTool.secureRandom().nextbytes(12)); + tinyEncryptMeta.setUserAgent("TinyEncrypt v" + TinyEncryptConstant.VERSION + "@" + OSUtil.getCurrentOS().name()); + 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/encrypt/Tlv.java b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/Tlv.java new file mode 100644 index 0000000..c9d150b --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/Tlv.java @@ -0,0 +1,31 @@ +package me.hatter.tools.tinyencrypt.encrypt; + +public class Tlv { + private int tag; + private int length; + private byte[] value; + + public int getTag() { + return tag; + } + + public void setTag(int tag) { + this.tag = tag; + } + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + + public byte[] getValue() { + return value; + } + + public void setValue(byte[] value) { + this.value = value; + } +} diff --git a/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TlvUtil.java b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TlvUtil.java new file mode 100644 index 0000000..ee2349b --- /dev/null +++ b/src/main/java/me/hatter/tools/tinyencrypt/encrypt/TlvUtil.java @@ -0,0 +1,39 @@ +package me.hatter.tools.tinyencrypt.encrypt; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class TlvUtil { + + public static void write(DataOutputStream os, Tlv tlv) throws IOException { + os.writeShort(tlv.getTag()); + os.writeInt(tlv.getLength()); + os.write(tlv.getValue()); + } + + public static Tlv read(DataInputStream is) throws IOException { + int tag = is.readShort(); + int length = is.readInt(); + byte[] bs = new byte[length]; + is.readFully(bs); + Tlv tlv = new Tlv(); + tlv.setTag(tag); + tlv.setLength(length); + tlv.setValue(bs); + return tlv; + } + + public static Tlv create(int tag, String value) { + return create(tag, value.getBytes(StandardCharsets.UTF_8)); + } + + public static Tlv create(int tag, byte[] value) { + Tlv tlv = new Tlv(); + tlv.setTag(tag); + tlv.setLength(value.length); + tlv.setValue(value); + return tlv; + } +}