Fix DSA APK signatures for API Level 8 and lower.
This modifies JAR signing code to produce DSA signatures which are
accepted by all Android platforms rather than only API Level 9 and
higher.
The issue is that by default Bouncy Castle uses OID 1.2.840.10040.4.3
(dsaWithSha1) in PKCS #7 CMS SignerInfo whereas Android accepts that
only since API Level 9. However, OID 1.2.840.10040.4.1 (dsa) is
accepted by all Android platforms.
Bug: 27461702
Change-Id: I24256a255bcdc2108bdb447557af7568a2c096e3
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
index 8b59b8e..9f4ccce 100644
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
+++ b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
@@ -41,13 +41,20 @@
import java.util.jar.Manifest;
import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignatureEncryptionAlgorithmFinder;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
-import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.DefaultCMSSignatureEncryptionAlgorithmFinder;
+import org.bouncycastle.cms.SignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
@@ -462,10 +469,11 @@
.build(signerConfig.privateKey);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addSignerInfoGenerator(
- new JcaSignerInfoGeneratorBuilder(
- new JcaDigestCalculatorProviderBuilder().build())
- .setDirectSignature(true)
- .build(signer, signerCert));
+ new SignerInfoGeneratorBuilder(
+ new JcaDigestCalculatorProviderBuilder().build(),
+ SignerInfoSignatureAlgorithmFinder.INSTANCE)
+ .setDirectSignature(true)
+ .build(signer, new JcaX509CertificateHolder(signerCert)));
gen.addCertificates(certs);
CMSSignedData sigData =
@@ -482,6 +490,37 @@
}
}
+ /**
+ * Chooser of SignatureAlgorithm for PKCS #7 CMS SignerInfo.
+ */
+ private static class SignerInfoSignatureAlgorithmFinder
+ implements CMSSignatureEncryptionAlgorithmFinder {
+ private static final SignerInfoSignatureAlgorithmFinder INSTANCE =
+ new SignerInfoSignatureAlgorithmFinder();
+
+ private static final AlgorithmIdentifier DSA =
+ new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, DERNull.INSTANCE);
+
+ private final CMSSignatureEncryptionAlgorithmFinder mDefault =
+ new DefaultCMSSignatureEncryptionAlgorithmFinder();
+
+ @Override
+ public AlgorithmIdentifier findEncryptionAlgorithm(AlgorithmIdentifier id) {
+ // Use the default chooser, but replace dsaWithSha1 with dsa. This is because "dsa" is
+ // accepted by any Android platform whereas "dsaWithSha1" is accepted only since
+ // API Level 9.
+ id = mDefault.findEncryptionAlgorithm(id);
+ if (id != null) {
+ ASN1ObjectIdentifier oid = id.getAlgorithm();
+ if (X9ObjectIdentifiers.id_dsa_with_sha1.equals(oid)) {
+ return DSA;
+ }
+ }
+
+ return id;
+ }
+ }
+
private static String getEntryDigestAttributeName(DigestAlgorithm digestAlgorithm) {
switch (digestAlgorithm) {
case SHA1: