Merge remote-tracking branch 'john/master' into development

# Conflicts:
#	app/build.gradle
#	app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt
#	app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt
#	app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
This commit is contained in:
Viktor De Pasquale 2019-05-13 16:50:08 +02:00
commit 80855e89ec
22 changed files with 147 additions and 100 deletions

View File

@ -65,7 +65,7 @@ dependencies {
def vRoom = "2.1.0-alpha05"
implementation "androidx.room:room-rxjava2:${vRoom}"
def markwonVersion = '3.0.0'
def markwonVersion = '3.0.1'
implementation "ru.noties.markwon:core:${markwonVersion}"
implementation "ru.noties.markwon:html:${markwonVersion}"
implementation "ru.noties.markwon:image-svg:${markwonVersion}"
@ -97,13 +97,12 @@ dependencies {
implementation "com.chibatching.kotpref:kotpref:${vKotpref}"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.browser:browser:1.0.0'
implementation 'androidx.preference:preference:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha04'
implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha05'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.google.android.material:material:1.1.0-alpha05'
implementation 'com.google.android.material:material:1.1.0-alpha06'
implementation 'androidx.work:work-runtime:2.0.1'
implementation 'androidx.transition:transition:1.1.0-beta01'
implementation 'androidx.transition:transition:1.2.0-alpha01'
implementation 'androidx.multidex:multidex:2.0.1'
}

View File

@ -16,12 +16,6 @@
# public *;
#}
# BouncyCastle
-keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
-keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
-keep,allowoptimization class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
-dontwarn javax.naming.**
# Snet
-keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; }
-keep,allowobfuscation interface com.topjohnwu.magisk.utils.ISafetyNetHelper$Callback
@ -40,12 +34,6 @@
# BootSigner
-keepclassmembers class com.topjohnwu.signing.BootSigner { *; }
# SVG
-dontwarn com.caverock.androidsvg.SVGAndroidRenderer
# RetroStreams
-dontwarn java9.**
# Strip logging
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
public *** debug(...);

View File

