use of com.android.apksig.internal.util.Pair in project LSPatch by LSPosed.
the class SigningBlockUtils method extractAllSigners.
/**
* Extracts all signing block entries except padding block.
*
* @param signingBlock APK v2 signing block containing: length prefix, signers (can include
* padding block), length postfix and APK sig v2 block magic.
* @return list of block entry value and block entry id pairs.
*/
private static ImmutableList<Pair<byte[], Integer>> extractAllSigners(DataSource signingBlock) throws IOException {
long wholeBlockSize = signingBlock.size();
// Take the segment of the existing signing block without the length prefix (8 bytes)
// at the beginning and the length and magic (24 bytes) at the end, so it is just the sequence
// of length prefix id value pairs.
DataSource lengthPrefixedIdValuePairsSource = signingBlock.slice(SIZE_OF_BLOCK_NUM_BYTES, wholeBlockSize - 2 * SIZE_OF_BLOCK_NUM_BYTES - MAGIC_NUM_BYTES);
final int lengthAndIdByteCount = BLOCK_LENGTH_NUM_BYTES + BLOCK_ID_NUM_BYTES;
ByteBuffer lengthAndId = ByteBuffer.allocate(lengthAndIdByteCount).order(LITTLE_ENDIAN);
ImmutableList.Builder<Pair<byte[], Integer>> idValuePairs = ImmutableList.builder();
for (int index = 0; index <= lengthPrefixedIdValuePairsSource.size() - lengthAndIdByteCount; ) {
lengthPrefixedIdValuePairsSource.copyTo(index, lengthAndIdByteCount, lengthAndId);
lengthAndId.flip();
int blockLength = Ints.checkedCast(lengthAndId.getLong());
int id = lengthAndId.getInt();
lengthAndId.clear();
if (id != VERITY_PADDING_BLOCK_ID) {
int blockValueSize = blockLength - BLOCK_ID_NUM_BYTES;
ByteBuffer blockValue = ByteBuffer.allocate(blockValueSize);
lengthPrefixedIdValuePairsSource.copyTo(index + BLOCK_LENGTH_NUM_BYTES + BLOCK_ID_NUM_BYTES, blockValueSize, blockValue);
idValuePairs.add(Pair.of(blockValue.array(), id));
}
index += blockLength + BLOCK_LENGTH_NUM_BYTES;
}
return idValuePairs.build();
}
use of com.android.apksig.internal.util.Pair 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.internal.util.Pair in project apksig by venshine.
the class DefaultApkSignerEngine method outputJarEntries.
@Override
public OutputJarSignatureRequest outputJarEntries() throws ApkFormatException, InvalidKeyException, SignatureException, NoSuchAlgorithmException {
checkNotClosed();
if (!mV1SignaturePending) {
return null;
}
if ((mInputJarManifestEntryDataRequest != null) && (!mInputJarManifestEntryDataRequest.isDone())) {
throw new IllegalStateException("Still waiting to inspect input APK's " + mInputJarManifestEntryDataRequest.getEntryName());
}
for (GetJarEntryDataDigestRequest digestRequest : mOutputJarEntryDigestRequests.values()) {
String entryName = digestRequest.getEntryName();
if (!digestRequest.isDone()) {
throw new IllegalStateException("Still waiting to inspect output APK's " + entryName);
}
mOutputJarEntryDigests.put(entryName, digestRequest.getDigest());
}
if (isEligibleForSourceStamp()) {
MessageDigest messageDigest = MessageDigest.getInstance(V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm));
messageDigest.update(generateSourceStampCertificateDigest());
mOutputJarEntryDigests.put(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME, messageDigest.digest());
}
mOutputJarEntryDigestRequests.clear();
for (GetJarEntryDataRequest dataRequest : mOutputSignatureJarEntryDataRequests.values()) {
if (!dataRequest.isDone()) {
throw new IllegalStateException("Still waiting to inspect output APK's " + dataRequest.getEntryName());
}
}
List<Integer> apkSigningSchemeIds = new ArrayList<>();
if (mV2SigningEnabled) {
apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
}
if (mV3SigningEnabled) {
apkSigningSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
}
byte[] inputJarManifest = (mInputJarManifestEntryDataRequest != null) ? mInputJarManifestEntryDataRequest.getData() : null;
if (isEligibleForSourceStamp()) {
inputJarManifest = V1SchemeSigner.generateManifestFile(mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest).contents;
}
// Check whether the most recently used signature (if present) is still fine.
checkOutputApkNotDebuggableIfDebuggableMustBeRejected();
List<Pair<String, byte[]>> signatureZipEntries;
if ((mAddV1SignatureRequest == null) || (!mAddV1SignatureRequest.isDone())) {
try {
signatureZipEntries = V1SchemeSigner.sign(mV1SignerConfigs, mV1ContentDigestAlgorithm, mOutputJarEntryDigests, apkSigningSchemeIds, inputJarManifest, mCreatedBy);
} catch (CertificateException e) {
throw new SignatureException("Failed to generate v1 signature", e);
}
} else {
V1SchemeSigner.OutputManifestFile newManifest = V1SchemeSigner.generateManifestFile(mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest);
byte[] emittedSignatureManifest = mEmittedSignatureJarEntryData.get(V1SchemeConstants.MANIFEST_ENTRY_NAME);
if (!Arrays.equals(newManifest.contents, emittedSignatureManifest)) {
// Emitted v1 signature is no longer valid.
try {
signatureZipEntries = V1SchemeSigner.signManifest(mV1SignerConfigs, mV1ContentDigestAlgorithm, apkSigningSchemeIds, mCreatedBy, newManifest);
} catch (CertificateException e) {
throw new SignatureException("Failed to generate v1 signature", e);
}
} else {
// Emitted v1 signature is still valid. Check whether the signature is there in the
// output.
signatureZipEntries = new ArrayList<>();
for (Map.Entry<String, byte[]> expectedOutputEntry : mEmittedSignatureJarEntryData.entrySet()) {
String entryName = expectedOutputEntry.getKey();
byte[] expectedData = expectedOutputEntry.getValue();
GetJarEntryDataRequest actualDataRequest = mOutputSignatureJarEntryDataRequests.get(entryName);
if (actualDataRequest == null) {
// This signature entry hasn't been output.
signatureZipEntries.add(Pair.of(entryName, expectedData));
continue;
}
byte[] actualData = actualDataRequest.getData();
if (!Arrays.equals(expectedData, actualData)) {
signatureZipEntries.add(Pair.of(entryName, expectedData));
}
}
if (signatureZipEntries.isEmpty()) {
// v1 signature in the output is valid
return null;
}
// v1 signature in the output is not valid.
}
}
if (signatureZipEntries.isEmpty()) {
// v1 signature in the output is valid
mV1SignaturePending = false;
return null;
}
List<OutputJarSignatureRequest.JarEntry> sigEntries = new ArrayList<>(signatureZipEntries.size());
for (Pair<String, byte[]> entry : signatureZipEntries) {
String entryName = entry.getFirst();
byte[] entryData = entry.getSecond();
sigEntries.add(new OutputJarSignatureRequest.JarEntry(entryName, entryData));
mEmittedSignatureJarEntryData.put(entryName, entryData);
}
mAddV1SignatureRequest = new OutputJarSignatureRequestImpl(sigEntries);
return mAddV1SignatureRequest;
}
use of com.android.apksig.internal.util.Pair in project apksig by venshine.
the class V3SchemeSigner method generateSignerBlock.
private static byte[] generateSignerBlock(SignerConfig signerConfig, Map<ContentDigestAlgorithm, byte[]> contentDigests) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
if (signerConfig.certificates.isEmpty()) {
throw new SignatureException("No certificates configured for signer");
}
PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
byte[] encodedPublicKey = encodePublicKey(publicKey);
V3SignatureSchemeBlock.SignedData signedData = new V3SignatureSchemeBlock.SignedData();
try {
signedData.certificates = encodeCertificates(signerConfig.certificates);
} catch (CertificateEncodingException e) {
throw new SignatureException("Failed to encode certificates", e);
}
List<Pair<Integer, byte[]>> digests = new ArrayList<>(signerConfig.signatureAlgorithms.size());
for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm();
byte[] contentDigest = contentDigests.get(contentDigestAlgorithm);
if (contentDigest == null) {
throw new RuntimeException(contentDigestAlgorithm + " content digest for " + signatureAlgorithm + " not computed");
}
digests.add(Pair.of(signatureAlgorithm.getId(), contentDigest));
}
signedData.digests = digests;
signedData.minSdkVersion = signerConfig.minSdkVersion;
signedData.maxSdkVersion = signerConfig.maxSdkVersion;
signedData.additionalAttributes = generateAdditionalAttributes(signerConfig);
V3SignatureSchemeBlock.Signer signer = new V3SignatureSchemeBlock.Signer();
signer.signedData = encodeSignedData(signedData);
signer.minSdkVersion = signerConfig.minSdkVersion;
signer.maxSdkVersion = signerConfig.maxSdkVersion;
signer.publicKey = encodedPublicKey;
signer.signatures = ApkSigningBlockUtils.generateSignaturesOverData(signerConfig, signer.signedData);
return encodeSigner(signer);
}
use of com.android.apksig.internal.util.Pair in project apksig by venshine.
the class ApkSigningBlockUtils method getApkSignatureBlockSigners.
/**
* Returns the individual APK signers within the provided {@code signatureBlock} in a {@code
* List} of {@code Pair} instances where the first element is a {@code List} of {@link
* X509Certificate}s and the second element is a byte array of the individual signer's block.
*
* <p>This method supports any signature block that adheres to the following format up to the
* signing certificate(s):
* <pre>
* * length-prefixed sequence of length-prefixed signers
* * length-prefixed signed data
* * length-prefixed sequence of length-prefixed digests:
* * uint32: signature algorithm ID
* * length-prefixed bytes: digest of contents
* * length-prefixed sequence of certificates:
* * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded).
* </pre>
*
* <p>Note, this is a convenience method to obtain any signers from an existing signature block;
* the signature of each signer will not be verified.
*
* @throws ApkFormatException if an error is encountered while parsing the provided {@code
* signatureBlock}
* @throws CertificateException if the signing certificate(s) within an individual signer block
* cannot be parsed
*/
public static List<Pair<List<X509Certificate>, byte[]>> getApkSignatureBlockSigners(byte[] signatureBlock) throws ApkFormatException, CertificateException {
ByteBuffer signatureBlockBuffer = ByteBuffer.wrap(signatureBlock);
signatureBlockBuffer.order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer signersBuffer = getLengthPrefixedSlice(signatureBlockBuffer);
List<Pair<List<X509Certificate>, byte[]>> signers = new ArrayList<>();
while (signersBuffer.hasRemaining()) {
// Parse the next signer block, save all of its bytes for the resulting List, and
// rewind the buffer to allow the signing certificate(s) to be parsed.
ByteBuffer signer = getLengthPrefixedSlice(signersBuffer);
byte[] signerBytes = new byte[signer.remaining()];
signer.get(signerBytes);
signer.rewind();
ByteBuffer signedData = getLengthPrefixedSlice(signer);
// The first length prefixed slice is the sequence of digests which are not required
// when obtaining the signing certificate(s).
getLengthPrefixedSlice(signedData);
ByteBuffer certificatesBuffer = getLengthPrefixedSlice(signedData);
List<X509Certificate> certificates = new ArrayList<>();
while (certificatesBuffer.hasRemaining()) {
int certLength = certificatesBuffer.getInt();
byte[] certBytes = new byte[certLength];
if (certLength > certificatesBuffer.remaining()) {
throw new IllegalArgumentException("Cert index " + (certificates.size() + 1) + " under signer index " + (signers.size() + 1) + " size out of range: " + certLength);
}
certificatesBuffer.get(certBytes);
GuaranteedEncodedFormX509Certificate signerCert = new GuaranteedEncodedFormX509Certificate(X509CertificateUtils.generateCertificate(certBytes), certBytes);
certificates.add(signerCert);
}
signers.add(Pair.of(certificates, signerBytes));
}
return signers;
}
Aggregations