diff --git a/.gitignore b/.gitignore
index a219f7aaa..d692faa0a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@ obj/
libs/
*.zip
*.jks
+*.apk
-# Copied binaries
+# Built binaries
ziptools/zipadjust
diff --git a/.gitmodules b/.gitmodules
index c12cb1cb2..d87eda592 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -11,7 +11,7 @@
path = jni/magiskpolicy
url = https://github.com/topjohnwu/magiskpolicy.git
[submodule "MagiskManager"]
- path = MagiskManager
+ path = java
url = https://github.com/topjohnwu/MagiskManager.git
[submodule "jni/busybox"]
path = jni/external/busybox
diff --git a/MagiskManager b/MagiskManager
deleted file mode 160000
index 773c24b7f..000000000
--- a/MagiskManager
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 773c24b7fc7e6931c0932cd53943813eb4ebd027
diff --git a/README.MD b/README.MD
index 7b587ab15..e7c460cf5 100644
--- a/README.MD
+++ b/README.MD
@@ -45,7 +45,7 @@ along with this program. If not, see .
## Credits
-**MagiskManager** (`MagiskManager`)
+**MagiskManager** (`java`)
* Copyright 2016-2017, John Wu (@topjohnwu)
* All contributors and translators
diff --git a/build.py b/build.py
index 826c804dd..9a0d13644 100755
--- a/build.py
+++ b/build.py
@@ -51,6 +51,10 @@ def zip_with_msg(zipfile, source, target):
print('zip: {} -> {}'.format(source, target))
zipfile.write(source, target)
+def cp(source, target):
+ print('cp: {} -> {}'.format(source, target))
+ shutil.copyfile(source, target)
+
def build_all(args):
build_binary(args)
build_apk(args)
@@ -75,26 +79,23 @@ def build_apk(args):
for key in ['public.certificate.x509.pem', 'private.key.pk8']:
source = os.path.join('ziptools', key)
- target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', key)
- print('cp: {} -> {}'.format(source, target))
- shutil.copyfile(source, target)
+ target = os.path.join('java', 'app', 'src', 'main', 'assets', key)
+ cp(source, target)
for script in ['magisk_uninstaller.sh', 'util_functions.sh']:
source = os.path.join('scripts', script)
- target = os.path.join('MagiskManager', 'app', 'src', 'main', 'assets', script)
- print('cp: {} -> {}'.format(source, target))
- shutil.copyfile(source, target)
+ target = os.path.join('java', 'app', 'src', 'main', 'assets', script)
+ cp(source, target)
- os.chdir('MagiskManager')
+ os.chdir('java')
# Build unhide app and place in assets
- proc = subprocess.run('{} unhide::assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
+ proc = subprocess.run('{} unhide:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
source = os.path.join('unhide', 'build', 'outputs', 'apk', 'release', 'unhide-release-unsigned.apk')
target = os.path.join('app', 'src', 'main', 'assets', 'unhide.apk')
- print('cp: {} -> {}'.format(source, target))
- shutil.copyfile(source, target)
+ cp(source, target)
print('')
@@ -102,7 +103,7 @@ def build_apk(args):
if not os.path.exists(os.path.join('..', 'release_signature.jks')):
error('Please generate a java keystore and place it in \'release_signature.jks\'')
- proc = subprocess.run('{} app::assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
+ proc = subprocess.run('{} app:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
@@ -141,16 +142,26 @@ def build_apk(args):
silentremove(unsigned)
silentremove(aligned)
else:
- proc = subprocess.run('{} app::assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
+ proc = subprocess.run('{} app:assembleDebug'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
error('Build Magisk Manager failed!')
# Return to upper directory
os.chdir('..')
-def sign_adjust_zip(unsigned, output):
+def build_snet(args):
+ os.chdir('java')
+ proc = subprocess.run('{} snet:assembleRelease'.format(os.path.join('.', 'gradlew')), shell=True)
+ if proc.returncode != 0:
+ error('Build snet extention failed!')
+ source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
+ target = os.path.join('..', 'snet.apk')
+ print('')
+ cp(source, target)
+ os.chdir('..')
- zipsigner = os.path.join('ziptools', 'zipsigner', 'build', 'libs', 'zipsigner.jar')
+def sign_adjust_zip(unsigned, output):
+ jarsigner = os.path.join('java', 'jarsigner', 'build', 'libs', 'jarsigner-fat.jar')
if os.name != 'nt' and not os.path.exists(os.path.join('ziptools', 'zipadjust')):
header('* Building zipadjust')
@@ -158,13 +169,13 @@ def sign_adjust_zip(unsigned, output):
proc = subprocess.run('gcc -o ziptools/zipadjust ziptools/zipadjust_src/*.c -lz', shell=True)
if proc.returncode != 0:
error('Build zipadjust failed!')
- if not os.path.exists(zipsigner):
- header('* Building zipsigner.jar')
- os.chdir(os.path.join('ziptools', 'zipsigner'))
- proc = subprocess.run('{} shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
+ if not os.path.exists(jarsigner):
+ header('* Building jarsigner-fat.jar')
+ os.chdir('java')
+ proc = subprocess.run('{} jarsigner:shadowJar'.format(os.path.join('.', 'gradlew')), shell=True)
if proc.returncode != 0:
- error('Build zipsigner.jar failed!')
- os.chdir(os.path.join('..', '..'))
+ error('Build jarsigner-fat.jar failed!')
+ os.chdir('..')
header('* Signing / Adjusting Zip')
@@ -172,7 +183,7 @@ def sign_adjust_zip(unsigned, output):
privateKey = os.path.join('ziptools', 'private.key.pk8')
# Unsigned->signed
- proc = subprocess.run(['java', '-jar', zipsigner,
+ proc = subprocess.run(['java', '-jar', jarsigner,
publicKey, privateKey, unsigned, 'tmp_signed.zip'])
if proc.returncode != 0:
error('First sign flashable zip failed!')
@@ -183,7 +194,7 @@ def sign_adjust_zip(unsigned, output):
error('Adjust flashable zip failed!')
# Adjusted -> output
- proc = subprocess.run(['java', '-jar', zipsigner,
+ proc = subprocess.run(['java', '-jar', jarsigner,
"-m", publicKey, privateKey, 'tmp_adjusted.zip', output])
if proc.returncode != 0:
error('Second sign flashable zip failed!')
@@ -243,7 +254,7 @@ def zip_main(args):
zip_with_msg(zipf, source, target)
# APK
- source = os.path.join('MagiskManager', 'app', 'build', 'outputs', 'apk',
+ source = os.path.join('java', 'app', 'build', 'outputs', 'apk',
'release' if args.release else 'debug', 'app-release.apk' if args.release else 'app-debug.apk')
target = os.path.join('common', 'magisk.apk')
zip_with_msg(zipf, source, target)
@@ -328,20 +339,20 @@ def zip_uninstaller(args):
def cleanup(args):
if len(args.target) == 0:
- args.target = ['binary', 'apk', 'zip']
+ args.target = ['binary', 'java', 'zip']
if 'binary' in args.target:
- header('* Cleaning Magisk binaries')
+ header('* Cleaning binaries')
subprocess.run(os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') + ' clean', shell=True)
- if 'apk' in args.target:
- header('* Cleaning Magisk Manager')
- os.chdir('MagiskManager')
+ if 'java' in args.target:
+ header('* Cleaning java')
+ os.chdir('java')
subprocess.run('{} clean'.format(os.path.join('.', 'gradlew')), shell=True)
os.chdir('..')
if 'zip' in args.target:
- header('* Cleaning created zip files')
+ header('* Cleaning zip files')
for f in os.listdir('.'):
if '.zip' in f:
print('rm {}'.format(f))
@@ -364,6 +375,9 @@ binary_parser.set_defaults(func=build_binary)
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
apk_parser.set_defaults(func=build_apk)
+snet_parser = subparsers.add_parser('snet', help='build snet extention for Magisk Manager')
+snet_parser.set_defaults(func=build_snet)
+
zip_parser = subparsers.add_parser('zip', help='zip and sign Magisk into a flashable zip')
zip_parser.add_argument('versionString')
zip_parser.add_argument('versionCode', type=int)
@@ -372,7 +386,7 @@ zip_parser.set_defaults(func=zip_main)
uninstaller_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller')
uninstaller_parser.set_defaults(func=zip_uninstaller)
-clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary apk zip')
+clean_parser = subparsers.add_parser('clean', help='clean [target...] targets: binary java zip')
clean_parser.add_argument('target', nargs='*')
clean_parser.set_defaults(func=cleanup)
diff --git a/java b/java
new file mode 160000
index 000000000..39b6df27b
--- /dev/null
+++ b/java
@@ -0,0 +1 @@
+Subproject commit 39b6df27b3d72c706010e6773d4bb50a202a1543
diff --git a/ziptools/zipsigner/.gitignore b/ziptools/zipsigner/.gitignore
deleted file mode 100644
index 87dc02017..000000000
--- a/ziptools/zipsigner/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-*.iml
-.gradle
-/local.properties
-.idea/
-/build
-*.hprof
-app/.externalNativeBuild/
diff --git a/ziptools/zipsigner/build.gradle b/ziptools/zipsigner/build.gradle
deleted file mode 100644
index 60879d23a..000000000
--- a/ziptools/zipsigner/build.gradle
+++ /dev/null
@@ -1,36 +0,0 @@
-group 'com.topjohnwu'
-version '1.0.0'
-
-apply plugin: 'java'
-apply plugin: 'com.github.johnrengelman.shadow'
-
-sourceCompatibility = 1.8
-
-jar {
- manifest {
- attributes 'Main-Class': 'com.topjohnwu.ZipSigner'
- }
-}
-
-shadowJar {
- classifier = null
- version = null
-}
-
-buildscript {
- repositories {
- jcenter()
- }
- dependencies {
- classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
- }
-}
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- compile 'org.bouncycastle:bcprov-jdk15on:1.57'
- compile 'org.bouncycastle:bcpkix-jdk15on:1.57'
-}
diff --git a/ziptools/zipsigner/gradle/wrapper/gradle-wrapper.jar b/ziptools/zipsigner/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index ccfe89381..000000000
Binary files a/ziptools/zipsigner/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/ziptools/zipsigner/gradle/wrapper/gradle-wrapper.properties b/ziptools/zipsigner/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 1d882d3e6..000000000
--- a/ziptools/zipsigner/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Thu Aug 24 10:35:40 CST 2017
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-rc-2-bin.zip
diff --git a/ziptools/zipsigner/gradlew b/ziptools/zipsigner/gradlew
deleted file mode 100755
index 4453ccea3..000000000
--- a/ziptools/zipsigner/gradlew
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env sh
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
- echo "$*"
-}
-
-die ( ) {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save ( ) {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
-exec "$JAVACMD" "$@"
diff --git a/ziptools/zipsigner/gradlew.bat b/ziptools/zipsigner/gradlew.bat
deleted file mode 100644
index f9553162f..000000000
--- a/ziptools/zipsigner/gradlew.bat
+++ /dev/null
@@ -1,84 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/ziptools/zipsigner/settings.gradle b/ziptools/zipsigner/settings.gradle
deleted file mode 100644
index a42023557..000000000
--- a/ziptools/zipsigner/settings.gradle
+++ /dev/null
@@ -1,4 +0,0 @@
-rootProject.name = 'zipsigner'
-include 'apksigner'
-rootProject.name = 'zipsigner'
-
diff --git a/ziptools/zipsigner/src/main/java/com/topjohnwu/ZipSigner.java b/ziptools/zipsigner/src/main/java/com/topjohnwu/ZipSigner.java
deleted file mode 100644
index bf7245314..000000000
--- a/ziptools/zipsigner/src/main/java/com/topjohnwu/ZipSigner.java
+++ /dev/null
@@ -1,567 +0,0 @@
-package com.topjohnwu;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.cert.jcajce.JcaCertStore;
-import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.CMSSignedDataGenerator;
-import org.bouncycastle.cms.CMSTypedData;
-import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
-import org.bouncycastle.util.encoders.Base64;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.security.DigestOutputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.MessageDigest;
-import java.security.PrivateKey;
-import java.security.Provider;
-import java.security.Security;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.jar.Attributes;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.regex.Pattern;
-
-/*
-* Modified from from AOSP(Marshmallow) SignAPK.java
-* */
-
-public class ZipSigner {
- private static final String CERT_SF_NAME = "META-INF/CERT.SF";
- private static final String CERT_SIG_NAME = "META-INF/CERT.%s";
-
- private static Provider sBouncyCastleProvider;
-
- // bitmasks for which hash algorithms we need the manifest to include.
- private static final int USE_SHA1 = 1;
- private static final int USE_SHA256 = 2;
-
- /**
- * Return one of USE_SHA1 or USE_SHA256 according to the signature
- * algorithm specified in the cert.
- */
- private static int getDigestAlgorithm(X509Certificate cert) {
- String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
- if ("SHA1WITHRSA".equals(sigAlg) ||
- "MD5WITHRSA".equals(sigAlg)) { // see "HISTORICAL NOTE" above.
- return USE_SHA1;
- } else if (sigAlg.startsWith("SHA256WITH")) {
- return USE_SHA256;
- } else {
- throw new IllegalArgumentException("unsupported signature algorithm \"" + sigAlg +
- "\" in cert [" + cert.getSubjectDN());
- }
- }
- /** Returns the expected signature algorithm for this key type. */
- private static String getSignatureAlgorithm(X509Certificate cert) {
- String sigAlg = cert.getSigAlgName().toUpperCase(Locale.US);
- String keyType = cert.getPublicKey().getAlgorithm().toUpperCase(Locale.US);
- if ("RSA".equalsIgnoreCase(keyType)) {
- if (getDigestAlgorithm(cert) == USE_SHA256) {
- return "SHA256withRSA";
- } else {
- return "SHA1withRSA";
- }
- } else if ("EC".equalsIgnoreCase(keyType)) {
- return "SHA256withECDSA";
- } else {
- throw new IllegalArgumentException("unsupported key type: " + keyType);
- }
- }
- // Files matching this pattern are not copied to the output.
- private static Pattern stripPattern =
- Pattern.compile("^(META-INF/((.*)[.](SF|RSA|DSA|EC)|com/android/otacert))|(" +
- Pattern.quote(JarFile.MANIFEST_NAME) + ")$");
- private static X509Certificate readPublicKey(InputStream input)
- throws IOException, GeneralSecurityException {
- try {
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- return (X509Certificate) cf.generateCertificate(input);
- } finally {
- input.close();
- }
- }
-
- /** Read a PKCS#8 format private key. */
- private static PrivateKey readPrivateKey(InputStream input)
- throws IOException, GeneralSecurityException {
- try {
- byte[] buffer = new byte[4096];
- int size = input.read(buffer);
- byte[] bytes = Arrays.copyOf(buffer, size);
- /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
- PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
- /*
- * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm
- * OID and use that to construct a KeyFactory.
- */
- ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
- PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
- String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
- return KeyFactory.getInstance(algOid).generatePrivate(spec);
- } finally {
- input.close();
- }
- }
- /**
- * Add the hash(es) of every file to the manifest, creating it if
- * necessary.
- */
- private static Manifest addDigestsToManifest(JarFile jar, int hashes)
- throws IOException, GeneralSecurityException {
- Manifest input = jar.getManifest();
- Manifest output = new Manifest();
- Attributes main = output.getMainAttributes();
- if (input != null) {
- main.putAll(input.getMainAttributes());
- } else {
- main.putValue("Manifest-Version", "1.0");
- main.putValue("Created-By", "1.0 (Android SignApk)");
- }
- MessageDigest md_sha1 = null;
- MessageDigest md_sha256 = null;
- if ((hashes & USE_SHA1) != 0) {
- md_sha1 = MessageDigest.getInstance("SHA1");
- }
- if ((hashes & USE_SHA256) != 0) {
- md_sha256 = MessageDigest.getInstance("SHA256");
- }
- byte[] buffer = new byte[4096];
- int num;
- // We sort the input entries by name, and add them to the
- // output manifest in sorted order. We expect that the output
- // map will be deterministic.
- TreeMap byName = new TreeMap();
- for (Enumeration e = jar.entries(); e.hasMoreElements(); ) {
- JarEntry entry = e.nextElement();
- byName.put(entry.getName(), entry);
- }
- for (JarEntry entry: byName.values()) {
- String name = entry.getName();
- if (!entry.isDirectory() &&
- (stripPattern == null || !stripPattern.matcher(name).matches())) {
- InputStream data = jar.getInputStream(entry);
- while ((num = data.read(buffer)) > 0) {
- if (md_sha1 != null) md_sha1.update(buffer, 0, num);
- if (md_sha256 != null) md_sha256.update(buffer, 0, num);
- }
- Attributes attr = null;
- if (input != null) attr = input.getAttributes(name);
- attr = attr != null ? new Attributes(attr) : new Attributes();
- if (md_sha1 != null) {
- attr.putValue("SHA1-Digest",
- new String(Base64.encode(md_sha1.digest()), "ASCII"));
- }
- if (md_sha256 != null) {
- attr.putValue("SHA-256-Digest",
- new String(Base64.encode(md_sha256.digest()), "ASCII"));
- }
- output.getEntries().put(name, attr);
- }
- }
- return output;
- }
-
- /** Write to another stream and track how many bytes have been
- * written.
- */
- private static class CountOutputStream extends FilterOutputStream {
- private int mCount;
- public CountOutputStream(OutputStream out) {
- super(out);
- mCount = 0;
- }
- @Override
- public void write(int b) throws IOException {
- super.write(b);
- mCount++;
- }
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- super.write(b, off, len);
- mCount += len;
- }
- public int size() {
- return mCount;
- }
- }
- /** Write a .SF file with a digest of the specified manifest. */
- private static void writeSignatureFile(Manifest manifest, OutputStream out,
- int hash)
- throws IOException, GeneralSecurityException {
- Manifest sf = new Manifest();
- Attributes main = sf.getMainAttributes();
- main.putValue("Signature-Version", "1.0");
- main.putValue("Created-By", "1.0 (Android SignApk)");
- MessageDigest md = MessageDigest.getInstance(
- hash == USE_SHA256 ? "SHA256" : "SHA1");
- PrintStream print = new PrintStream(
- new DigestOutputStream(new ByteArrayOutputStream(), md),
- true, "UTF-8");
- // Digest of the entire manifest
- manifest.write(print);
- print.flush();
- main.putValue(hash == USE_SHA256 ? "SHA-256-Digest-Manifest" : "SHA1-Digest-Manifest",
- new String(Base64.encode(md.digest()), "ASCII"));
- Map entries = manifest.getEntries();
- for (Map.Entry entry : entries.entrySet()) {
- // Digest of the manifest stanza for this entry.
- print.print("Name: " + entry.getKey() + "\r\n");
- for (Map.Entry