use of com.android.apksig.apk.ApkFormatException in project AppManager by MuntashirAkon.
the class AppDetailsViewModel method loadSignatures.
@SuppressWarnings("deprecation")
@WorkerThread
private void loadSignatures() {
List<AppDetailsItem<X509Certificate>> appDetailsItems = new ArrayList<>();
if (mApkFile == null) {
mSignatures.postValue(appDetailsItems);
return;
}
try {
File idsigFile = mApkFile.getIdsigFile();
ApkVerifier.Builder builder = new ApkVerifier.Builder(mApkFile.getBaseEntry().getRealCachedFile());
if (idsigFile != null) {
builder.setV4SignatureFile(idsigFile);
}
ApkVerifier apkVerifier = builder.build();
mApkVerifierResult = apkVerifier.verify();
// Get signer certificates
List<X509Certificate> certificates = mApkVerifierResult.getSignerCertificates();
if (certificates != null && certificates.size() > 0) {
for (X509Certificate certificate : certificates) {
AppDetailsItem<X509Certificate> item = new AppDetailsItem<>(certificate);
item.name = "Signer Certificate";
appDetailsItems.add(item);
}
if (mIsExternalApk && mPackageInfo.signatures == null) {
List<Signature> signatures = new ArrayList<>(certificates.size());
for (X509Certificate certificate : certificates) {
try {
signatures.add(new Signature(certificate.getEncoded()));
} catch (CertificateEncodingException ignore) {
}
}
mPackageInfo.signatures = signatures.toArray(new Signature[0]);
}
} else {
// noinspection ConstantConditions Null is deliberately set here to get at least one row
appDetailsItems.add(new AppDetailsItem<>(null));
}
// Get source stamp certificate
if (mApkVerifierResult.isSourceStampVerified()) {
ApkVerifier.Result.SourceStampInfo sourceStampInfo = mApkVerifierResult.getSourceStampInfo();
X509Certificate certificate = sourceStampInfo.getCertificate();
if (certificate != null) {
AppDetailsItem<X509Certificate> item = new AppDetailsItem<>(certificate);
item.name = "SourceStamp Certificate";
appDetailsItems.add(item);
}
}
SigningCertificateLineage lineage = mApkVerifierResult.getSigningCertificateLineage();
if (lineage != null) {
certificates = lineage.getCertificatesInLineage();
if (certificates != null && certificates.size() > 0) {
for (X509Certificate certificate : certificates) {
AppDetailsItem<X509Certificate> item = new AppDetailsItem<>(certificate);
item.name = "Certificate for Lineage";
appDetailsItems.add(item);
}
}
}
} catch (IOException | ApkFormatException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
mSignatures.postValue(appDetailsItems);
}
use of com.android.apksig.apk.ApkFormatException in project bundletool by google.
the class ApkSignatureVerifier method verify.
/**
* Verifies signature of each APK and returns the public certificate of the app signing key.
*/
static Result verify(ImmutableList<Path> deviceSpecificApks) {
checkArgument(!deviceSpecificApks.isEmpty(), "Expected non-empty list of device-specific APKs.");
Optional<X509Certificate> apkSigningKeyCertificate = Optional.empty();
try {
for (Path apkPath : deviceSpecificApks) {
ApkVerifier.Result apkSignatureVerificationResult = new ApkVerifier.Builder(apkPath.toFile()).build().verify();
if (!apkSignatureVerificationResult.isVerified()) {
return Result.failure("APK signature invalid for " + apkPath.getFileName());
}
X509Certificate currentCertificate = apkSignatureVerificationResult.getSignerCertificates().get(0);
if (apkSigningKeyCertificate.isPresent()) {
if (!apkSigningKeyCertificate.get().equals(currentCertificate)) {
return Result.failure("APK signature verification failed: the keys used to sign the given set of device" + " specific APKs do not match.");
}
} else {
apkSigningKeyCertificate = Optional.of(currentCertificate);
}
}
} catch (IOException | ApkFormatException | NoSuchAlgorithmException e) {
throw CommandExecutionException.builder().withInternalMessage("Exception during APK signature verification.").withCause(e).build();
}
return Result.success(CodeTransparencyCryptoUtils.getCertificateFingerprint(apkSigningKeyCertificate.get()));
}
use of com.android.apksig.apk.ApkFormatException in project apksig by venshine.
the class V1SchemeSigner method generateManifestFile.
/**
* Generated and returns the {@code META-INF/MANIFEST.MF} file based on the provided (optional)
* input {@code MANIFEST.MF} and digests of JAR entries covered by the manifest.
*/
public static OutputManifestFile generateManifestFile(DigestAlgorithm jarEntryDigestAlgorithm, Map<String, byte[]> jarEntryDigests, byte[] sourceManifestBytes) throws ApkFormatException {
Manifest sourceManifest = null;
if (sourceManifestBytes != null) {
try {
sourceManifest = new Manifest(new ByteArrayInputStream(sourceManifestBytes));
} catch (IOException e) {
throw new ApkFormatException("Malformed source META-INF/MANIFEST.MF", e);
}
}
ByteArrayOutputStream manifestOut = new ByteArrayOutputStream();
Attributes mainAttrs = new Attributes();
// JAR/APK.
if (sourceManifest != null) {
mainAttrs.putAll(sourceManifest.getMainAttributes());
} else {
mainAttrs.put(Attributes.Name.MANIFEST_VERSION, ATTRIBUTE_VALUE_MANIFEST_VERSION);
}
try {
ManifestWriter.writeMainSection(manifestOut, mainAttrs);
} catch (IOException e) {
throw new RuntimeException("Failed to write in-memory MANIFEST.MF", e);
}
List<String> sortedEntryNames = new ArrayList<>(jarEntryDigests.keySet());
Collections.sort(sortedEntryNames);
SortedMap<String, byte[]> invidualSectionsContents = new TreeMap<>();
String entryDigestAttributeName = getEntryDigestAttributeName(jarEntryDigestAlgorithm);
for (String entryName : sortedEntryNames) {
checkEntryNameValid(entryName);
byte[] entryDigest = jarEntryDigests.get(entryName);
Attributes entryAttrs = new Attributes();
entryAttrs.putValue(entryDigestAttributeName, Base64.getEncoder().encodeToString(entryDigest));
ByteArrayOutputStream sectionOut = new ByteArrayOutputStream();
byte[] sectionBytes;
try {
ManifestWriter.writeIndividualSection(sectionOut, entryName, entryAttrs);
sectionBytes = sectionOut.toByteArray();
manifestOut.write(sectionBytes);
} catch (IOException e) {
throw new RuntimeException("Failed to write in-memory MANIFEST.MF", e);
}
invidualSectionsContents.put(entryName, sectionBytes);
}
OutputManifestFile result = new OutputManifestFile();
result.contents = manifestOut.toByteArray();
result.mainSectionAttributes = mainAttrs;
result.individualSectionsContents = invidualSectionsContents;
return result;
}
use of com.android.apksig.apk.ApkFormatException in project apksig by venshine.
the class SourceStampVerifier method verifySourceStamp.
/**
* Verifies the provided {@code apk}'s source stamp signature, including verification of the
* SHA-256 digest of the stamp signing certificate matches the {@code expectedCertDigest}, and
* returns the result of the verification.
*
* @see #verifySourceStamp(String)
*/
private SourceStampVerifier.Result verifySourceStamp(DataSource apk, String expectedCertDigest) {
Result result = new Result();
try {
ZipSections zipSections = ApkUtilsLite.findZipSections(apk);
// Attempt to obtain the source stamp's certificate digest from the APK.
List<CentralDirectoryRecord> cdRecords = ZipUtils.parseZipCentralDirectory(apk, zipSections);
CentralDirectoryRecord sourceStampCdRecord = null;
for (CentralDirectoryRecord cdRecord : cdRecords) {
if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) {
sourceStampCdRecord = cdRecord;
break;
}
}
// APK's signature block to determine the appropriate status to return.
if (sourceStampCdRecord == null) {
boolean stampSigningBlockFound;
try {
ApkSigningBlockUtilsLite.findSignature(apk, zipSections, SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID);
stampSigningBlockFound = true;
} catch (SignatureNotFoundException e) {
stampSigningBlockFound = false;
}
result.addVerificationError(stampSigningBlockFound ? ApkVerificationIssue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST : ApkVerificationIssue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING);
return result;
}
// Verify that the contents of the source stamp certificate digest match the expected
// value, if provided.
byte[] sourceStampCertificateDigest = LocalFileRecord.getUncompressedData(apk, sourceStampCdRecord, zipSections.getZipCentralDirectoryOffset());
if (expectedCertDigest != null) {
String actualCertDigest = ApkSigningBlockUtilsLite.toHex(sourceStampCertificateDigest);
if (!expectedCertDigest.equalsIgnoreCase(actualCertDigest)) {
result.addVerificationError(ApkVerificationIssue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH, actualCertDigest, expectedCertDigest);
return result;
}
}
Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeApkContentDigests = new HashMap<>();
if (mMaxSdkVersion >= AndroidSdkVersion.P) {
SignatureInfo signatureInfo;
try {
signatureInfo = ApkSigningBlockUtilsLite.findSignature(apk, zipSections, V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
} catch (SignatureNotFoundException e) {
signatureInfo = null;
}
if (signatureInfo != null) {
Map<ContentDigestAlgorithm, byte[]> apkContentDigests = new EnumMap<>(ContentDigestAlgorithm.class);
parseSigners(signatureInfo.signatureBlock, VERSION_APK_SIGNATURE_SCHEME_V3, apkContentDigests, result);
signatureSchemeApkContentDigests.put(VERSION_APK_SIGNATURE_SCHEME_V3, apkContentDigests);
}
}
if (mMaxSdkVersion >= AndroidSdkVersion.N && (mMinSdkVersion < AndroidSdkVersion.P || signatureSchemeApkContentDigests.isEmpty())) {
SignatureInfo signatureInfo;
try {
signatureInfo = ApkSigningBlockUtilsLite.findSignature(apk, zipSections, V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
} catch (SignatureNotFoundException e) {
signatureInfo = null;
}
if (signatureInfo != null) {
Map<ContentDigestAlgorithm, byte[]> apkContentDigests = new EnumMap<>(ContentDigestAlgorithm.class);
parseSigners(signatureInfo.signatureBlock, VERSION_APK_SIGNATURE_SCHEME_V2, apkContentDigests, result);
signatureSchemeApkContentDigests.put(VERSION_APK_SIGNATURE_SCHEME_V2, apkContentDigests);
}
}
if (mMinSdkVersion < AndroidSdkVersion.N || signatureSchemeApkContentDigests.isEmpty()) {
Map<ContentDigestAlgorithm, byte[]> apkContentDigests = getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections, result);
signatureSchemeApkContentDigests.put(VERSION_JAR_SIGNATURE_SCHEME, apkContentDigests);
}
ApkSigResult sourceStampResult = V2SourceStampVerifier.verify(apk, zipSections, sourceStampCertificateDigest, signatureSchemeApkContentDigests, mMinSdkVersion, mMaxSdkVersion);
result.mergeFrom(sourceStampResult);
return result;
} catch (ApkFormatException | IOException | ZipFormatException e) {
result.addVerificationError(ApkVerificationIssue.MALFORMED_APK, e);
} catch (NoSuchAlgorithmException e) {
result.addVerificationError(ApkVerificationIssue.UNEXPECTED_EXCEPTION, e);
} catch (SignatureNotFoundException e) {
result.addVerificationError(ApkVerificationIssue.SOURCE_STAMP_SIG_MISSING);
}
return result;
}
use of com.android.apksig.apk.ApkFormatException in project apksig by venshine.
the class SourceStampVerifier method getApkContentDigestFromV1SigningScheme.
/**
* Returns a mapping of the {@link ContentDigestAlgorithm} to the {@code byte[]} digest of the
* V1 / jar signing META-INF/MANIFEST.MF; if this file is not found then an empty {@code Map} is
* returned.
*
* <p>If any errors are encountered while parsing the V1 signers the provided {@code result}
* will be updated to include a warning, but the source stamp verification can still proceed.
*/
private static Map<ContentDigestAlgorithm, byte[]> getApkContentDigestFromV1SigningScheme(List<CentralDirectoryRecord> cdRecords, DataSource apk, ZipSections zipSections, Result result) throws IOException, ApkFormatException {
CentralDirectoryRecord manifestCdRecord = null;
List<CentralDirectoryRecord> signatureBlockRecords = new ArrayList<>(1);
Map<ContentDigestAlgorithm, byte[]> v1ContentDigest = new EnumMap<>(ContentDigestAlgorithm.class);
for (CentralDirectoryRecord cdRecord : cdRecords) {
String cdRecordName = cdRecord.getName();
if (cdRecordName == null) {
continue;
}
if (manifestCdRecord == null && MANIFEST_ENTRY_NAME.equals(cdRecordName)) {
manifestCdRecord = cdRecord;
continue;
}
if (cdRecordName.startsWith("META-INF/") && (cdRecordName.endsWith(".RSA") || cdRecordName.endsWith(".DSA") || cdRecordName.endsWith(".EC"))) {
signatureBlockRecords.add(cdRecord);
}
}
if (manifestCdRecord == null) {
// thus an empty digest will invalidate that signature.
return v1ContentDigest;
}
if (signatureBlockRecords.isEmpty()) {
result.addVerificationWarning(ApkVerificationIssue.JAR_SIG_NO_SIGNATURES);
} else {
for (CentralDirectoryRecord signatureBlockRecord : signatureBlockRecords) {
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
byte[] signatureBlockBytes = LocalFileRecord.getUncompressedData(apk, signatureBlockRecord, zipSections.getZipCentralDirectoryOffset());
for (Certificate certificate : certFactory.generateCertificates(new ByteArrayInputStream(signatureBlockBytes))) {
// first is used as the signer of this block.
if (certificate instanceof X509Certificate) {
Result.SignerInfo signerInfo = new Result.SignerInfo();
signerInfo.setSigningCertificate((X509Certificate) certificate);
result.addV1Signer(signerInfo);
break;
}
}
} catch (CertificateException e) {
// Log a warning for the parsing exception but still proceed with the stamp
// verification.
result.addVerificationWarning(ApkVerificationIssue.JAR_SIG_PARSE_EXCEPTION, signatureBlockRecord.getName(), e);
break;
} catch (ZipFormatException e) {
throw new ApkFormatException("Failed to read APK", e);
}
}
}
try {
byte[] manifestBytes = LocalFileRecord.getUncompressedData(apk, manifestCdRecord, zipSections.getZipCentralDirectoryOffset());
v1ContentDigest.put(ContentDigestAlgorithm.SHA256, computeSha256DigestBytes(manifestBytes));
return v1ContentDigest;
} catch (ZipFormatException e) {
throw new ApkFormatException("Failed to read APK", e);
}
}
Aggregations