From 198dbaf3f907c08f2dd61b75110548aef80a7241 Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Thu, 1 Nov 2018 10:12:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20Android=20P=E4=B8=80?= =?UTF-8?q?=E7=9B=B4=E6=98=BE=E7=A4=BA=E6=89=93=E5=8C=85=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../autojs/autojs/build/AutoJsApkBuilder.java | 14 +- .../org/autojs/autojs/build/TinySign.java | 218 ++++++++++++++++++ .../autojs/ui/project/BuildActivity.java | 10 +- common/release/output.json | 2 +- project-versions.json | 4 +- 5 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/org/autojs/autojs/build/TinySign.java diff --git a/app/src/main/java/org/autojs/autojs/autojs/build/AutoJsApkBuilder.java b/app/src/main/java/org/autojs/autojs/autojs/build/AutoJsApkBuilder.java index 174e609d..991e6de3 100644 --- a/app/src/main/java/org/autojs/autojs/autojs/build/AutoJsApkBuilder.java +++ b/app/src/main/java/org/autojs/autojs/autojs/build/AutoJsApkBuilder.java @@ -11,6 +11,8 @@ import com.stardust.autojs.project.BuildInfo; import com.stardust.autojs.project.ProjectConfig; import com.stardust.pio.PFiles; +import org.autojs.autojs.build.TinySign; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -20,6 +22,8 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.concurrent.Callable; + + /** * Created by Stardust on 2017/10/24. */ @@ -129,15 +133,19 @@ public class AutoJsApkBuilder extends ApkBuilder { private ManifestEditor mManifestEditor; private String mWorkspacePath; private AppConfig mAppConfig; + private final File mOutApkFile; + public AutoJsApkBuilder(InputStream apkInputStream, File outApkFile, String workspacePath) { super(apkInputStream, outApkFile, workspacePath); mWorkspacePath = workspacePath; + mOutApkFile = outApkFile; PFiles.ensureDir(outApkFile.getPath()); } public AutoJsApkBuilder(File inFile, File outFile, String workspacePath) throws FileNotFoundException { super(inFile, outFile, workspacePath); + mOutApkFile = outFile; mWorkspacePath = workspacePath; } @@ -249,8 +257,10 @@ public class AutoJsApkBuilder extends ApkBuilder { if (mProgressCallback != null) { GlobalAppContext.post(() -> mProgressCallback.onSign(AutoJsApkBuilder.this)); } - - return (AutoJsApkBuilder) super.sign(); + FileOutputStream fos = new FileOutputStream(mOutApkFile); + TinySign.sign(new File(this.mWorkspacePath), fos); + fos.close(); + return this; } @Override diff --git a/app/src/main/java/org/autojs/autojs/build/TinySign.java b/app/src/main/java/org/autojs/autojs/build/TinySign.java new file mode 100644 index 00000000..4634dbc3 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/build/TinySign.java @@ -0,0 +1,218 @@ +package org.autojs.autojs.build; + +import android.util.Base64; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.security.DigestOutputStream; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +public class TinySign { + public TinySign() { + } + + private static byte[] dBase64(String data) throws UnsupportedEncodingException { + return Base64.decode(data.getBytes("UTF-8"), Base64.NO_WRAP); + } + + private static void doDir(String prefix, File dir, ZipOutputStream zos, DigestOutputStream dos, Manifest m) throws IOException { + zos.putNextEntry(new ZipEntry(prefix)); + zos.closeEntry(); + File[] arr$ = dir.listFiles(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + File f = arr$[i$]; + if (f.isFile()) { + doFile(prefix + f.getName(), f, zos, dos, m); + } else { + doDir(prefix + f.getName() + "/", f, zos, dos, m); + } + } + + } + + private static void doFile(String name, File f, ZipOutputStream zos, DigestOutputStream dos, Manifest m) throws IOException { + zos.putNextEntry(new ZipEntry(name)); + FileInputStream fis = FileUtils.openInputStream(f); + IOUtils.copy(fis, dos); + IOUtils.closeQuietly(fis); + byte[] digets = dos.getMessageDigest().digest(); + zos.closeEntry(); + Attributes attr = new Attributes(); + attr.putValue("SHA1-Digest", eBase64(digets)); + m.getEntries().put(name, attr); + } + + private static String eBase64(byte[] data) { + return Base64.encodeToString(data, Base64.NO_WRAP); + } + + private static Manifest generateSF(Manifest manifest) throws NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest md = MessageDigest.getInstance("SHA1"); + PrintStream print = new PrintStream(new DigestOutputStream(new OutputStream() { + public void write(byte[] arg0) { + } + + public void write(byte[] arg0, int arg1, int arg2) { + } + + public void write(int arg0) { + } + }, md), true, "UTF-8"); + Manifest sf = new Manifest(); + Map entries = manifest.getEntries(); + Iterator> iterator = entries.entrySet().iterator(); + + while(iterator.hasNext()) { + Entry entry = iterator.next(); + print.print("Name: " + entry.getKey() + "\r\n"); + Iterator> iter = entry.getValue().entrySet().iterator(); + + while(iter.hasNext()) { + Entry att = iter.next(); + print.print(att.getKey() + ": " + att.getValue() + "\r\n"); + } + + print.print("\r\n"); + print.flush(); + Attributes sfAttr = new Attributes(); + sfAttr.putValue("SHA1-Digest", eBase64(md.digest())); + sf.getEntries().put(entry.getKey(), sfAttr); + } + + return sf; + } + + private static Signature instanceSignature() throws Exception { + byte[] data = dBase64("MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEAoiZSqWnFDHA5sXKoDiUUO9JuL7cm/2dCck5MKumVvv+WfSg0jsovnywsFN0pifmdRSLmOdUkh0d0J+tOnSgtsQIDAQABAkEAihag5u3Qhds9BsViIUmqhZebhr8vUuqZR8cuTo1GnbSoOHIPbAgD3J8TDbC/CVqae8NrgwLp325Pem1Tuof/0QIhAN1hqft1K307bsljgw3iYKopGVZBHRXsjRnNL4edV9QrAiEAu4F+XtS1wohGLz5QtfuMFsQNo4l31mCjt6WpBDmSi5MCIQCB++YijxmJ3mueM5+vd0vqnVcTHghF5y6yB5fwuKHpIQIgInnS1Hjj2prX3MPmby+LOHxfzZvvDtnCAHhTNVWonkUCIQCvV8l+SpL6Vh1nQ/2EKFJo2dbZB3wKG/BEYsFkPFbn9w=="); + KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(data)); + Signature signature = Signature.getInstance("SHA1withRSA"); + signature.initSign(privateKey); + return signature; + } + + public static void sign(File dir, OutputStream out) throws Exception { + ZipOutputStream zos = new ZipOutputStream(out); + zos.putNextEntry(new ZipEntry("META-INF/")); + zos.closeEntry(); + Manifest manifest = new Manifest(); + String sha1Manifest = writeMF(dir, manifest, zos); + Manifest sf = generateSF(manifest); + byte[] sign = writeSF(zos, sf, sha1Manifest); + writeRSA(zos, sign); + IOUtils.closeQuietly(zos); + } + + private static String writeMF(File dir, Manifest manifest, ZipOutputStream zos) throws NoSuchAlgorithmException, IOException { + MessageDigest md = MessageDigest.getInstance("SHA1"); + DigestOutputStream dos = new DigestOutputStream(zos, md); + zipAndSha1(dir, zos, dos, manifest); + Attributes main = manifest.getMainAttributes(); + main.putValue("Manifest-Version", "1.0"); + main.putValue("Created-By", "tiny-sign-" + TinySign.class.getPackage().getImplementationVersion()); + zos.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF")); + manifest.write(dos); + zos.closeEntry(); + return eBase64(md.digest()); + } + + private static void writeRSA(ZipOutputStream zos, byte[] sign) throws IOException { + zos.putNextEntry(new ZipEntry("META-INF/CERT.RSA")); + zos.write(dBase64("MIIB5gYJKoZIhvcNAQcCoIIB1zCCAdMCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHAaCCATYwggEyMIHdoAMCAQICBCunMokwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAxMEVGVzdDAeFw0xMjA0MjIwODQ1NDdaFw0xMzA0MjIwODQ1NDdaMA8xDTALBgNVBAMTBFRlc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAoiZSqWnFDHA5sXKoDiUUO9JuL7cm/2dCck5MKumVvv+WfSg0jsovnywsFN0pifmdRSLmOdUkh0d0J+tOnSgtsQIDAQABoyEwHzAdBgNVHQ4EFgQUVL2yOinUwpARE1tOPxc1bf4WrTgwDQYJKoZIhvcNAQELBQADQQAnj/eZwhqwb2tgSYNvgRo5bBNNCpJbQ4alEeP/MLSIWf2nZpAix8T3oS9X2affQtAgctPATcKQaiH2B4L7FKlVMXoweAIBATAXMA8xDTALBgNVBAMTBFRlc3QCBCunMokwCQYFKw4DAhoFADANBgkqhkiG9w0BAQEFAARA")); + zos.write(sign); + zos.closeEntry(); + } + + private static byte[] writeSF(ZipOutputStream zos, Manifest sf, String sha1Manifest) throws Exception { + Signature signature = instanceSignature(); + zos.putNextEntry(new ZipEntry("META-INF/CERT.SF")); + TinySign.SignatureOutputStream out = new TinySign.SignatureOutputStream(zos, signature); + out.write("Signature-Version: 1.0\r\n".getBytes("UTF-8")); + out.write(("Created-By: tiny-sign-" + TinySign.class.getPackage().getImplementationVersion() + "\r\n").getBytes("UTF-8")); + out.write("SHA1-Digest-Manifest: ".getBytes("UTF-8")); + out.write(sha1Manifest.getBytes("UTF-8")); + out.write(13); + out.write(10); + sf.write(out); + zos.closeEntry(); + return signature.sign(); + } + + private static void zipAndSha1(File dir, ZipOutputStream zos, DigestOutputStream dos, Manifest m) throws NoSuchAlgorithmException, IOException { + File[] arr$ = dir.listFiles(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + File f = arr$[i$]; + if (!f.getName().startsWith("META-INF")) { + if (f.isFile()) { + doFile(f.getName(), f, zos, dos, m); + } else { + doDir(f.getName() + "/", f, zos, dos, m); + } + } + } + + } + + private static class SignatureOutputStream extends FilterOutputStream { + private Signature mSignature; + + public SignatureOutputStream(OutputStream out, Signature sig) { + super(out); + this.mSignature = sig; + } + + public void write(byte[] buffer) throws IOException { + try { + this.mSignature.update(buffer); + } catch (SignatureException var3) { + throw new IOException("SignatureException: " + var3); + } + + this.out.write(buffer); + } + + public void write(byte[] b, int off, int len) throws IOException { + try { + this.mSignature.update(b, off, len); + } catch (SignatureException var5) { + throw new IOException("SignatureException: " + var5); + } + + this.out.write(b, off, len); + } + + public void write(int b) throws IOException { + try { + this.mSignature.update((byte)b); + } catch (SignatureException var3) { + throw new IOException("SignatureException: " + var3); + } + + this.out.write(b); + } + } +} diff --git a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java index 7feaa877..103d6a93 100644 --- a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java @@ -227,11 +227,11 @@ public class BuildActivity extends BaseActivity implements AutoJsApkBuilder.Prog private boolean checkPackageNameValid(TextInputEditText editText) { Editable text = editText.getText(); String hint = ((TextInputLayout) editText.getParent().getParent()).getHint().toString(); - if(TextUtils.isEmpty(text)){ + if (TextUtils.isEmpty(text)) { editText.setError(hint + getString(R.string.text_should_not_be_empty)); return false; } - if(!REGEX_PACKAGE_NAME.matcher(text).matches()){ + if (!REGEX_PACKAGE_NAME.matcher(text).matches()) { editText.setError(getString(R.string.text_invalid_package_name)); return false; } @@ -302,8 +302,10 @@ public class BuildActivity extends BaseActivity implements AutoJsApkBuilder.Prog } private void onBuildFailed(Throwable error) { - mProgressDialog.dismiss(); - mProgressDialog = null; + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } Toast.makeText(this, getString(R.string.text_build_failed) + error.getMessage(), Toast.LENGTH_SHORT).show(); Log.e(LOG_TAG, "Build failed", error); } diff --git a/common/release/output.json b/common/release/output.json index 2b5a5624..0bed4d9b 100644 --- a/common/release/output.json +++ b/common/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":435,"versionName":"4.0.4 Alpha6","enabled":true,"outputFile":"commonRelease-4.0.4 Alpha6.apk","fullName":"commonRelease","baseName":"common-release"},"path":"commonRelease-4.0.4 Alpha6.apk","properties":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":436,"versionName":"4.0.4 Alpha7","enabled":true,"outputFile":"commonRelease-4.0.4 Alpha7.apk","fullName":"commonRelease","baseName":"common-release"},"path":"commonRelease-4.0.4 Alpha7.apk","properties":{}}] \ No newline at end of file diff --git a/project-versions.json b/project-versions.json index b99f2042..54411dcc 100644 --- a/project-versions.json +++ b/project-versions.json @@ -1,6 +1,6 @@ { - "appVersionCode": 435, - "appVersionName": "4.0.4 Alpha6", + "appVersionCode": 436, + "appVersionName": "4.0.4 Alpha7", "target": 28, "mini": 17, "compile": 28,