use of com.android.apksig.apk.ApkFormatException in project apksig by venshine.
the class ApkSigner method sign.
private void sign(DataSource inputApk, DataSink outputApkOut, DataSource outputApkIn) throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
// Step 1. Find input APK's main ZIP sections
ApkUtils.ZipSections inputZipSections;
try {
inputZipSections = ApkUtils.findZipSections(inputApk);
} catch (ZipFormatException e) {
throw new ApkFormatException("Malformed APK: not a ZIP archive", e);
}
long inputApkSigningBlockOffset = -1;
DataSource inputApkSigningBlock = null;
try {
ApkUtils.ApkSigningBlock apkSigningBlockInfo = ApkUtils.findApkSigningBlock(inputApk, inputZipSections);
inputApkSigningBlockOffset = apkSigningBlockInfo.getStartOffset();
inputApkSigningBlock = apkSigningBlockInfo.getContents();
} catch (ApkSigningBlockNotFoundException e) {
// Input APK does not contain an APK Signing Block. That's OK. APKs are not required to
// contain this block. It's only needed if the APK is signed using APK Signature Scheme
// v2 and/or v3.
}
DataSource inputApkLfhSection = inputApk.slice(0, (inputApkSigningBlockOffset != -1) ? inputApkSigningBlockOffset : inputZipSections.getZipCentralDirectoryOffset());
// Step 2. Parse the input APK's ZIP Central Directory
ByteBuffer inputCd = getZipCentralDirectory(inputApk, inputZipSections);
List<CentralDirectoryRecord> inputCdRecords = parseZipCentralDirectory(inputCd, inputZipSections);
List<Hints.PatternWithRange> pinPatterns = extractPinPatterns(inputCdRecords, inputApkLfhSection);
List<Hints.ByteRange> pinByteRanges = pinPatterns == null ? null : new ArrayList<>();
// Step 3. Obtain a signer engine instance
ApkSignerEngine signerEngine;
if (mSignerEngine != null) {
// Use the provided signer engine
signerEngine = mSignerEngine;
} else {
// Construct a signer engine from the provided parameters
int minSdkVersion;
if (mMinSdkVersion != null) {
// No need to extract minSdkVersion from the APK's AndroidManifest.xml
minSdkVersion = mMinSdkVersion;
} else {
// Need to extract minSdkVersion from the APK's AndroidManifest.xml
minSdkVersion = getMinSdkVersionFromApk(inputCdRecords, inputApkLfhSection);
}
List<DefaultApkSignerEngine.SignerConfig> engineSignerConfigs = new ArrayList<>(mSignerConfigs.size());
for (SignerConfig signerConfig : mSignerConfigs) {
engineSignerConfigs.add(new DefaultApkSignerEngine.SignerConfig.Builder(signerConfig.getName(), signerConfig.getPrivateKey(), signerConfig.getCertificates(), signerConfig.getDeterministicDsaSigning()).build());
}
DefaultApkSignerEngine.Builder signerEngineBuilder = new DefaultApkSignerEngine.Builder(engineSignerConfigs, minSdkVersion).setV1SigningEnabled(mV1SigningEnabled).setV2SigningEnabled(mV2SigningEnabled).setV3SigningEnabled(mV3SigningEnabled).setVerityEnabled(mVerityEnabled).setDebuggableApkPermitted(mDebuggableApkPermitted).setOtherSignersSignaturesPreserved(mOtherSignersSignaturesPreserved).setSigningCertificateLineage(mSigningCertificateLineage);
if (mCreatedBy != null) {
signerEngineBuilder.setCreatedBy(mCreatedBy);
}
if (mSourceStampSignerConfig != null) {
signerEngineBuilder.setStampSignerConfig(new DefaultApkSignerEngine.SignerConfig.Builder(mSourceStampSignerConfig.getName(), mSourceStampSignerConfig.getPrivateKey(), mSourceStampSignerConfig.getCertificates(), mSourceStampSignerConfig.getDeterministicDsaSigning()).build());
}
if (mSourceStampSigningCertificateLineage != null) {
signerEngineBuilder.setSourceStampSigningCertificateLineage(mSourceStampSigningCertificateLineage);
}
signerEngine = signerEngineBuilder.build();
}
// Step 4. Provide the signer engine with the input APK's APK Signing Block (if any)
if (inputApkSigningBlock != null) {
signerEngine.inputApkSigningBlock(inputApkSigningBlock);
}
// Step 5. Iterate over input APK's entries and output the Local File Header + data of those
// entries which need to be output. Entries are iterated in the order in which their Local
// File Header records are stored in the file. This is to achieve better data locality in
// case Central Directory entries are in the wrong order.
List<CentralDirectoryRecord> inputCdRecordsSortedByLfhOffset = new ArrayList<>(inputCdRecords);
Collections.sort(inputCdRecordsSortedByLfhOffset, CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
int lastModifiedDateForNewEntries = -1;
int lastModifiedTimeForNewEntries = -1;
long inputOffset = 0;
long outputOffset = 0;
byte[] sourceStampCertificateDigest = null;
Map<String, CentralDirectoryRecord> outputCdRecordsByName = new HashMap<>(inputCdRecords.size());
for (final CentralDirectoryRecord inputCdRecord : inputCdRecordsSortedByLfhOffset) {
String entryName = inputCdRecord.getName();
if (Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME.equals(entryName)) {
// We'll re-add below if needed.
continue;
}
if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(entryName)) {
try {
sourceStampCertificateDigest = LocalFileRecord.getUncompressedData(inputApkLfhSection, inputCdRecord, inputApkLfhSection.size());
} catch (ZipFormatException ex) {
throw new ApkFormatException("Bad source stamp entry");
}
// Existing source stamp is handled below as needed.
continue;
}
ApkSignerEngine.InputJarEntryInstructions entryInstructions = signerEngine.inputJarEntry(entryName);
boolean shouldOutput;
switch(entryInstructions.getOutputPolicy()) {
case OUTPUT:
shouldOutput = true;
break;
case OUTPUT_BY_ENGINE:
case SKIP:
shouldOutput = false;
break;
default:
throw new RuntimeException("Unknown output policy: " + entryInstructions.getOutputPolicy());
}
long inputLocalFileHeaderStartOffset = inputCdRecord.getLocalFileHeaderOffset();
if (inputLocalFileHeaderStartOffset > inputOffset) {
// Unprocessed data in input starting at inputOffset and ending and the start of
// this record's LFH. We output this data verbatim because this signer is supposed
// to preserve as much of input as possible.
long chunkSize = inputLocalFileHeaderStartOffset - inputOffset;
inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut);
outputOffset += chunkSize;
inputOffset = inputLocalFileHeaderStartOffset;
}
LocalFileRecord inputLocalFileRecord;
try {
inputLocalFileRecord = LocalFileRecord.getRecord(inputApkLfhSection, inputCdRecord, inputApkLfhSection.size());
} catch (ZipFormatException e) {
throw new ApkFormatException("Malformed ZIP entry: " + inputCdRecord.getName(), e);
}
inputOffset += inputLocalFileRecord.getSize();
ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest = entryInstructions.getInspectJarEntryRequest();
if (inspectEntryRequest != null) {
fulfillInspectInputJarEntryRequest(inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest);
}
if (shouldOutput) {
// Find the max value of last modified, to be used for new entries added by the
// signer.
int lastModifiedDate = inputCdRecord.getLastModificationDate();
int lastModifiedTime = inputCdRecord.getLastModificationTime();
if ((lastModifiedDateForNewEntries == -1) || (lastModifiedDate > lastModifiedDateForNewEntries) || ((lastModifiedDate == lastModifiedDateForNewEntries) && (lastModifiedTime > lastModifiedTimeForNewEntries))) {
lastModifiedDateForNewEntries = lastModifiedDate;
lastModifiedTimeForNewEntries = lastModifiedTime;
}
inspectEntryRequest = signerEngine.outputJarEntry(entryName);
if (inspectEntryRequest != null) {
fulfillInspectInputJarEntryRequest(inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest);
}
// Output entry's Local File Header + data
long outputLocalFileHeaderOffset = outputOffset;
OutputSizeAndDataOffset outputLfrResult = outputInputJarEntryLfhRecordPreservingDataAlignment(inputApkLfhSection, inputLocalFileRecord, outputApkOut, outputLocalFileHeaderOffset);
outputOffset += outputLfrResult.outputBytes;
long outputDataOffset = outputLocalFileHeaderOffset + outputLfrResult.dataOffsetBytes;
if (pinPatterns != null) {
boolean pinFileHeader = false;
for (Hints.PatternWithRange pinPattern : pinPatterns) {
if (pinPattern.matcher(inputCdRecord.getName()).matches()) {
Hints.ByteRange dataRange = new Hints.ByteRange(outputDataOffset, outputOffset);
Hints.ByteRange pinRange = pinPattern.ClampToAbsoluteByteRange(dataRange);
if (pinRange != null) {
pinFileHeader = true;
pinByteRanges.add(pinRange);
}
}
}
if (pinFileHeader) {
pinByteRanges.add(new Hints.ByteRange(outputLocalFileHeaderOffset, outputDataOffset));
}
}
// Enqueue entry's Central Directory record for output
CentralDirectoryRecord outputCdRecord;
if (outputLocalFileHeaderOffset == inputLocalFileRecord.getStartOffsetInArchive()) {
outputCdRecord = inputCdRecord;
} else {
outputCdRecord = inputCdRecord.createWithModifiedLocalFileHeaderOffset(outputLocalFileHeaderOffset);
}
outputCdRecordsByName.put(entryName, outputCdRecord);
}
}
long inputLfhSectionSize = inputApkLfhSection.size();
if (inputOffset < inputLfhSectionSize) {
// Unprocessed data in input starting at inputOffset and ending and the end of the input
// APK's LFH section. We output this data verbatim because this signer is supposed
// to preserve as much of input as possible.
long chunkSize = inputLfhSectionSize - inputOffset;
inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut);
outputOffset += chunkSize;
inputOffset = inputLfhSectionSize;
}
// Step 6. Sort output APK's Central Directory records in the order in which they should
// appear in the output
List<CentralDirectoryRecord> outputCdRecords = new ArrayList<>(inputCdRecords.size() + 10);
for (CentralDirectoryRecord inputCdRecord : inputCdRecords) {
String entryName = inputCdRecord.getName();
CentralDirectoryRecord outputCdRecord = outputCdRecordsByName.get(entryName);
if (outputCdRecord != null) {
outputCdRecords.add(outputCdRecord);
}
}
if (lastModifiedDateForNewEntries == -1) {
// Jan 1 2009 (DOS)
lastModifiedDateForNewEntries = 0x3a21;
lastModifiedTimeForNewEntries = 0;
}
// records.
if (signerEngine.isEligibleForSourceStamp()) {
byte[] uncompressedData = signerEngine.generateSourceStampCertificateDigest();
if (mForceSourceStampOverwrite || sourceStampCertificateDigest == null || Arrays.equals(uncompressedData, sourceStampCertificateDigest)) {
outputOffset += outputDataToOutputApk(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME, uncompressedData, outputOffset, outputCdRecords, lastModifiedTimeForNewEntries, lastModifiedDateForNewEntries, outputApkOut);
} else {
throw new ApkFormatException(String.format("Cannot generate SourceStamp. APK contains an existing entry with" + " the name: %s, and it is different than the provided source" + " stamp certificate", SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME));
}
}
// This has to be before the step 8 so that the file is signed.
if (pinByteRanges != null) {
// Covers JAR signature and zip central dir entry.
// The signature files don't have to be pinned, but pinning them isn't that wasteful
// since the total size is small.
pinByteRanges.add(new Hints.ByteRange(outputOffset, Long.MAX_VALUE));
String entryName = Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME;
byte[] uncompressedData = Hints.encodeByteRangeList(pinByteRanges);
requestOutputEntryInspection(signerEngine, entryName, uncompressedData);
outputOffset += outputDataToOutputApk(entryName, uncompressedData, outputOffset, outputCdRecords, lastModifiedTimeForNewEntries, lastModifiedDateForNewEntries, outputApkOut);
}
// Step 8. Generate and output JAR signatures, if necessary. This may output more Local File
// Header + data entries and add to the list of output Central Directory records.
ApkSignerEngine.OutputJarSignatureRequest outputJarSignatureRequest = signerEngine.outputJarEntries();
if (outputJarSignatureRequest != null) {
for (ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry : outputJarSignatureRequest.getAdditionalJarEntries()) {
String entryName = entry.getName();
byte[] uncompressedData = entry.getData();
requestOutputEntryInspection(signerEngine, entryName, uncompressedData);
outputOffset += outputDataToOutputApk(entryName, uncompressedData, outputOffset, outputCdRecords, lastModifiedTimeForNewEntries, lastModifiedDateForNewEntries, outputApkOut);
}
outputJarSignatureRequest.done();
}
// Step 9. Construct output ZIP Central Directory in an in-memory buffer
long outputCentralDirSizeBytes = 0;
for (CentralDirectoryRecord record : outputCdRecords) {
outputCentralDirSizeBytes += record.getSize();
}
if (outputCentralDirSizeBytes > Integer.MAX_VALUE) {
throw new IOException("Output ZIP Central Directory too large: " + outputCentralDirSizeBytes + " bytes");
}
ByteBuffer outputCentralDir = ByteBuffer.allocate((int) outputCentralDirSizeBytes);
for (CentralDirectoryRecord record : outputCdRecords) {
record.copyTo(outputCentralDir);
}
outputCentralDir.flip();
DataSource outputCentralDirDataSource = new ByteBufferDataSource(outputCentralDir);
long outputCentralDirStartOffset = outputOffset;
int outputCentralDirRecordCount = outputCdRecords.size();
// Step 10. Construct output ZIP End of Central Directory record in an in-memory buffer
ByteBuffer outputEocd = EocdRecord.createWithModifiedCentralDirectoryInfo(inputZipSections.getZipEndOfCentralDirectory(), outputCentralDirRecordCount, outputCentralDirDataSource.size(), outputCentralDirStartOffset);
// Step 11. Generate and output APK Signature Scheme v2 and/or v3 signatures and/or
// SourceStamp signatures, if necessary.
// This may insert an APK Signing Block just before the output's ZIP Central Directory
ApkSignerEngine.OutputApkSigningBlockRequest2 outputApkSigningBlockRequest = signerEngine.outputZipSections2(outputApkIn, outputCentralDirDataSource, DataSources.asDataSource(outputEocd));
if (outputApkSigningBlockRequest != null) {
int padding = outputApkSigningBlockRequest.getPaddingSizeBeforeApkSigningBlock();
outputApkOut.consume(ByteBuffer.allocate(padding));
byte[] outputApkSigningBlock = outputApkSigningBlockRequest.getApkSigningBlock();
outputApkOut.consume(outputApkSigningBlock, 0, outputApkSigningBlock.length);
ZipUtils.setZipEocdCentralDirectoryOffset(outputEocd, outputCentralDirStartOffset + padding + outputApkSigningBlock.length);
outputApkSigningBlockRequest.done();
}
// Step 12. Output ZIP Central Directory and ZIP End of Central Directory
outputCentralDirDataSource.feed(0, outputCentralDirDataSource.size(), outputApkOut);
outputApkOut.consume(outputEocd);
signerEngine.outputDone();
// Step 13. Generate and output APK Signature Scheme v4 signatures, if necessary.
if (mV4SigningEnabled) {
signerEngine.signV4(outputApkIn, mOutputV4File, !mV4ErrorReportingEnabled);
}
}
use of com.android.apksig.apk.ApkFormatException in project apksig by venshine.
the class ApkSignerTool method getLineageFromInputFile.
/**
* Extracts the Signing Certificate Lineage from the provided lineage or APK file.
*/
private static SigningCertificateLineage getLineageFromInputFile(File inputLineageFile) throws ParameterException {
try (RandomAccessFile f = new RandomAccessFile(inputLineageFile, "r")) {
if (f.length() < 4) {
throw new ParameterException("The input file is not a valid lineage file.");
}
DataSource apk = DataSources.asDataSource(f);
int magicValue = apk.getByteBuffer(0, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
if (magicValue == SigningCertificateLineage.MAGIC) {
return SigningCertificateLineage.readFromFile(inputLineageFile);
} else if (magicValue == ZIP_MAGIC) {
return SigningCertificateLineage.readFromApkFile(inputLineageFile);
} else {
throw new ParameterException("The input file is not a valid lineage file.");
}
} catch (IOException | ApkFormatException | IllegalArgumentException e) {
throw new ParameterException(e.getMessage());
}
}
use of com.android.apksig.apk.ApkFormatException in project apksig by venshine.
the class ZipUtils method parseZipCentralDirectory.
public static List<CentralDirectoryRecord> parseZipCentralDirectory(DataSource apk, ZipSections apkSections) throws IOException, ApkFormatException {
// Read the ZIP Central Directory
long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes();
if (cdSizeBytes > Integer.MAX_VALUE) {
throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes);
}
long cdOffset = apkSections.getZipCentralDirectoryOffset();
ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes);
cd.order(ByteOrder.LITTLE_ENDIAN);
// Parse the ZIP Central Directory
int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount();
List<CentralDirectoryRecord> cdRecords = new ArrayList<>(expectedCdRecordCount);
for (int i = 0; i < expectedCdRecordCount; i++) {
CentralDirectoryRecord cdRecord;
int offsetInsideCd = cd.position();
try {
cdRecord = CentralDirectoryRecord.getRecord(cd);
} catch (ZipFormatException e) {
throw new ApkFormatException("Malformed ZIP Central Directory record #" + (i + 1) + " at file offset " + (cdOffset + offsetInsideCd), e);
}
String entryName = cdRecord.getName();
if (entryName.endsWith("/")) {
// Ignore directory entries
continue;
}
cdRecords.add(cdRecord);
}
return cdRecords;
}
use of com.android.apksig.apk.ApkFormatException in project apksig by venshine.
the class DefaultApkSignerEngine method outputZipSectionsInternal.
private OutputApkSigningBlockRequestImpl outputZipSectionsInternal(DataSource zipEntries, DataSource zipCentralDirectory, DataSource zipEocd, boolean apkSigningBlockPaddingSupported) throws IOException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
checkNotClosed();
checkV1SigningDoneIfEnabled();
if (!mV2SigningEnabled && !mV3SigningEnabled && !isEligibleForSourceStamp()) {
return null;
}
checkOutputApkNotDebuggableIfDebuggableMustBeRejected();
// adjust to proper padding
Pair<DataSource, Integer> paddingPair = ApkSigningBlockUtils.generateApkSigningBlockPadding(zipEntries, apkSigningBlockPaddingSupported);
DataSource beforeCentralDir = paddingPair.getFirst();
int padSizeBeforeApkSigningBlock = paddingPair.getSecond();
DataSource eocd = ApkSigningBlockUtils.copyWithModifiedCDOffset(beforeCentralDir, zipEocd);
List<Pair<byte[], Integer>> signingSchemeBlocks = new ArrayList<>();
ApkSigningBlockUtils.SigningSchemeBlockAndDigests v2SigningSchemeBlockAndDigests = null;
ApkSigningBlockUtils.SigningSchemeBlockAndDigests v3SigningSchemeBlockAndDigests = null;
// new APK signing block.
if (mOtherSignersSignaturesPreserved && mPreservedSignatureBlocks != null && !mPreservedSignatureBlocks.isEmpty()) {
signingSchemeBlocks.addAll(mPreservedSignatureBlocks);
}
// create APK Signature Scheme V2 Signature if requested
if (mV2SigningEnabled) {
invalidateV2Signature();
List<ApkSigningBlockUtils.SignerConfig> v2SignerConfigs = createV2SignerConfigs(apkSigningBlockPaddingSupported);
v2SigningSchemeBlockAndDigests = V2SchemeSigner.generateApkSignatureSchemeV2Block(mExecutor, beforeCentralDir, zipCentralDirectory, eocd, v2SignerConfigs, mV3SigningEnabled, mOtherSignersSignaturesPreserved ? mPreservedV2Signers : null);
signingSchemeBlocks.add(v2SigningSchemeBlockAndDigests.signingSchemeBlock);
}
if (mV3SigningEnabled) {
invalidateV3Signature();
List<ApkSigningBlockUtils.SignerConfig> v3SignerConfigs = createV3SignerConfigs(apkSigningBlockPaddingSupported);
v3SigningSchemeBlockAndDigests = V3SchemeSigner.generateApkSignatureSchemeV3Block(mExecutor, beforeCentralDir, zipCentralDirectory, eocd, v3SignerConfigs);
signingSchemeBlocks.add(v3SigningSchemeBlockAndDigests.signingSchemeBlock);
}
if (isEligibleForSourceStamp()) {
ApkSigningBlockUtils.SignerConfig sourceStampSignerConfig = createSourceStampSignerConfig();
Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeDigestInfos = new HashMap<>();
if (mV3SigningEnabled) {
signatureSchemeDigestInfos.put(VERSION_APK_SIGNATURE_SCHEME_V3, v3SigningSchemeBlockAndDigests.digestInfo);
}
if (mV2SigningEnabled) {
signatureSchemeDigestInfos.put(VERSION_APK_SIGNATURE_SCHEME_V2, v2SigningSchemeBlockAndDigests.digestInfo);
}
if (mV1SigningEnabled) {
Map<ContentDigestAlgorithm, byte[]> v1SigningSchemeDigests = new HashMap<>();
try {
// Jar signing related variables must have been already populated at this point
// if V1 signing is enabled since it is happening before computations on the APK
// signing block (V2/V3/V4/SourceStamp signing).
byte[] inputJarManifest = (mInputJarManifestEntryDataRequest != null) ? mInputJarManifestEntryDataRequest.getData() : null;
byte[] jarManifest = V1SchemeSigner.generateManifestFile(mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest).contents;
// The digest of the jar manifest does not need to be computed in chunks due to
// the small size of the manifest.
v1SigningSchemeDigests.put(ContentDigestAlgorithm.SHA256, computeSha256DigestBytes(jarManifest));
} catch (ApkFormatException e) {
throw new RuntimeException("Failed to generate manifest file", e);
}
signatureSchemeDigestInfos.put(VERSION_JAR_SIGNATURE_SCHEME, v1SigningSchemeDigests);
}
signingSchemeBlocks.add(V2SourceStampSigner.generateSourceStampBlock(sourceStampSignerConfig, signatureSchemeDigestInfos));
}
// create APK Signing Block with v2 and/or v3 and/or SourceStamp blocks
byte[] apkSigningBlock = ApkSigningBlockUtils.generateApkSigningBlock(signingSchemeBlocks);
mAddSigningBlockRequest = new OutputApkSigningBlockRequestImpl(apkSigningBlock, padSizeBeforeApkSigningBlock);
return mAddSigningBlockRequest;
}
use of com.android.apksig.apk.ApkFormatException in project apksig by venshine.
the class SigningCertificateLineage method readFromApkDataSource.
/**
* Extracts a Signing Certificate Lineage from the proof-of-rotation attribute in the V3
* signature block of the provided APK DataSource.
*
* @throws IllegalArgumentException if the provided APK does not contain a V3 signature block,
* or if the V3 signature block does not contain a valid lineage.
*/
public static SigningCertificateLineage readFromApkDataSource(DataSource apk) throws IOException, ApkFormatException {
SignatureInfo signatureInfo;
try {
ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk);
ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections, V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID, result);
} catch (ZipFormatException e) {
throw new ApkFormatException(e.getMessage());
} catch (ApkSigningBlockUtils.SignatureNotFoundException e) {
throw new IllegalArgumentException("The provided APK does not contain a valid V3 signature block.");
}
// FORMAT:
// * length-prefixed sequence of length-prefixed signers:
// * length-prefixed signed data
// * minSDK
// * maxSDK
// * length-prefixed sequence of length-prefixed signatures
// * length-prefixed public key
ByteBuffer signers = getLengthPrefixedSlice(signatureInfo.signatureBlock);
List<SigningCertificateLineage> lineages = new ArrayList<>(1);
while (signers.hasRemaining()) {
ByteBuffer signer = getLengthPrefixedSlice(signers);
ByteBuffer signedData = getLengthPrefixedSlice(signer);
try {
SigningCertificateLineage lineage = readFromSignedData(signedData);
lineages.add(lineage);
} catch (IllegalArgumentException ignored) {
// The current signer block does not contain a valid lineage, but it is possible
// another block will.
}
}
SigningCertificateLineage result;
if (lineages.isEmpty()) {
throw new IllegalArgumentException("The provided APK does not contain a valid lineage.");
} else if (lineages.size() > 1) {
result = consolidateLineages(lineages);
} else {
result = lineages.get(0);
}
return result;
}
Aggregations