You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Port zipadjust to Java
This commit is contained in:
+1
-1
@@ -15,7 +15,7 @@ jar {
|
||||
shadowJar {
|
||||
baseName = 'zipsigner'
|
||||
classifier = null
|
||||
version = 2.0
|
||||
version = 2.1
|
||||
}
|
||||
|
||||
buildscript {
|
||||
|
||||
@@ -18,9 +18,13 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -67,6 +71,26 @@ public class SignAPK {
|
||||
Security.insertProviderAt(sBouncyCastleProvider, 1);
|
||||
}
|
||||
|
||||
public static void signZip(InputStream cert, InputStream key,
|
||||
JarMap input, OutputStream output) throws Exception {
|
||||
File temp1 = File.createTempFile("signAPK", null);
|
||||
File temp2 = File.createTempFile("signAPK", null);
|
||||
try {
|
||||
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))) {
|
||||
signZip(cert, key, input, out, false);
|
||||
}
|
||||
|
||||
ZipAdjust.adjust(temp1, temp2);
|
||||
|
||||
try (JarMap map = new JarMap(temp2, false)) {
|
||||
signZip(cert, key, map, output, true);
|
||||
}
|
||||
} finally {
|
||||
temp1.delete();
|
||||
temp2.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static void signZip(InputStream cert, InputStream key,
|
||||
JarMap input, OutputStream output, boolean minSign) throws Exception {
|
||||
int alignment = 4;
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
package com.topjohnwu.crypto;
|
||||
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ZipAdjust {
|
||||
|
||||
public static void adjust(File input, File output) throws IOException {
|
||||
try (
|
||||
RandomAccessFile in = new RandomAccessFile(input, "r");
|
||||
FileOutputStream out = new FileOutputStream(output)
|
||||
) {
|
||||
adjust(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
public static void adjust(RandomAccessFile in, OutputStream out) throws IOException {
|
||||
|
||||
CentralFooter footer = new CentralFooter(in);
|
||||
int outOff = 0;
|
||||
long centralOff = unsigned(footer.centralDirectoryOffset);
|
||||
CentralHeader[] centralHeaders = new CentralHeader[unsigned(footer.numEntries)];
|
||||
|
||||
// Loop through central directory entries
|
||||
for (int i = 0; i < centralHeaders.length; ++i) {
|
||||
// Read central header
|
||||
in.seek(centralOff);
|
||||
centralHeaders[i] = new CentralHeader(in);
|
||||
centralOff = in.getFilePointer();
|
||||
|
||||
// Read local header
|
||||
in.seek(unsigned(centralHeaders[i].localHeaderOffset));
|
||||
LocalHeader localHeader = new LocalHeader(in);
|
||||
|
||||
// Make sure local and central headers matches, and strip out data descriptor flag
|
||||
centralHeaders[i].localHeaderOffset = outOff;
|
||||
centralHeaders[i].flags &= ~(1 << 3);
|
||||
localHeader.flags = centralHeaders[i].flags;
|
||||
localHeader.crc32 = centralHeaders[i].crc32;
|
||||
localHeader.compressedSize = centralHeaders[i].compressedSize;
|
||||
localHeader.uncompressedSize = centralHeaders[i].uncompressedSize;
|
||||
localHeader.fileNameLength = centralHeaders[i].fileNameLength;
|
||||
localHeader.filename = centralHeaders[i].filename;
|
||||
|
||||
// Write local header
|
||||
outOff += localHeader.write(out);
|
||||
|
||||
// Copy data
|
||||
int read;
|
||||
long len = unsigned(localHeader.compressedSize);
|
||||
outOff += len;
|
||||
byte data[] = new byte[4096];
|
||||
while ((read = in.read(data, 0,
|
||||
len < data.length ? (int) len : data.length)) > 0) {
|
||||
out.write(data, 0, read);
|
||||
len -= read;
|
||||
}
|
||||
}
|
||||
|
||||
footer.centralDirectoryOffset = outOff;
|
||||
|
||||
// Write central directory
|
||||
outOff = 0;
|
||||
for (CentralHeader header : centralHeaders)
|
||||
outOff += header.write(out);
|
||||
|
||||
// Write central directory record
|
||||
footer.centralDirectorySize = outOff;
|
||||
footer.write(out);
|
||||
}
|
||||
|
||||
public static short unsigned(byte n) {
|
||||
return (short)(n & 0xff);
|
||||
}
|
||||
|
||||
public static int unsigned(short n) {
|
||||
return n & 0xffff;
|
||||
}
|
||||
|
||||
public static long unsigned(int n) {
|
||||
return n & 0xffffffffL;
|
||||
}
|
||||
|
||||
public static class CentralFooter {
|
||||
|
||||
static final int MAGIC = 0x06054b50;
|
||||
|
||||
int signature;
|
||||
short diskNumber;
|
||||
short centralDirectoryDiskNumber;
|
||||
short numEntriesThisDisk;
|
||||
short numEntries;
|
||||
int centralDirectorySize;
|
||||
int centralDirectoryOffset;
|
||||
short zipCommentLength;
|
||||
// byte[] comments;
|
||||
|
||||
CentralFooter(RandomAccessFile file) throws IOException {
|
||||
byte[] buffer = new byte[22];
|
||||
for (long i = file.length() - 4; i >= 0; --i) {
|
||||
file.seek(i);
|
||||
file.read(buffer, 0 ,4);
|
||||
ByteBuffer buf = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||
signature = buf.getInt();
|
||||
if (signature != MAGIC) {
|
||||
continue;
|
||||
}
|
||||
file.read(buffer, 4, buffer.length - 4);
|
||||
diskNumber = buf.getShort();
|
||||
centralDirectoryDiskNumber = buf.getShort();
|
||||
numEntriesThisDisk = buf.getShort();
|
||||
numEntries = buf.getShort();
|
||||
centralDirectorySize = buf.getInt();
|
||||
centralDirectoryOffset = buf.getInt();
|
||||
zipCommentLength = buf.getShort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int write(OutputStream out) throws IOException {
|
||||
byte[] buffer = new byte[22];
|
||||
ByteBuffer buf = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||
buf.putInt(signature);
|
||||
buf.putShort(diskNumber);
|
||||
buf.putShort(centralDirectoryDiskNumber);
|
||||
buf.putShort(numEntriesThisDisk);
|
||||
buf.putShort(numEntries);
|
||||
buf.putInt(centralDirectorySize);
|
||||
buf.putInt(centralDirectoryOffset);
|
||||
buf.putShort((short) 0); // zipCommentLength
|
||||
out.write(buffer);
|
||||
return buffer.length;
|
||||
}
|
||||
}
|
||||
|
||||
static class CentralHeader {
|
||||
|
||||
static final int MAGIC = 0x02014b50;
|
||||
|
||||
int signature;
|
||||
short versionMadeBy;
|
||||
short versionNeededToExtract;
|
||||
short flags;
|
||||
short compressionMethod;
|
||||
short lastModFileTime;
|
||||
short lastModFileDate;
|
||||
int crc32;
|
||||
int compressedSize;
|
||||
int uncompressedSize;
|
||||
short fileNameLength;
|
||||
short extraFieldLength;
|
||||
short fileCommentLength;
|
||||
short diskNumberStart;
|
||||
short internalFileAttributes;
|
||||
int externalFileAttributes;
|
||||
int localHeaderOffset;
|
||||
byte[] filename;
|
||||
// byte[] extra;
|
||||
// byte[] comment;
|
||||
|
||||
CentralHeader(RandomAccessFile file) throws IOException {
|
||||
byte[] buffer = new byte[46];
|
||||
file.read(buffer);
|
||||
ByteBuffer buf = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||
signature = buf.getInt();
|
||||
if (signature != MAGIC)
|
||||
throw new IOException();
|
||||
versionMadeBy = buf.getShort();
|
||||
versionNeededToExtract = buf.getShort();
|
||||
flags = buf.getShort();
|
||||
compressionMethod = buf.getShort();
|
||||
lastModFileTime = buf.getShort();
|
||||
lastModFileDate = buf.getShort();
|
||||
crc32 = buf.getInt();
|
||||
compressedSize = buf.getInt();
|
||||
uncompressedSize = buf.getInt();
|
||||
fileNameLength = buf.getShort();
|
||||
extraFieldLength = buf.getShort();
|
||||
fileCommentLength = buf.getShort();
|
||||
diskNumberStart = buf.getShort();
|
||||
internalFileAttributes = buf.getShort();
|
||||
externalFileAttributes = buf.getInt();
|
||||
localHeaderOffset = buf.getInt();
|
||||
filename = new byte[unsigned(fileNameLength)];
|
||||
file.read(filename);
|
||||
file.skipBytes(unsigned(extraFieldLength) + unsigned(fileCommentLength));
|
||||
}
|
||||
|
||||
int write(OutputStream out) throws IOException {
|
||||
byte[] buffer = new byte[46];
|
||||
ByteBuffer buf = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||
buf.putInt(signature);
|
||||
buf.putShort(versionMadeBy);
|
||||
buf.putShort(versionNeededToExtract);
|
||||
buf.putShort(flags);
|
||||
buf.putShort(compressionMethod);
|
||||
buf.putShort(lastModFileTime);
|
||||
buf.putShort(lastModFileDate);
|
||||
buf.putInt(crc32);
|
||||
buf.putInt(compressedSize);
|
||||
buf.putInt(uncompressedSize);
|
||||
buf.putShort(fileNameLength);
|
||||
buf.putShort((short) 0); // extraFieldLength
|
||||
buf.putShort((short) 0); // fileCommentLength
|
||||
buf.putShort(diskNumberStart);
|
||||
buf.putShort(internalFileAttributes);
|
||||
buf.putInt(externalFileAttributes);
|
||||
buf.putInt(localHeaderOffset);
|
||||
out.write(buffer);
|
||||
out.write(filename);
|
||||
return buffer.length + filename.length;
|
||||
}
|
||||
}
|
||||
|
||||
static class LocalHeader {
|
||||
|
||||
static final int MAGIC = 0x04034b50;
|
||||
|
||||
int signature;
|
||||
short versionNeededToExtract;
|
||||
short flags;
|
||||
short compressionMethod;
|
||||
short lastModFileTime;
|
||||
short lastModFileDate;
|
||||
int crc32;
|
||||
int compressedSize;
|
||||
int uncompressedSize;
|
||||
short fileNameLength;
|
||||
short extraFieldLength;
|
||||
byte[] filename;
|
||||
// byte[] extra;
|
||||
|
||||
LocalHeader(RandomAccessFile file) throws IOException {
|
||||
byte[] buffer = new byte[30];
|
||||
file.read(buffer);
|
||||
ByteBuffer buf = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||
signature = buf.getInt();
|
||||
if (signature != MAGIC)
|
||||
throw new IOException();
|
||||
versionNeededToExtract = buf.getShort();
|
||||
flags = buf.getShort();
|
||||
compressionMethod = buf.getShort();
|
||||
lastModFileTime = buf.getShort();
|
||||
lastModFileDate = buf.getShort();
|
||||
crc32 = buf.getInt();
|
||||
compressedSize = buf.getInt();
|
||||
uncompressedSize = buf.getInt();
|
||||
fileNameLength = buf.getShort();
|
||||
extraFieldLength = buf.getShort();
|
||||
file.skipBytes(unsigned(fileNameLength) + unsigned(extraFieldLength));
|
||||
}
|
||||
|
||||
int write(OutputStream out) throws IOException {
|
||||
byte[] buffer = new byte[30];
|
||||
ByteBuffer buf = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
|
||||
buf.putInt(signature);
|
||||
buf.putShort(versionNeededToExtract);
|
||||
buf.putShort(flags);
|
||||
buf.putShort(compressionMethod);
|
||||
buf.putShort(lastModFileTime);
|
||||
buf.putShort(lastModFileDate);
|
||||
buf.putInt(crc32);
|
||||
buf.putInt(compressedSize);
|
||||
buf.putInt(uncompressedSize);
|
||||
buf.putShort(fileNameLength);
|
||||
buf.putShort((short) 0); // extraFieldLength
|
||||
out.write(buffer);
|
||||
out.write(filename);
|
||||
return buffer.length + filename.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,24 +15,17 @@ import java.security.Security;
|
||||
public class ZipSigner {
|
||||
|
||||
public static void usage() {
|
||||
System.err.println("Usage: zipsigner [-m] [x509.pem] [pk8] input.jar output.jar");
|
||||
System.err.println("If no certificate/private key pair is specified, it will use the embedded test keys.");
|
||||
System.err.println(" -m: enable minimal signing");
|
||||
System.err.println("Usage: zipsigner [x509.pem] [pk8] input.jar output.jar");
|
||||
System.err.println("Note: If no certificate/private key pair is specified, it will use the embedded test keys.");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
boolean minSign = false;
|
||||
int argStart = 0;
|
||||
|
||||
if (args.length < 2)
|
||||
usage();
|
||||
|
||||
if (args[0].equals("-m")) {
|
||||
minSign = true;
|
||||
argStart = 1;
|
||||
}
|
||||
|
||||
InputStream cert = null;
|
||||
InputStream key = null;
|
||||
|
||||
@@ -53,7 +46,7 @@ public class ZipSigner {
|
||||
|
||||
try (JarMap jar = new JarMap(input, false);
|
||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(output))) {
|
||||
SignAPK.signZip(cert, key, jar, out, minSign);
|
||||
SignAPK.signZip(cert, key, jar, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user