Include source stamp cert digest in V1 signature am: cc8dd8103b am: b2ffd922fe
Change-Id: Idbca38545ae581c6145463dcf4c1f6e49bbc99b1
diff --git a/src/main/java/com/android/apksig/ApkSigner.java b/src/main/java/com/android/apksig/ApkSigner.java
index 9ccd267..556c0eb 100644
--- a/src/main/java/com/android/apksig/ApkSigner.java
+++ b/src/main/java/com/android/apksig/ApkSigner.java
@@ -16,6 +16,8 @@
package com.android.apksig;
+import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME;
+
import com.android.apksig.apk.ApkFormatException;
import com.android.apksig.apk.ApkSigningBlockNotFoundException;
import com.android.apksig.apk.ApkUtils;
@@ -39,7 +41,6 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.InvalidKeyException;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
@@ -83,9 +84,6 @@
/** Name of the Android manifest ZIP entry in APKs. */
private static final String ANDROID_MANIFEST_ZIP_ENTRY_NAME = "AndroidManifest.xml";
- /** Name of the SourceStamp certificate hash ZIP entry in APKs. */
- public static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256";
-
private final List<SignerConfig> mSignerConfigs;
private final SignerConfig mSourceStampSignerConfig;
private final Integer mMinSdkVersion;
@@ -463,12 +461,7 @@
// more Local File Header + data entries and add to the list of output Central Directory
// records.
if (signerEngine.isEligibleForSourceStamp()) {
- if (mSourceStampSignerConfig.getCertificates().isEmpty()) {
- throw new SignatureException("No certificates configured for stamp");
- }
- byte[] uncompressedData =
- computeSha256DigestBytes(
- mSourceStampSignerConfig.getCertificates().get(0).getEncoded());
+ byte[] uncompressedData = signerEngine.generateSourceStampCertificateDigest();
outputOffset +=
outputDataToOutputApk(
SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME,
@@ -915,17 +908,6 @@
return ApkUtils.getMinSdkVersionFromBinaryAndroidManifest(androidManifest);
}
- private static byte[] computeSha256DigestBytes(byte[] data) {
- MessageDigest messageDigest;
- try {
- messageDigest = MessageDigest.getInstance("SHA-256");
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("SHA-256 is not found", e);
- }
- messageDigest.update(data);
- return messageDigest.digest();
- }
-
/**
* Configuration of a signer.
*
diff --git a/src/main/java/com/android/apksig/ApkSignerEngine.java b/src/main/java/com/android/apksig/ApkSignerEngine.java
index 6d768d5..49a136b 100644
--- a/src/main/java/com/android/apksig/ApkSignerEngine.java
+++ b/src/main/java/com/android/apksig/ApkSignerEngine.java
@@ -331,6 +331,11 @@
return false;
}
+ /** Generates the digest of the certificate used to sign the source stamp. */
+ default byte[] generateSourceStampCertificateDigest() throws SignatureException {
+ return new byte[0];
+ }
+
/**
* Indicates to this engine that it will no longer be used. Invoking this on an already closed
* engine is OK.
diff --git a/src/main/java/com/android/apksig/DefaultApkSignerEngine.java b/src/main/java/com/android/apksig/DefaultApkSignerEngine.java
index 54da524..bbd6933 100644
--- a/src/main/java/com/android/apksig/DefaultApkSignerEngine.java
+++ b/src/main/java/com/android/apksig/DefaultApkSignerEngine.java
@@ -16,6 +16,8 @@
package com.android.apksig;
+import static com.android.apksig.apk.ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME;
+
import com.android.apksig.apk.ApkFormatException;
import com.android.apksig.apk.ApkUtils;
import com.android.apksig.internal.apk.ApkSigningBlockUtils;
@@ -47,6 +49,7 @@
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -197,8 +200,8 @@
if (subLineage.size() != 1) {
throw new IllegalArgumentException(
"v1 signing enabled but the oldest signer in the"
- + " SigningCertificateLineage is missing. Please provide the"
- + " oldest signer to enable v1 signing");
+ + " SigningCertificateLineage is missing. Please provide the"
+ + " oldest signer to enable v1 signing");
}
}
createV1SignerConfigs(Collections.singletonList(oldestConfig), minSdkVersion);
@@ -248,7 +251,7 @@
v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm;
} else {
if (DigestAlgorithm.BY_STRENGTH_COMPARATOR.compare(
- v1SignatureDigestAlgorithm, v1ContentDigestAlgorithm)
+ v1SignatureDigestAlgorithm, v1ContentDigestAlgorithm)
> 0) {
v1ContentDigestAlgorithm = v1SignatureDigestAlgorithm;
}
@@ -484,10 +487,10 @@
Optional<V1SchemeVerifier.NamedDigest> extractedDigest =
V1SchemeVerifier.getDigestsToVerify(
- entry.getValue(),
- "-Digest",
- mMinSdkVersion,
- Integer.MAX_VALUE)
+ entry.getValue(),
+ "-Digest",
+ mMinSdkVersion,
+ Integer.MAX_VALUE)
.stream()
.filter(d -> d.jcaDigestAlgorithm == alg)
.findFirst();
@@ -655,7 +658,7 @@
@Override
public OutputJarSignatureRequest outputJarEntries()
throws ApkFormatException, InvalidKeyException, SignatureException,
- NoSuchAlgorithmException {
+ NoSuchAlgorithmException {
checkNotClosed();
if (!mV1SignaturePending) {
@@ -677,6 +680,14 @@
}
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()) {
@@ -697,6 +708,14 @@
(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();
@@ -893,6 +912,19 @@
}
@Override
+ public byte[] generateSourceStampCertificateDigest() throws SignatureException {
+ if (mSourceStampSignerConfig.getCertificates().isEmpty()) {
+ throw new SignatureException("No certificates configured for stamp");
+ }
+ try {
+ return computeSha256DigestBytes(
+ mSourceStampSignerConfig.getCertificates().get(0).getEncoded());
+ } catch (CertificateEncodingException e) {
+ throw new SignatureException("Failed to encode source stamp certificate", e);
+ }
+ }
+
+ @Override
public void close() {
mClosed = true;
@@ -1051,6 +1083,17 @@
return InputJarEntryInstructions.OutputPolicy.SKIP;
}
+ private static byte[] computeSha256DigestBytes(byte[] data) {
+ MessageDigest messageDigest;
+ try {
+ messageDigest = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("SHA-256 is not found", e);
+ }
+ messageDigest.update(data);
+ return messageDigest.digest();
+ }
+
private static class OutputJarSignatureRequestImpl implements OutputJarSignatureRequest {
private final List<JarEntry> mAdditionalJarEntries;
private volatile boolean mDone;
diff --git a/src/main/java/com/android/apksig/apk/ApkUtils.java b/src/main/java/com/android/apksig/apk/ApkUtils.java
index 135d815..870971f 100644
--- a/src/main/java/com/android/apksig/apk/ApkUtils.java
+++ b/src/main/java/com/android/apksig/apk/ApkUtils.java
@@ -41,6 +41,9 @@
*/
public static final String ANDROID_MANIFEST_ZIP_ENTRY_NAME = "AndroidManifest.xml";
+ /** Name of the SourceStamp certificate hash ZIP entry in APKs. */
+ public static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256";
+
private ApkUtils() {}
/**
diff --git a/src/test/java/com/android/apksig/ApkSignerTest.java b/src/test/java/com/android/apksig/ApkSignerTest.java
index fba292c..3b77b26 100644
--- a/src/test/java/com/android/apksig/ApkSignerTest.java
+++ b/src/test/java/com/android/apksig/ApkSignerTest.java
@@ -16,7 +16,6 @@
package com.android.apksig;
-import static com.android.apksig.ApkSigner.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME;
import static com.android.apksig.apk.ApkUtils.findZipSections;
import static org.junit.Assert.assertArrayEquals;
@@ -969,7 +968,7 @@
cdRecords.stream()
.filter(
cdRecord ->
- SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(
+ ApkUtils.SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(
cdRecord.getName()))
.findAny()
.orElse(null);