Let caller handle NoSuchAlgorithmException.
This surfaces relevant NoSuchAlgorithmExceptions to the caller instead of rethrowing as other exception types. Some setups need to be able to distringuish issues due to their own misconfiguration (required crypto algorithm mising -- NoSuchAlgorithmException) from issues with the APK being signed or verified. Bug: 27461702 Change-Id: I993f73edb29b2cd4cc485734a89a924ec357ef19
This commit is contained in:
@@ -19,6 +19,7 @@ package com.android.apksigner.core;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.List;
|
||||
|
||||
@@ -182,13 +183,17 @@ public interface ApkSignerEngine extends Closeable {
|
||||
* request must be fulfilled before
|
||||
* {@link #outputZipSections(DataSource, DataSource, DataSource)} is invoked.
|
||||
*
|
||||
* @throws NoSuchAlgorithmException if a signature could not be generated because a required
|
||||
* cryptographic algorithm implementation is missing
|
||||
* @throws InvalidKeyException if a signature could not be generated because a signing key is
|
||||
* not suitable for generating the signature
|
||||
* @throws SignatureException if an error occurred while generating the JAR signature
|
||||
* @throws SignatureException if an error occurred while generating a signature
|
||||
* @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR
|
||||
* entries, or if the engine is closed
|
||||
*/
|
||||
OutputJarSignatureRequest outputJarEntries() throws InvalidKeyException, SignatureException;
|
||||
OutputJarSignatureRequest outputJarEntries()
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException,
|
||||
IllegalStateException;
|
||||
|
||||
/**
|
||||
* Indicates to this engine that the ZIP sections comprising the output APK have been output.
|
||||
@@ -207,16 +212,20 @@ public interface ApkSignerEngine extends Closeable {
|
||||
* {@link #outputDone()} is invoked.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs while reading the provided ZIP sections
|
||||
* @throws NoSuchAlgorithmException if a signature could not be generated because a required
|
||||
* cryptographic algorithm implementation is missing
|
||||
* @throws InvalidKeyException if a signature could not be generated because a signing key is
|
||||
* not suitable for generating the signature
|
||||
* @throws SignatureException if an error occurred while generating the APK's signature
|
||||
* @throws SignatureException if an error occurred while generating a signature
|
||||
* @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR
|
||||
* entries or to output JAR signature, or if the engine is closed
|
||||
*/
|
||||
OutputApkSigningBlockRequest outputZipSections(
|
||||
DataSource zipEntries,
|
||||
DataSource zipCentralDirectory,
|
||||
DataSource zipEocd) throws IOException, InvalidKeyException, SignatureException;
|
||||
DataSource zipEocd)
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
SignatureException, IllegalStateException;
|
||||
|
||||
/**
|
||||
* Indicates to this engine that the signed APK was output.
|
||||
|
@@ -26,6 +26,7 @@ import com.android.apksigner.core.util.DataSource;
|
||||
import com.android.apksigner.core.zip.ZipFormatException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
@@ -61,9 +62,11 @@ public class ApkVerifier {
|
||||
*
|
||||
* @throws IOException if an I/O error is encountered while reading the APK
|
||||
* @throws ZipFormatException if the APK is malformed at ZIP format level
|
||||
* @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
|
||||
* required cryptographic algorithm implementation is missing
|
||||
*/
|
||||
public Result verify(DataSource apk, int minSdkVersion, int maxSdkVersion)
|
||||
throws IOException, ZipFormatException {
|
||||
throws IOException, ZipFormatException, NoSuchAlgorithmException {
|
||||
if (minSdkVersion < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"minSdkVersion must not be negative: " + minSdkVersion);
|
||||
|
@@ -293,7 +293,7 @@ public class DefaultApkSignerEngine implements ApkSignerEngine {
|
||||
|
||||
@Override
|
||||
public OutputJarSignatureRequest outputJarEntries()
|
||||
throws InvalidKeyException, SignatureException {
|
||||
throws InvalidKeyException, SignatureException, NoSuchAlgorithmException {
|
||||
checkNotClosed();
|
||||
|
||||
if (!mV1SignaturePending) {
|
||||
@@ -413,7 +413,9 @@ public class DefaultApkSignerEngine implements ApkSignerEngine {
|
||||
public OutputApkSigningBlockRequest outputZipSections(
|
||||
DataSource zipEntries,
|
||||
DataSource zipCentralDirectory,
|
||||
DataSource zipEocd) throws IOException, InvalidKeyException, SignatureException {
|
||||
DataSource zipEocd)
|
||||
throws IOException, InvalidKeyException, SignatureException,
|
||||
NoSuchAlgorithmException {
|
||||
checkNotClosed();
|
||||
checkV1SigningDoneIfEnabled();
|
||||
if (!mV2SigningEnabled) {
|
||||
|
@@ -155,13 +155,10 @@ public abstract class V1SchemeSigner {
|
||||
/**
|
||||
* Returns a new {@link MessageDigest} instance corresponding to the provided digest algorithm.
|
||||
*/
|
||||
public static MessageDigest getMessageDigestInstance(DigestAlgorithm digestAlgorithm) {
|
||||
private static MessageDigest getMessageDigestInstance(DigestAlgorithm digestAlgorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm();
|
||||
try {
|
||||
return MessageDigest.getInstance(jcaAlgorithm);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Failed to obtain " + jcaAlgorithm + " MessageDigest", e);
|
||||
}
|
||||
return MessageDigest.getInstance(jcaAlgorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,6 +212,8 @@ public abstract class V1SchemeSigner {
|
||||
* @param signerConfigs signer configurations, one for each signer. At least one signer config
|
||||
* must be provided.
|
||||
*
|
||||
* @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is
|
||||
* missing
|
||||
* @throws InvalidKeyException if a signing key is not suitable for this signature scheme or
|
||||
* cannot be used in general
|
||||
* @throws SignatureException if an error occurs when computing digests of generating
|
||||
@@ -226,7 +225,8 @@ public abstract class V1SchemeSigner {
|
||||
Map<String, byte[]> jarEntryDigests,
|
||||
List<Integer> apkSigningSchemeIds,
|
||||
byte[] sourceManifestBytes)
|
||||
throws InvalidKeyException, CertificateException, SignatureException {
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, CertificateException,
|
||||
SignatureException {
|
||||
if (signerConfigs.isEmpty()) {
|
||||
throw new IllegalArgumentException("At least one signer config must be provided");
|
||||
}
|
||||
@@ -253,7 +253,8 @@ public abstract class V1SchemeSigner {
|
||||
DigestAlgorithm digestAlgorithm,
|
||||
List<Integer> apkSigningSchemeIds,
|
||||
OutputManifestFile manifest)
|
||||
throws InvalidKeyException, CertificateException, SignatureException {
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, CertificateException,
|
||||
SignatureException {
|
||||
if (signerConfigs.isEmpty()) {
|
||||
throw new IllegalArgumentException("At least one signer config must be provided");
|
||||
}
|
||||
@@ -378,7 +379,7 @@ public abstract class V1SchemeSigner {
|
||||
private static byte[] generateSignatureFile(
|
||||
List<Integer> apkSignatureSchemeIds,
|
||||
DigestAlgorithm manifestDigestAlgorithm,
|
||||
OutputManifestFile manifest) {
|
||||
OutputManifestFile manifest) throws NoSuchAlgorithmException {
|
||||
Manifest sf = new Manifest();
|
||||
Attributes mainAttrs = sf.getMainAttributes();
|
||||
mainAttrs.put(Attributes.Name.SIGNATURE_VERSION, ATTRIBUTE_VALUE_SIGNATURE_VERSION);
|
||||
@@ -447,7 +448,8 @@ public abstract class V1SchemeSigner {
|
||||
@SuppressWarnings("restriction")
|
||||
private static byte[] generateSignatureBlock(
|
||||
SignerConfig signerConfig, byte[] signatureFileBytes)
|
||||
throws InvalidKeyException, CertificateException, SignatureException {
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, CertificateException,
|
||||
SignatureException {
|
||||
List<X509Certificate> signerCerts = signerConfig.certificates;
|
||||
X509Certificate signerCert = signerCerts.get(0);
|
||||
PublicKey signerPublicKey = signerCert.getPublicKey();
|
||||
@@ -455,16 +457,10 @@ public abstract class V1SchemeSigner {
|
||||
Pair<String, AlgorithmId> signatureAlgs =
|
||||
getSignerInfoSignatureAlgorithm(signerPublicKey, digestAlgorithm);
|
||||
String jcaSignatureAlgorithm = signatureAlgs.getFirst();
|
||||
byte[] signatureBytes;
|
||||
try {
|
||||
Signature signature = Signature.getInstance(jcaSignatureAlgorithm);
|
||||
signature.initSign(signerConfig.privateKey);
|
||||
signature.update(signatureFileBytes);
|
||||
signatureBytes = signature.sign();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new SignatureException(
|
||||
jcaSignatureAlgorithm + " Signature implementation not found", e);
|
||||
}
|
||||
Signature signature = Signature.getInstance(jcaSignatureAlgorithm);
|
||||
signature.initSign(signerConfig.privateKey);
|
||||
signature.update(signatureFileBytes);
|
||||
byte[] signatureBytes = signature.sign();
|
||||
|
||||
X500Name issuerName;
|
||||
try {
|
||||
|
@@ -72,6 +72,8 @@ public abstract class V1SchemeVerifier {
|
||||
*
|
||||
* @throws ZipFormatException if the APK is malformed
|
||||
* @throws IOException if an I/O error occurs when reading the APK
|
||||
* @throws NoSuchAlgorithmException if the APK's JAR signatures cannot be verified because a
|
||||
* required cryptographic algorithm implementation is missing
|
||||
*/
|
||||
public static Result verify(
|
||||
DataSource apk,
|
||||
@@ -79,7 +81,7 @@ public abstract class V1SchemeVerifier {
|
||||
Map<Integer, String> supportedApkSigSchemeNames,
|
||||
Set<Integer> foundApkSigSchemeIds,
|
||||
int minSdkVersion,
|
||||
int maxSdkVersion) throws IOException, ZipFormatException {
|
||||
int maxSdkVersion) throws IOException, ZipFormatException, NoSuchAlgorithmException {
|
||||
if (minSdkVersion > maxSdkVersion) {
|
||||
throw new IllegalArgumentException(
|
||||
"minSdkVersion (" + minSdkVersion + ") > maxSdkVersion (" + maxSdkVersion
|
||||
@@ -152,7 +154,7 @@ public abstract class V1SchemeVerifier {
|
||||
Set<Integer> foundApkSigSchemeIds,
|
||||
int minSdkVersion,
|
||||
int maxSdkVersion,
|
||||
Result result) throws ZipFormatException, IOException {
|
||||
Result result) throws ZipFormatException, IOException, NoSuchAlgorithmException {
|
||||
|
||||
// Find JAR manifest and signature block files.
|
||||
CentralDirectoryRecord manifestEntry = null;
|
||||
@@ -312,6 +314,8 @@ public abstract class V1SchemeVerifier {
|
||||
cdRecords,
|
||||
entryNameToManifestSection,
|
||||
signers,
|
||||
minSdkVersion,
|
||||
maxSdkVersion,
|
||||
result);
|
||||
if (result.containsErrors()) {
|
||||
return;
|
||||
@@ -405,7 +409,7 @@ public abstract class V1SchemeVerifier {
|
||||
@SuppressWarnings("restriction")
|
||||
public void verifySigBlockAgainstSigFile(
|
||||
DataSource apk, long cdStartOffset, int minSdkVersion, int maxSdkVersion)
|
||||
throws IOException, ZipFormatException {
|
||||
throws IOException, ZipFormatException, NoSuchAlgorithmException {
|
||||
byte[] sigBlockBytes =
|
||||
LocalFileHeader.getUncompressedData(
|
||||
apk, 0,
|
||||
@@ -461,7 +465,7 @@ public abstract class V1SchemeVerifier {
|
||||
}
|
||||
try {
|
||||
verifiedSignerInfo = sigBlock.verify(unverifiedSignerInfo, mSigFileBytes);
|
||||
} catch (NoSuchAlgorithmException | SignatureException e) {
|
||||
} catch (SignatureException e) {
|
||||
mResult.addError(
|
||||
Issue.JAR_SIG_VERIFY_EXCEPTION,
|
||||
mSignatureBlockEntry.getName(),
|
||||
@@ -856,7 +860,7 @@ public abstract class V1SchemeVerifier {
|
||||
Map<Integer, String> supportedApkSigSchemeNames,
|
||||
Set<Integer> foundApkSigSchemeIds,
|
||||
int minSdkVersion,
|
||||
int maxSdkVersion) {
|
||||
int maxSdkVersion) throws NoSuchAlgorithmException {
|
||||
// Inspect the main section of the .SF file.
|
||||
ManifestParser sf = new ManifestParser(mSigFileBytes);
|
||||
ManifestParser.Section sfMainSection = sf.readSection();
|
||||
@@ -965,7 +969,7 @@ public abstract class V1SchemeVerifier {
|
||||
boolean createdBySigntool,
|
||||
byte[] manifestBytes,
|
||||
int minSdkVersion,
|
||||
int maxSdkVersion) {
|
||||
int maxSdkVersion) throws NoSuchAlgorithmException {
|
||||
Collection<NamedDigest> expectedDigests =
|
||||
getDigestsToVerify(
|
||||
sfMainSection,
|
||||
@@ -1008,7 +1012,7 @@ public abstract class V1SchemeVerifier {
|
||||
ManifestParser.Section manifestMainSection,
|
||||
byte[] manifestBytes,
|
||||
int minSdkVersion,
|
||||
int maxSdkVersion) {
|
||||
int maxSdkVersion) throws NoSuchAlgorithmException {
|
||||
Collection<NamedDigest> expectedDigests =
|
||||
getDigestsToVerify(
|
||||
sfMainSection,
|
||||
@@ -1049,7 +1053,7 @@ public abstract class V1SchemeVerifier {
|
||||
ManifestParser.Section manifestIndividualSection,
|
||||
byte[] manifestBytes,
|
||||
int minSdkVersion,
|
||||
int maxSdkVersion) {
|
||||
int maxSdkVersion) throws NoSuchAlgorithmException {
|
||||
String entryName = sfIndividualSection.getName();
|
||||
Collection<NamedDigest> expectedDigests =
|
||||
getDigestsToVerify(
|
||||
@@ -1344,7 +1348,9 @@ public abstract class V1SchemeVerifier {
|
||||
Collection<CentralDirectoryRecord> cdRecords,
|
||||
Map<String, ManifestParser.Section> entryNameToManifestSection,
|
||||
List<Signer> signers,
|
||||
Result result) throws ZipFormatException, IOException {
|
||||
int minSdkVersion,
|
||||
int maxSdkVersion,
|
||||
Result result) throws ZipFormatException, IOException, NoSuchAlgorithmException {
|
||||
// Iterate over APK contents as sequentially as possible to improve performance.
|
||||
List<CentralDirectoryRecord> cdRecordsSortedByLocalFileHeaderOffset =
|
||||
new ArrayList<>(cdRecords);
|
||||
@@ -1391,22 +1397,8 @@ public abstract class V1SchemeVerifier {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<NamedDigest> expectedDigests = new ArrayList<>();
|
||||
for (ManifestParser.Attribute attr : manifestSection.getAttributes()) {
|
||||
String name = attr.getName();
|
||||
String nameUpperCase = name.toUpperCase(Locale.US);
|
||||
if (!nameUpperCase.endsWith("-DIGEST")) {
|
||||
continue;
|
||||
}
|
||||
String jcaDigestAlgorithm =
|
||||
nameUpperCase.substring(0, nameUpperCase.length() - "-DIGEST".length());
|
||||
if ("SHA1".equals(jcaDigestAlgorithm)) {
|
||||
jcaDigestAlgorithm = "SHA-1";
|
||||
}
|
||||
byte[] digest = Base64.getDecoder().decode(attr.getValue());
|
||||
expectedDigests.add(new NamedDigest(jcaDigestAlgorithm, digest));
|
||||
}
|
||||
|
||||
Collection<NamedDigest> expectedDigests =
|
||||
getDigestsToVerify(manifestSection, "-Digest", minSdkVersion, maxSdkVersion);
|
||||
if (expectedDigests.isEmpty()) {
|
||||
result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
|
||||
continue;
|
||||
@@ -1465,21 +1457,19 @@ public abstract class V1SchemeVerifier {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static MessageDigest getMessageDigest(String algorithm) {
|
||||
try {
|
||||
return MessageDigest.getInstance(algorithm);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Failed to obtain " + algorithm + " MessageDigest", e);
|
||||
}
|
||||
private static MessageDigest getMessageDigest(String algorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
return MessageDigest.getInstance(algorithm);
|
||||
}
|
||||
|
||||
private static byte[] digest(String algorithm, byte[] data, int offset, int length) {
|
||||
private static byte[] digest(String algorithm, byte[] data, int offset, int length)
|
||||
throws NoSuchAlgorithmException {
|
||||
MessageDigest md = getMessageDigest(algorithm);
|
||||
md.update(data, offset, length);
|
||||
return md.digest();
|
||||
}
|
||||
|
||||
private static byte[] digest(String algorithm, byte[] data) {
|
||||
private static byte[] digest(String algorithm, byte[] data) throws NoSuchAlgorithmException {
|
||||
return getMessageDigest(algorithm).digest(data);
|
||||
}
|
||||
|
||||
|
@@ -163,6 +163,8 @@ public abstract class V2SchemeSigner {
|
||||
* must be provided.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is
|
||||
* missing
|
||||
* @throws InvalidKeyException if a signing key is not suitable for this signature scheme or
|
||||
* cannot be used in general
|
||||
* @throws SignatureException if an error occurs when computing digests of generating
|
||||
@@ -173,7 +175,8 @@ public abstract class V2SchemeSigner {
|
||||
DataSource centralDir,
|
||||
DataSource eocd,
|
||||
List<SignerConfig> signerConfigs)
|
||||
throws IOException, InvalidKeyException, SignatureException {
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
SignatureException {
|
||||
if (signerConfigs.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"No signer configs provided. At least one is required");
|
||||
@@ -219,7 +222,7 @@ public abstract class V2SchemeSigner {
|
||||
|
||||
static Map<ContentDigestAlgorithm, byte[]> computeContentDigests(
|
||||
Set<ContentDigestAlgorithm> digestAlgorithms,
|
||||
DataSource[] contents) throws IOException, DigestException {
|
||||
DataSource[] contents) throws IOException, NoSuchAlgorithmException, DigestException {
|
||||
// For each digest algorithm the result is computed as follows:
|
||||
// 1. Each segment of contents is split into consecutive chunks of 1 MB in size.
|
||||
// The final chunk will be shorter iff the length of segment is not a multiple of 1 MB.
|
||||
@@ -256,11 +259,7 @@ public abstract class V2SchemeSigner {
|
||||
chunkCount, concatenationOfChunkCountAndChunkDigests, 1);
|
||||
digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests;
|
||||
String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm();
|
||||
try {
|
||||
mds[i] = MessageDigest.getInstance(jcaAlgorithm);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(jcaAlgorithm + " MessageDigest not supported", e);
|
||||
}
|
||||
mds[i] = MessageDigest.getInstance(jcaAlgorithm);
|
||||
}
|
||||
|
||||
MessageDigestSink mdSink = new MessageDigestSink(mds);
|
||||
@@ -338,7 +337,7 @@ public abstract class V2SchemeSigner {
|
||||
private static byte[] generateApkSigningBlock(
|
||||
List<SignerConfig> signerConfigs,
|
||||
Map<ContentDigestAlgorithm, byte[]> contentDigests)
|
||||
throws InvalidKeyException, SignatureException {
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
byte[] apkSignatureSchemeV2Block =
|
||||
generateApkSignatureSchemeV2Block(signerConfigs, contentDigests);
|
||||
return generateApkSigningBlock(apkSignatureSchemeV2Block);
|
||||
@@ -379,7 +378,7 @@ public abstract class V2SchemeSigner {
|
||||
private static byte[] generateApkSignatureSchemeV2Block(
|
||||
List<SignerConfig> signerConfigs,
|
||||
Map<ContentDigestAlgorithm, byte[]> contentDigests)
|
||||
throws InvalidKeyException, SignatureException {
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
// FORMAT:
|
||||
// * length-prefixed sequence of length-prefixed signer blocks.
|
||||
|
||||
@@ -407,7 +406,7 @@ public abstract class V2SchemeSigner {
|
||||
private static byte[] generateSignerBlock(
|
||||
SignerConfig signerConfig,
|
||||
Map<ContentDigestAlgorithm, byte[]> contentDigests)
|
||||
throws InvalidKeyException, SignatureException {
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
if (signerConfig.certificates.isEmpty()) {
|
||||
throw new SignatureException("No certificates configured for signer");
|
||||
}
|
||||
@@ -470,10 +469,9 @@ public abstract class V2SchemeSigner {
|
||||
signature.update(signer.signedData);
|
||||
signatureBytes = signature.sign();
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new InvalidKeyException("Failed sign using " + jcaSignatureAlgorithm, e);
|
||||
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
|
||||
| SignatureException e) {
|
||||
throw new SignatureException("Failed sign using " + jcaSignatureAlgorithm, e);
|
||||
throw new InvalidKeyException("Failed to sign using " + jcaSignatureAlgorithm, e);
|
||||
} catch (InvalidAlgorithmParameterException | SignatureException e) {
|
||||
throw new SignatureException("Failed to sign using " + jcaSignatureAlgorithm, e);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -487,12 +485,13 @@ public abstract class V2SchemeSigner {
|
||||
throw new SignatureException("Signature did not verify");
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new InvalidKeyException("Failed to verify generated " + jcaSignatureAlgorithm
|
||||
+ " signature using public key from certificate", e);
|
||||
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
|
||||
| SignatureException e) {
|
||||
throw new SignatureException("Failed to verify generated " + jcaSignatureAlgorithm
|
||||
+ " signature using public key from certificate", e);
|
||||
throw new InvalidKeyException(
|
||||
"Failed to verify generated " + jcaSignatureAlgorithm + " signature using"
|
||||
+ " public key from certificate", e);
|
||||
} catch (InvalidAlgorithmParameterException | SignatureException e) {
|
||||
throw new SignatureException(
|
||||
"Failed to verify generated " + jcaSignatureAlgorithm + " signature using"
|
||||
+ " public key from certificate", e);
|
||||
}
|
||||
|
||||
signer.signatures.add(Pair.of(signatureAlgorithm.getId(), signatureBytes));
|
||||
@@ -526,7 +525,8 @@ public abstract class V2SchemeSigner {
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] encodePublicKey(PublicKey publicKey) throws InvalidKeyException {
|
||||
private static byte[] encodePublicKey(PublicKey publicKey)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
byte[] encodedPublicKey = null;
|
||||
if ("X.509".equals(publicKey.getFormat())) {
|
||||
encodedPublicKey = publicKey.getEncoded();
|
||||
@@ -537,11 +537,6 @@ public abstract class V2SchemeSigner {
|
||||
KeyFactory.getInstance(publicKey.getAlgorithm())
|
||||
.getKeySpec(publicKey, X509EncodedKeySpec.class)
|
||||
.getEncoded();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new InvalidKeyException(
|
||||
"Failed to obtain X.509 encoded form of public key " + publicKey
|
||||
+ " of class " + publicKey.getClass().getName(),
|
||||
e);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw new InvalidKeyException(
|
||||
"Failed to obtain X.509 encoded form of public key " + publicKey
|
||||
|
@@ -31,9 +31,13 @@ import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.DigestException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
@@ -74,11 +78,13 @@ public abstract class V2SchemeVerifier {
|
||||
* verification. APK is considered verified only if {@link Result#verified} is {@code true}. If
|
||||
* verification fails, the result will contain errors -- see {@link Result#getErrors()}.
|
||||
*
|
||||
* @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
|
||||
* required cryptographic algorithm implementation is missing
|
||||
* @throws SignatureNotFoundException if no APK Signature Scheme v2 signatures are found
|
||||
* @throws IOException if an I/O error occurs when reading the APK
|
||||
*/
|
||||
public static Result verify(DataSource apk, ApkUtils.ZipSections zipSections)
|
||||
throws IOException, SignatureNotFoundException {
|
||||
throws IOException, NoSuchAlgorithmException, SignatureNotFoundException {
|
||||
Result result = new Result();
|
||||
SignatureInfo signatureInfo = findSignature(apk, zipSections, result);
|
||||
|
||||
@@ -107,7 +113,7 @@ public abstract class V2SchemeVerifier {
|
||||
ByteBuffer apkSignatureSchemeV2Block,
|
||||
DataSource centralDir,
|
||||
ByteBuffer eocd,
|
||||
Result result) throws IOException {
|
||||
Result result) throws IOException, NoSuchAlgorithmException {
|
||||
Set<ContentDigestAlgorithm> contentDigestsToVerify = new HashSet<>(1);
|
||||
parseSigners(apkSignatureSchemeV2Block, contentDigestsToVerify, result);
|
||||
if (result.containsErrors()) {
|
||||
@@ -131,7 +137,7 @@ public abstract class V2SchemeVerifier {
|
||||
private static void parseSigners(
|
||||
ByteBuffer apkSignatureSchemeV2Block,
|
||||
Set<ContentDigestAlgorithm> contentDigestsToVerify,
|
||||
Result result) {
|
||||
Result result) throws NoSuchAlgorithmException {
|
||||
ByteBuffer signers;
|
||||
try {
|
||||
signers = getLengthPrefixedSlice(apkSignatureSchemeV2Block);
|
||||
@@ -178,7 +184,8 @@ public abstract class V2SchemeVerifier {
|
||||
ByteBuffer signerBlock,
|
||||
CertificateFactory certFactory,
|
||||
Result.SignerInfo result,
|
||||
Set<ContentDigestAlgorithm> contentDigestsToVerify) throws IOException {
|
||||
Set<ContentDigestAlgorithm> contentDigestsToVerify)
|
||||
throws IOException, NoSuchAlgorithmException {
|
||||
ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
|
||||
byte[] signedDataBytes = new byte[signedData.remaining()];
|
||||
signedData.get(signedDataBytes);
|
||||
@@ -252,7 +259,8 @@ public abstract class V2SchemeVerifier {
|
||||
}
|
||||
result.verifiedSignatures.put(signatureAlgorithm, sigBytes);
|
||||
contentDigestsToVerify.add(signatureAlgorithm.getContentDigestAlgorithm());
|
||||
} catch (Exception e) {
|
||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException
|
||||
| SignatureException e) {
|
||||
result.addError(Issue.V2_SIG_VERIFY_EXCEPTION, signatureAlgorithm, e);
|
||||
return;
|
||||
}
|
||||
@@ -440,7 +448,7 @@ public abstract class V2SchemeVerifier {
|
||||
DataSource centralDir,
|
||||
ByteBuffer eocd,
|
||||
Set<ContentDigestAlgorithm> contentDigestAlgorithms,
|
||||
Result result) throws IOException {
|
||||
Result result) throws IOException, NoSuchAlgorithmException {
|
||||
if (contentDigestAlgorithms.isEmpty()) {
|
||||
// This should never occur because this method is invoked once at least one signature
|
||||
// is verified, meaning at least one content digest is known.
|
||||
|
Reference in New Issue
Block a user