@ -10,8 +10,10 @@ import com.topjohnwu.magisk.data.database.base.su
import com.topjohnwu.magisk.data.repository.AppRepository
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.magisk.utils.DownloadApp
import com.topjohnwu.magisk.utils.RootUtils
import com.topjohnwu.magisk.utils.SuLogger
import com.topjohnwu.magisk.utils.inject
import com.topjohnwu.magisk.utils.get
import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell
@ -20,8 +22,15 @@ open class GeneralReceiver : BroadcastReceiver() {
private val appRepo: AppRepository by inject()
private fun getPkg(i: Intent): String {
return if (i.data == null) "" else i.data!!.encodedSchemeSpecificPart
companion object {
const val REQUEST = "request"
const val LOG = "log"
const val NOTIFY = "notify"
const val TEST = "test"
}
private fun getPkg(intent: Intent): String {
return intent.data?.encodedSchemeSpecificPart ?: ""
}
override fun onReceive(context: Context, intent: Intent?) {
@ -40,16 +49,17 @@ open class GeneralReceiver : BroadcastReceiver() {
return
}
when (action) {
SuRequestActivity.REQUEST -> {
REQUEST -> {
val i = Intent(context, ClassMap[SuRequestActivity::class.java])
.setAction(action)
.putExtra("socket", intent.getStringExtra("socket"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
.setAction(action)
.putExtra("socket", intent.getStringExtra("socket"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
context.startActivity(i)
}
SuRequestActivity.LOG -> SuLogger.handleLogs(intent)
SuRequestActivity.NOTIFY -> SuLogger.handleNotify(intent)
LOG -> SuLogger.handleLogs(intent)
NOTIFY -> SuLogger.handleNotify(intent)
TEST -> Shell.su("magisk --use-broadcast").submit()
}
}
Intent.ACTION_PACKAGE_REPLACED ->
@ -67,7 +77,7 @@ open class GeneralReceiver : BroadcastReceiver() {
Config.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK)
DownloadApp.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME))
}
Const.Key.BROADCAST_REBOOT -> Shell.su("/system/bin/reboot").submit()
Const.Key.BROADCAST_REBOOT -> RootUtils.reboot()
}
}
}

View File

@ -69,7 +69,7 @@ class HomeViewModel(
""
}
val safetyNetTitle = KObservableField(R.string.safetyNet_check_text.res())
val safetyNetTitle = KObservableField(R.string.safetyNet_check_text)
val ctsState = KObservableField(SafetyNetState.IDLE)
val basicIntegrityState = KObservableField(SafetyNetState.IDLE)
val safetyNetState = Observer(ctsState, basicIntegrityState) {
@ -121,7 +121,7 @@ class HomeViewModel(
fun safetyNetPressed() {
ctsState.value = SafetyNetState.LOADING
basicIntegrityState.value = SafetyNetState.LOADING
safetyNetTitle.value = R.string.checking_safetyNet_status.res()
safetyNetTitle.value = R.string.checking_safetyNet_status
UpdateSafetyNetEvent().publish()
}
@ -130,7 +130,7 @@ class HomeViewModel(
response and 0x0F == 0 -> {
val hasCtsPassed = response and ISafetyNetHelper.CTS_PASS != 0
val hasBasicIntegrityPassed = response and ISafetyNetHelper.BASIC_PASS != 0
safetyNetTitle.value = R.string.safetyNet_check_success.res()
safetyNetTitle.value = R.string.safetyNet_check_success
ctsState.value = if (hasCtsPassed) {
SafetyNetState.PASS
} else {
@ -152,7 +152,7 @@ class HomeViewModel(
safetyNetTitle.value = when (response) {
ISafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid
else -> R.string.safetyNet_api_error
}.res()
}
}
}
@ -162,6 +162,9 @@ class HomeViewModel(
.doOnSubscribeUi {
magiskState.value = MagiskState.LOADING
managerState.value = MagiskState.LOADING
ctsState.value = SafetyNetState.IDLE
basicIntegrityState.value = SafetyNetState.IDLE
safetyNetTitle.value = R.string.safetyNet_check_text
}
.subscribeK {
it.app.let {

View File

@ -10,6 +10,7 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ActivityRequestBinding
import com.topjohnwu.magisk.model.entity.Policy
import com.topjohnwu.magisk.model.events.DieEvent
import com.topjohnwu.magisk.model.receiver.GeneralReceiver
import com.topjohnwu.magisk.ui.base.MagiskActivity
import com.topjohnwu.magisk.utils.SuLogger
import org.koin.androidx.viewmodel.ext.android.viewModel
@ -31,15 +32,15 @@ open class SuRequestActivity : MagiskActivity<SuRequestViewModel, ActivityReques
val intent = intent
val action = intent.action
if (TextUtils.equals(action, REQUEST)) {
if (TextUtils.equals(action, GeneralReceiver.REQUEST)) {
if (!viewModel.handleRequest(intent) {})
finish()
return
}
if (TextUtils.equals(action, LOG))
if (TextUtils.equals(action, GeneralReceiver.LOG))
SuLogger.handleLogs(intent)
else if (TextUtils.equals(action, NOTIFY))
else if (TextUtils.equals(action, GeneralReceiver.NOTIFY))
SuLogger.handleNotify(intent)
finish()
@ -58,10 +59,4 @@ open class SuRequestActivity : MagiskActivity<SuRequestViewModel, ActivityReques
else
ActivityInfo.SCREEN_ORIENTATION_LOCKED
}
companion object {
const val REQUEST = "request"
const val LOG = "log"
const val NOTIFY = "notify"
}
}

View File

@ -15,6 +15,8 @@
<import type="com.topjohnwu.magisk.R" />
<import type="com.topjohnwu.magisk.utils.XStringKt" />
<variable
name="viewModel"
type="com.topjohnwu.magisk.ui.home.HomeViewModel" />
@ -225,7 +227,7 @@
android:layout_marginRight="@dimen/margin_generic"
android:gravity="center"
android:maxLines="1"
android:text="@{viewModel.safetyNetTitle}"
android:text="@{XStringKt.res(viewModel.safetyNetTitle)}"
android:textStyle="bold"
app:autoSizeMaxTextSize="14sp"
app:autoSizeTextType="uniform"

View File

@ -1,9 +1,12 @@
mount_partitions() {
[ "`getprop ro.build.ab_update`" = "true" ] && SLOT=`getprop ro.boot.slot_suffix` || SLOT=
[ "`getprop ro.build.ab_update`" = "true" ] && SLOT=`getprop ro.boot.slot_suffix`
[ "`getprop ro.build.system_root_image`" = "true" ] && SYSTEM_ROOT=true || SYSTEM_ROOT=false
}
get_flags() {
$SYSTEM_ROOT && KEEPVERITY=true || KEEPVERITY=false
[ "`getprop ro.crypto.state`" = "encrypted" ] && KEEPFORCEENCRYPT=true || KEEPFORCEENCRYPT=false
RECOVERYMODE=false
}
run_migrations() { return; }

View File

@ -732,4 +732,7 @@ void boot_complete(int client) {
install_apk("/data/magisk.apk");
}
}
// Test whether broadcast can be used or not
broadcast_test();
}

View File

@ -54,6 +54,7 @@ static void *request_handler(void *args) {
case LATE_START:
case BOOT_COMPLETE:
case SQLITE_CMD:
case BROADCAST_ACK:
if (credential.uid != 0) {
write_int(client, ROOT_REQUIRED);
close(client);
@ -90,6 +91,10 @@ static void *request_handler(void *args) {
case SQLITE_CMD:
exec_sql(client);
break;
case BROADCAST_ACK:
LOGD("* Use broadcasts for su logging and notify\n");
CONNECT_BROADCAST = true;
close(client);
default:
close(client);
break;

View File

@ -233,7 +233,7 @@ int get_db_strings(db_strings &str, int key) {
return 0;
}
int get_uid_policy(int uid, su_access &su) {
int get_uid_policy(su_access &su, int uid) {
char query[256], *err;
sprintf(query, "SELECT policy, logging, notification FROM policies "
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(nullptr));

View File

@ -12,6 +12,8 @@
#include <db.h>
#include <flags.h>
using namespace std::literals;
[[noreturn]] static void usage() {
fprintf(stderr,
FULL_VER(Magisk) " multi-call binary\n"
@ -33,6 +35,7 @@
" --clone-attr SRC DEST clone permission, owner, and selinux context\n"
" --clone SRC DEST clone SRC to DEST\n"
" --sqlite SQL exec SQL to Magisk database\n"
" --use-broadcast use broadcast for su logging and notify\n"
"\n"
"Supported init triggers:\n"
" post-fs-data, service, boot-complete\n"
@ -48,66 +51,70 @@
int magisk_main(int argc, char *argv[]) {
if (argc < 2)
usage();
if (strcmp(argv[1], "-c") == 0) {
if (argv[1] == "-c"sv) {
printf(MAGISK_VERSION ":MAGISK (" str(MAGISK_VER_CODE) ")\n");
return 0;
} else if (strcmp(argv[1], "-v") == 0) {
} else if (argv[1] == "-v"sv) {
int fd = connect_daemon();
write_int(fd, CHECK_VERSION);
char *v = read_string(fd);
printf("%s\n", v);
free(v);
return 0;
} else if (strcmp(argv[1], "-V") == 0) {
} else if (argv[1] == "-V"sv) {
int fd = connect_daemon();
write_int(fd, CHECK_VERSION_CODE);
printf("%d\n", read_int(fd));
return 0;
} else if (strcmp(argv[1], "--list") == 0) {
} else if (argv[1] == "--list"sv) {
for (int i = 0; applet_names[i]; ++i)
printf("%s\n", applet_names[i]);
return 0;
} else if (strcmp(argv[1], "--unlock-blocks") == 0) {
} else if (argv[1] == "--unlock-blocks"sv) {
unlock_blocks();
return 0;
} else if (strcmp(argv[1], "--restorecon") == 0) {
} else if (argv[1] == "--restorecon"sv) {
restore_rootcon();
restorecon();
return 0;
} else if (strcmp(argv[1], "--clone-attr") == 0) {
} else if (argv[1] == "--clone-attr"sv) {
if (argc < 4) usage();
clone_attr(argv[2], argv[3]);
return 0;
} else if (strcmp(argv[1], "--clone") == 0) {
} else if (argv[1] == "--clone"sv) {
if (argc < 4) usage();
cp_afc(argv[2], argv[3]);
return 0;
} else if (strcmp(argv[1], "--daemon") == 0) {
} else if (argv[1] == "--daemon"sv) {
int fd = connect_daemon(true);
write_int(fd, DO_NOTHING);
return 0;
} else if (strcmp(argv[1], "--post-fs-data") == 0) {
} else if (argv[1] == "--post-fs-data"sv) {
int fd = connect_daemon(true);
write_int(fd, POST_FS_DATA);
return read_int(fd);
} else if (strcmp(argv[1], "--service") == 0) {
} else if (argv[1] == "--service"sv) {
int fd = connect_daemon(true);
write_int(fd, LATE_START);
return read_int(fd);
} else if (strcmp(argv[1], "--boot-complete") == 0) {
} else if (argv[1] == "--boot-complete"sv) {
int fd = connect_daemon(true);
write_int(fd, BOOT_COMPLETE);
return read_int(fd);
} else if (strcmp(argv[1], "--sqlite") == 0) {
} else if (argv[1] == "--sqlite"sv) {
int fd = connect_daemon();
write_int(fd, SQLITE_CMD);
write_string(fd, argv[2]);
send_fd(fd, STDOUT_FILENO);
return read_int(fd);
} else if (argv[1] == "--use-broadcast"sv) {
int fd = connect_daemon();
write_int(fd, BROADCAST_ACK);
return 0;
}
#if 0
/* Entry point for testing stuffs */
else if (strcmp(argv[1], "--test") == 0) {
else if (argv[1] == "--test"sv) {
return 0;
}
#endif

View File

@ -17,7 +17,7 @@ enum {
BOOT_COMPLETE,
MAGISKHIDE,
SQLITE_CMD,
ZYGOTE_NOTIFY,
BROADCAST_ACK,
};
// Return codes for daemon
@ -82,6 +82,8 @@ void magiskhide_handler(int client);
*************/
void su_daemon_handler(int client, struct ucred *credential);
void broadcast_test();
extern int SDK_INT;
extern bool RECOVERY_MODE;
extern bool CONNECT_BROADCAST;

View File

@ -154,7 +154,7 @@ typedef std::function<bool(db_row&)> db_row_cb;
int get_db_settings(db_settings &cfg, int key = -1);
int get_db_strings(db_strings &str, int key = -1);
int get_uid_policy(int uid, su_access &su);
int get_uid_policy(su_access &su, int uid);
int validate_manager(std::string &alt_pkg, int userid, struct stat *st);
void exec_sql(int client);
char *db_exec(const char *sql);

View File

@ -11,12 +11,21 @@
#include "su.h"
bool CONNECT_BROADCAST;
#define START_ACTIVITY \
"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \
"start", "-n", nullptr, "--user", nullptr, "-f", "0x18000020", "-a"
// 0x18000020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_INCLUDE_STOPPED_PACKAGES
#define START_BROADCAST \
"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \
"broadcast", "-n", nullptr, "--user", nullptr, "-f", "0x00000020", \
"-a", "android.intent.action.REBOOT", "--es", "action"
// 0x00000020 = FLAG_INCLUDE_STOPPED_PACKAGES
static inline const char *get_command(const su_request *to) {
if (to->command[0])
return to->command;
@ -39,9 +48,9 @@ static inline void get_uid(char *uid, su_info *info) {
: info->uid);
}
static void silent_run(const char **args, su_info *info) {
static void exec_am_cmd(const char **args, su_info *info) {
char component[128];
sprintf(component, "%s/a.m", info->str[SU_MANAGER].data());
sprintf(component, "%s/%s", info->str[SU_MANAGER].data(), args[3][0] == 'b' ? "a.h" : "a.m");
char user[8];
get_user(user, info);
@ -62,6 +71,16 @@ static void silent_run(const char **args, su_info *info) {
exec_command(exec);
}
#define LOG_BODY \
"log", \
"--ei", "from.uid", fromUid, \
"--ei", "to.uid", toUid, \
"--ei", "pid", pid, \
"--ei", "policy", policy, \
"--es", "command", get_command(&ctx->req), \
"--ez", "notify", ctx->info->access.notify ? "true" : "false", \
nullptr
void app_log(su_context *ctx) {
char fromUid[8];
get_uid(fromUid, ctx->info);
@ -75,19 +94,21 @@ void app_log(su_context *ctx) {
char policy[2];
sprintf(policy, "%d", ctx->info->access.policy);
const char *cmd[] = {
START_ACTIVITY, "log",
"--ei", "from.uid", fromUid,
"--ei", "to.uid", toUid,
"--ei", "pid", pid,
"--ei", "policy", policy,
"--es", "command", get_command(&ctx->req),
"--ez", "notify", ctx->info->access.notify ? "true" : "false",
nullptr
};
silent_run(cmd, ctx->info);
if (CONNECT_BROADCAST) {
const char *cmd[] = { START_BROADCAST, LOG_BODY };
exec_am_cmd(cmd, ctx->info);
} else {
const char *cmd[] = { START_ACTIVITY, LOG_BODY };
exec_am_cmd(cmd, ctx->info);
}
}
#define NOTIFY_BODY \
"notify", \
"--ei", "from.uid", fromUid, \
"--ei", "policy", policy, \
nullptr
void app_notify(su_context *ctx) {
char fromUid[8];
get_uid(fromUid, ctx->info);
@ -95,13 +116,14 @@ void app_notify(su_context *ctx) {
char policy[2];
sprintf(policy, "%d", ctx->info->access.policy);
const char *cmd[] = {
START_ACTIVITY, "notify",
"--ei", "from.uid", fromUid,
"--ei", "policy", policy,
nullptr
};
silent_run(cmd, ctx->info);
if (CONNECT_BROADCAST) {
const char *cmd[] = { START_BROADCAST, NOTIFY_BODY };
exec_am_cmd(cmd, ctx->info);
} else {
const char *cmd[] = { START_ACTIVITY, NOTIFY_BODY };
exec_am_cmd(cmd, ctx->info);
}
}
void app_connect(const char *socket, su_info *info) {
@ -110,7 +132,17 @@ void app_connect(const char *socket, su_info *info) {
"--es", "socket", socket,
nullptr
};
silent_run(cmd, info);
exec_am_cmd(cmd, info);
}
void broadcast_test() {
su_info info;
get_db_settings(info.cfg);
get_db_strings(info.str);
validate_manager(info.str[SU_MANAGER], 0, &info.mgr_st);
const char *cmd[] = { START_BROADCAST, "test", nullptr };
exec_am_cmd(cmd, &info);
}
void socket_send_request(int fd, su_info *info) {

View File

@ -32,7 +32,7 @@ public:
int ref;
time_t timestamp;
su_info(unsigned uid);
su_info(unsigned uid = 0);
~su_info();
void lock();
void unlock();

View File

@ -83,7 +83,7 @@ static void database_check(su_info *info) {
}
if (uid > 0)
get_uid_policy(uid, info->access);
get_uid_policy(info->access, uid);
// We need to check our manager
if (info->access.log || info->access.notify)

View File

@ -44,7 +44,7 @@ public class Networking {
gms.getClassLoader()
.loadClass("com.google.android.gms.common.security.ProviderInstallerImpl")
.getMethod("insertProvider", Context.class)
.invoke(null, context);
.invoke(null, gms);
} catch (Exception e) {
// Failed to update SSL provider, use NoSSLv3SocketFactory on SDK < 21
if (Build.VERSION.SDK_INT < 21)

View File

@ -33,6 +33,8 @@ repositories {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'org.bouncycastle:bcprov-jdk15on:1.60'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.60'
def bcVer = '1.61'
api "org.bouncycastle:bcprov-jdk15on:${bcVer}"
api "org.bouncycastle:bcpkix-jdk15on:${bcVer}"
}

View File

@ -23,7 +23,7 @@ import java.util.zip.ZipFile;
* On the other hand, when a JarFile is provided, it simply works as a wrapper.
* */
public class JarMap implements Closeable, AutoCloseable {
public class JarMap implements Closeable {
private JarFile jarFile;
private JarInputStream jis;
@ -119,7 +119,10 @@ public class JarMap implements Closeable, AutoCloseable {
@Override
public void close() throws IOException {
(jarFile == null ? jis : jarFile).close();
if (jarFile != null)
jarFile.close();
else
jis.close();
}
private static class JarMapEntry extends JarEntry {

View File

@ -11,7 +11,6 @@ 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;
@ -60,16 +59,11 @@ public class SignAPK {
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;
private static Provider sBouncyCastleProvider = Security.getProvider("BC");
// 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;
static {
sBouncyCastleProvider = new BouncyCastleProvider();
Security.insertProviderAt(sBouncyCastleProvider, 1);
}
public static void sign(JarMap input, OutputStream output) throws Exception {
sign(SignAPK.class.getResourceAsStream("/keys/testkey.x509.pem"),
SignAPK.class.getResourceAsStream("/keys/testkey.pk8"), input, output);

View File

@ -12,7 +12,6 @@ import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
@ -23,7 +22,6 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
@ -32,10 +30,6 @@ import java.util.Arrays;
public class SignBoot {
static {
Security.addProvider(new BouncyCastleProvider());
}
private static class PushBackRWStream extends FilterInputStream {
private OutputStream out;
private int pos = 0;

View File

@ -1,9 +1,12 @@
package com.topjohnwu.signing;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Security;
public class ZipSigner {
@ -22,6 +25,8 @@ public class ZipSigner {
if (args.length != 2 && args.length != 4 && args.length != 6)
usage();
Security.insertProviderAt(new BouncyCastleProvider(), 1);
try (JarMap in = new JarMap(args[args.length - 2], false);
OutputStream out = new FileOutputStream(args[args.length - 1])) {
if (args.length == 2) {