Merge "Add support for Conscrypt APK sig verify with negative modulus" am: fcbd58eed9 am: 5adb7fead6
Original change: https://android-review.googlesource.com/c/platform/tools/apksig/+/1767097
Change-Id: Ibd5933cd1bd1b831e7eea5147eca3799faf0ad1a
diff --git a/build.gradle b/build.gradle
index 4c05a77..fe75e9a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,6 +22,7 @@
implementation 'com.google.protobuf:protobuf-javalite:3.8.0'
testImplementation 'junit:junit:4.13'
testImplementation 'org.bouncycastle:bcprov-jdk15on:1.68'
+ testImplementation 'org.conscrypt:conscrypt-openjdk-uber:2.5.1'
}
protobuf {
diff --git a/src/main/java/com/android/apksig/Constants.java b/src/main/java/com/android/apksig/Constants.java
index 680c5c3..32c7375 100644
--- a/src/main/java/com/android/apksig/Constants.java
+++ b/src/main/java/com/android/apksig/Constants.java
@@ -47,4 +47,6 @@
SourceStampConstants.V1_SOURCE_STAMP_BLOCK_ID;
public static final int V2_SOURCE_STAMP_BLOCK_ID =
SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID;
+
+ public static final String OID_RSA_ENCRYPTION = "1.2.840.113549.1.1.1";
}
diff --git a/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java b/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
index 61b7b00..5b34849 100644
--- a/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
+++ b/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
@@ -16,6 +16,7 @@
package com.android.apksig.internal.apk;
+import static com.android.apksig.Constants.OID_RSA_ENCRYPTION;
import static com.android.apksig.internal.apk.ContentDigestAlgorithm.CHUNKED_SHA256;
import static com.android.apksig.internal.apk.ContentDigestAlgorithm.CHUNKED_SHA512;
import static com.android.apksig.internal.apk.ContentDigestAlgorithm.VERITY_CHUNKED_SHA256;
@@ -666,7 +667,8 @@
if ("X.509".equals(publicKey.getFormat())) {
encodedPublicKey = publicKey.getEncoded();
// if the key is an RSA key check for a negative modulus
- if ("RSA".equals(publicKey.getAlgorithm())) {
+ String keyAlgorithm = publicKey.getAlgorithm();
+ if ("RSA".equals(keyAlgorithm) || OID_RSA_ENCRYPTION.equals(keyAlgorithm)) {
try {
// Parse the encoded public key into the separate elements of the
// SubjectPublicKeyInfo to obtain the SubjectPublicKey.
diff --git a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java
index 85301ca..ee3c9d8 100644
--- a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java
+++ b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java
@@ -16,6 +16,7 @@
package com.android.apksig.internal.apk.v1;
+import static com.android.apksig.Constants.OID_RSA_ENCRYPTION;
import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoDigestAlgorithmOid;
import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoSignatureAlgorithm;
@@ -111,7 +112,7 @@
public static DigestAlgorithm getSuggestedSignatureDigestAlgorithm(
PublicKey signingKey, int minSdkVersion) throws InvalidKeyException {
String keyAlgorithm = signingKey.getAlgorithm();
- if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
+ if ("RSA".equalsIgnoreCase(keyAlgorithm) || OID_RSA_ENCRYPTION.equals((keyAlgorithm))) {
// Prior to API Level 18, only SHA-1 can be used with RSA.
if (minSdkVersion < 18) {
return DigestAlgorithm.SHA1;
diff --git a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java
index 6d7e997..0ebef0e 100644
--- a/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java
+++ b/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java
@@ -26,6 +26,7 @@
import com.android.apksig.ApkVerifier.IssueWithParams;
import com.android.apksig.apk.ApkFormatException;
import com.android.apksig.apk.ApkUtils;
+import com.android.apksig.internal.apk.ApkSigningBlockUtils;
import com.android.apksig.internal.asn1.Asn1BerParser;
import com.android.apksig.internal.asn1.Asn1Class;
import com.android.apksig.internal.asn1.Asn1DecodingException;
@@ -54,13 +55,17 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
+import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
+import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
@@ -677,7 +682,27 @@
String jcaSignatureAlgorithm =
getJcaSignatureAlgorithm(digestAlgorithmOid, signatureAlgorithmOid);
Signature s = Signature.getInstance(jcaSignatureAlgorithm);
- s.initVerify(signingCertificate.getPublicKey());
+ PublicKey publicKey = signingCertificate.getPublicKey();
+ try {
+ s.initVerify(publicKey);
+ } catch (InvalidKeyException e) {
+ // An InvalidKeyException could be caught if the PublicKey in the certificate is not
+ // properly encoded; attempt to resolve any encoding errors, generate a new public
+ // key, and reattempt the initVerify with the newly encoded key.
+ try {
+ byte[] encodedPublicKey = ApkSigningBlockUtils.encodePublicKey(publicKey);
+ publicKey = KeyFactory.getInstance(publicKey.getAlgorithm()).generatePublic(
+ new X509EncodedKeySpec(encodedPublicKey));
+ } catch (InvalidKeySpecException ikse) {
+ // If an InvalidKeySpecException is caught then throw the original Exception
+ // since the key couldn't be properly re-encoded, and the original Exception
+ // will have more useful debugging info.
+ throw e;
+ }
+ s = Signature.getInstance(jcaSignatureAlgorithm);
+ s.initVerify(publicKey);
+ }
+
if (signerInfo.signedAttrs != null) {
// Signed attributes present -- verify signature against the ASN.1 DER encoded form
// of signed attributes. This verifies integrity of the signature file because
diff --git a/src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java b/src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java
index 4185dbc..9712767 100644
--- a/src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java
+++ b/src/main/java/com/android/apksig/internal/pkcs7/AlgorithmIdentifier.java
@@ -16,6 +16,7 @@
package com.android.apksig.internal.pkcs7;
+import static com.android.apksig.Constants.OID_RSA_ENCRYPTION;
import static com.android.apksig.internal.asn1.Asn1DerEncoder.ASN1_DER_NULL;
import static com.android.apksig.internal.oid.OidConstants.OID_DIGEST_SHA1;
import static com.android.apksig.internal.oid.OidConstants.OID_DIGEST_SHA256;
@@ -92,7 +93,7 @@
throw new IllegalArgumentException(
"Unexpected digest algorithm: " + digestAlgorithm);
}
- if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
+ if ("RSA".equalsIgnoreCase(keyAlgorithm) || OID_RSA_ENCRYPTION.equals(keyAlgorithm)) {
return Pair.of(
jcaDigestPrefixForSigAlg + "withRSA",
new AlgorithmIdentifier(OID_SIG_RSA, ASN1_DER_NULL));
diff --git a/src/test/java/com/android/apksig/ApkVerifierTest.java b/src/test/java/com/android/apksig/ApkVerifierTest.java
index 9e1a75e..5a7c64d 100644
--- a/src/test/java/com/android/apksig/ApkVerifierTest.java
+++ b/src/test/java/com/android/apksig/ApkVerifierTest.java
@@ -30,6 +30,7 @@
import com.android.apksig.internal.util.Resources;
import com.android.apksig.util.DataSources;
+import java.security.Provider;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -1330,6 +1331,23 @@
}
}
+ @Test
+ public void verifySignature_negativeModulusConscryptProvider() throws Exception {
+ Provider conscryptProvider = null;
+ try {
+ conscryptProvider = new org.conscrypt.OpenSSLProvider();
+ Security.insertProviderAt(conscryptProvider, 1);
+ assertVerified(verify("v1v2v3-rsa-2048-negmod-in-cert.apk"));
+ } catch (UnsatisfiedLinkError e) {
+ // If the library for conscrypt is not available then skip this test.
+ return;
+ } finally {
+ if (conscryptProvider != null) {
+ Security.removeProvider(conscryptProvider.getName());
+ }
+ }
+ }
+
private ApkVerifier.Result verify(String apkFilenameInResources)
throws IOException, ApkFormatException, NoSuchAlgorithmException {
return verify(apkFilenameInResources, null, null);
diff --git a/src/test/resources/com/android/apksig/v1v2v3-rsa-2048-negmod-in-cert.apk b/src/test/resources/com/android/apksig/v1v2v3-rsa-2048-negmod-in-cert.apk
new file mode 100644
index 0000000..6f73b92
--- /dev/null
+++ b/src/test/resources/com/android/apksig/v1v2v3-rsa-2048-negmod-in-cert.apk
Binary files differ