Update SignAPK to use less memory

This commit is contained in:
topjohnwu 2017-12-01 11:19:38 +08:00
parent c1dd23f5e0
commit 2c69e2c151
4 changed files with 34 additions and 57 deletions

View File

@ -8,7 +8,7 @@ android {
applicationId "com.topjohnwu.magisk" applicationId "com.topjohnwu.magisk"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 27 targetSdkVersion 27
versionCode 64 versionCode 66
versionName "5.4.3" versionName "5.4.3"
ndk { ndk {
moduleName 'zipadjust' moduleName 'zipadjust'

View File

@ -15,7 +15,7 @@ jar {
shadowJar { shadowJar {
baseName = 'zipsigner' baseName = 'zipsigner'
classifier = null classifier = null
version = 1.0 version = 1.1
} }
buildscript { buildscript {

View File

@ -2,8 +2,6 @@ package com.topjohnwu.crypto;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -28,10 +26,8 @@ import java.util.zip.ZipFile;
public class JarMap implements Closeable, AutoCloseable { public class JarMap implements Closeable, AutoCloseable {
private JarFile jarFile; private JarFile jarFile;
private JarInputStream jis; private JarInputStream jis;
private InputStream is; private boolean isInputStream = false;
private File file; private LinkedHashMap<String, JarEntry> bufMap;
private boolean isInputStream = false, hasLoaded = false, verify;
private LinkedHashMap<String, JarEntry> bufMap = new LinkedHashMap<>();
public JarMap(File file) throws IOException { public JarMap(File file) throws IOException {
this(file, true); this(file, true);
@ -42,7 +38,6 @@ public class JarMap implements Closeable, AutoCloseable {
} }
public JarMap(File file, boolean verify, int mode) throws IOException { public JarMap(File file, boolean verify, int mode) throws IOException {
this.file = file;
jarFile = new JarFile(file, verify, mode); jarFile = new JarFile(file, verify, mode);
} }
@ -60,47 +55,30 @@ public class JarMap implements Closeable, AutoCloseable {
public JarMap(InputStream is, boolean verify) throws IOException { public JarMap(InputStream is, boolean verify) throws IOException {
isInputStream = true; isInputStream = true;
this.is = is; bufMap = new LinkedHashMap<>();
this.verify = verify;
}
private void loadJarInputStream() {
if (!isInputStream || hasLoaded) return;
hasLoaded = true;
JarEntry entry;
try {
jis = new JarInputStream(is, verify); jis = new JarInputStream(is, verify);
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) { while ((entry = jis.getNextJarEntry()) != null) {
bufMap.put(entry.getName(), new JarMapEntry(entry, jis)); bufMap.put(entry.getName(), new JarMapEntry(entry, jis));
} }
} catch (IOException e) {
e.printStackTrace();
}
} }
public InputStream getInputStream() { public File getFile() {
try { return isInputStream ? null : new File(jarFile.getName());
return isInputStream ? is : new FileInputStream(file);
} catch (FileNotFoundException e) {
return null;
}
} }
public Manifest getManifest() throws IOException { public Manifest getManifest() throws IOException {
loadJarInputStream();
return isInputStream ? jis.getManifest() : jarFile.getManifest(); return isInputStream ? jis.getManifest() : jarFile.getManifest();
} }
public InputStream getInputStream(ZipEntry ze) throws IOException { public InputStream getInputStream(ZipEntry ze) throws IOException {
loadJarInputStream(); return isInputStream ? ((JarMapEntry) bufMap.get(ze.getName())).data.getInputStream() :
return isInputStream ? ((JarMapEntry) bufMap.get(ze.getName())).getInputStream() :
jarFile.getInputStream(ze); jarFile.getInputStream(ze);
} }
public OutputStream getOutputStream(ZipEntry ze) { public OutputStream getOutputStream(ZipEntry ze) {
if (!isInputStream) // Only support inputstream mode if (!isInputStream) // Only support InputStream mode
return null; return null;
loadJarInputStream();
ByteArrayStream bs = ((JarMapEntry) bufMap.get(ze.getName())).data; ByteArrayStream bs = ((JarMapEntry) bufMap.get(ze.getName())).data;
bs.reset(); bs.reset();
return bs; return bs;
@ -108,7 +86,6 @@ public class JarMap implements Closeable, AutoCloseable {
public byte[] getRawData(ZipEntry ze) throws IOException { public byte[] getRawData(ZipEntry ze) throws IOException {
if (isInputStream) { if (isInputStream) {
loadJarInputStream();
return ((JarMapEntry) bufMap.get(ze.getName())).data.toByteArray(); return ((JarMapEntry) bufMap.get(ze.getName())).data.toByteArray();
} else { } else {
ByteArrayStream bytes = new ByteArrayStream(); ByteArrayStream bytes = new ByteArrayStream();
@ -118,7 +95,6 @@ public class JarMap implements Closeable, AutoCloseable {
} }
public Enumeration<JarEntry> entries() { public Enumeration<JarEntry> entries() {
loadJarInputStream();
return isInputStream ? Collections.enumeration(bufMap.values()) : jarFile.entries(); return isInputStream ? Collections.enumeration(bufMap.values()) : jarFile.entries();
} }
@ -127,16 +103,12 @@ public class JarMap implements Closeable, AutoCloseable {
} }
public JarEntry getJarEntry(String name) { public JarEntry getJarEntry(String name) {
loadJarInputStream();
return isInputStream ? bufMap.get(name) : jarFile.getJarEntry(name); return isInputStream ? bufMap.get(name) : jarFile.getJarEntry(name);
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if (isInputStream) (isInputStream ? jis : jarFile).close();
is.close();
else
jarFile.close();
} }
private static class JarMapEntry extends JarEntry { private static class JarMapEntry extends JarEntry {
@ -146,8 +118,5 @@ public class JarMap implements Closeable, AutoCloseable {
data = new ByteArrayStream(); data = new ByteArrayStream();
data.readFrom(is); data.readFrom(is);
} }
InputStream getInputStream() {
return data.getInputStream();
}
} }
} }

View File

@ -21,12 +21,14 @@ import org.bouncycastle.util.encoders.Base64;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FilterOutputStream; import java.io.FilterOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.security.DigestOutputStream; import java.security.DigestOutputStream;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.MessageDigest; import java.security.MessageDigest;
@ -36,7 +38,6 @@ import java.security.Security;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Locale; import java.util.Locale;
@ -84,7 +85,7 @@ public class SignAPK {
outputFile = new BufferedOutputStream(new FileOutputStream(output)); outputFile = new BufferedOutputStream(new FileOutputStream(output));
if (minSign) { if (minSign) {
signWholeFile(input.getInputStream(), publicKey, privateKey, outputFile); signWholeFile(input.getFile(), publicKey, privateKey, outputFile);
} else { } else {
JarOutputStream outputJar = new JarOutputStream(outputFile); JarOutputStream outputJar = new JarOutputStream(outputFile);
// For signing .apks, use the maximum compression to make // For signing .apks, use the maximum compression to make
@ -373,15 +374,12 @@ public class SignAPK {
// Used for signWholeFile // Used for signWholeFile
private static class CMSProcessableFile implements CMSTypedData { private static class CMSProcessableFile implements CMSTypedData {
private InputStream is;
private ASN1ObjectIdentifier type; private ASN1ObjectIdentifier type;
ByteArrayStream bos; private RandomAccessFile file;
CMSProcessableFile(InputStream is) { CMSProcessableFile(File file) throws FileNotFoundException {
this.is = is; this.file = new RandomAccessFile(file, "r");
type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()); type = new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId());
bos = new ByteArrayStream();
bos.readFrom(is);
} }
@Override @Override
@ -391,20 +389,30 @@ public class SignAPK {
@Override @Override
public void write(OutputStream out) throws IOException, CMSException { public void write(OutputStream out) throws IOException, CMSException {
bos.writeTo(out, 0, bos.size() - 2); file.seek(0);
int read;
byte buffer[] = new byte[4096];
int len = (int) file.length() - 2;
while ((read = file.read(buffer, 0, len < buffer.length ? len : buffer.length)) > 0) {
out.write(buffer, 0, read);
len -= read;
}
} }
@Override @Override
public Object getContent() { public Object getContent() {
return is; return file;
} }
byte[] getTail() { byte[] getTail() throws IOException {
return Arrays.copyOfRange(bos.getBuf(), bos.size() - 22, bos.size()); byte tail[] = new byte[22];
file.seek(file.length() - 22);
file.readFully(tail);
return tail;
} }
} }
private static void signWholeFile(InputStream input, X509Certificate publicKey, private static void signWholeFile(File input, X509Certificate publicKey,
PrivateKey privateKey, OutputStream outputStream) PrivateKey privateKey, OutputStream outputStream)
throws Exception { throws Exception {
ByteArrayOutputStream temp = new ByteArrayOutputStream(); ByteArrayOutputStream temp = new ByteArrayOutputStream();