use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class ApkVerifier method collectApkContentDigests.
private static void collectApkContentDigests(List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> contentDigests, Map<ContentDigestAlgorithm, byte[]> apkContentDigests) {
for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest : contentDigests) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(contentDigest.getSignatureAlgorithmId());
if (signatureAlgorithm == null) {
continue;
}
ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm();
apkContentDigests.put(contentDigestAlgorithm, contentDigest.getValue());
}
}
use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class SourceStampVerifier method parseSigner.
/**
* Parses the provided signer block and populates the {@code result}.
*
* <p>This verifies signatures over {@code signed-data} contained in this block but does not
* verify the integrity of the rest of the APK. To facilitate APK integrity verification, this
* method adds the {@code contentDigestsToVerify}. These digests can then be used to verify the
* integrity of the APK.
*
* <p>This method adds one or more errors to the {@code result} if a verification error is
* expected to be encountered on an Android platform version in the
* {@code [minSdkVersion, maxSdkVersion]} range.
*/
private static void parseSigner(ByteBuffer signerBlock, int apkSigSchemeVersion, CertificateFactory certFactory, Map<ContentDigestAlgorithm, byte[]> apkContentDigests, Result.SignerInfo signerInfo) throws ApkFormatException {
boolean isV2Signer = apkSigSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2;
// Both the V2 and V3 signer blocks contain the following:
// * length-prefixed signed data
// * length-prefixed sequence of length-prefixed digests:
// * uint32: signature algorithm ID
// * length-prefixed bytes: digest of contents
// * length-prefixed sequence of certificates:
// * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded).
ByteBuffer signedData = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(signerBlock);
ByteBuffer digests = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(signedData);
ByteBuffer certificates = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(signedData);
// Parse the digests block
while (digests.hasRemaining()) {
try {
ByteBuffer digest = ApkSigningBlockUtilsLite.getLengthPrefixedSlice(digests);
int sigAlgorithmId = digest.getInt();
byte[] digestBytes = ApkSigningBlockUtilsLite.readLengthPrefixedByteArray(digest);
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId);
if (signatureAlgorithm == null) {
continue;
}
apkContentDigests.put(signatureAlgorithm.getContentDigestAlgorithm(), digestBytes);
} catch (ApkFormatException | BufferUnderflowException e) {
signerInfo.addVerificationWarning(isV2Signer ? ApkVerificationIssue.V2_SIG_MALFORMED_DIGEST : ApkVerificationIssue.V3_SIG_MALFORMED_DIGEST);
return;
}
}
// Parse the certificates block
if (certificates.hasRemaining()) {
byte[] encodedCert = ApkSigningBlockUtilsLite.readLengthPrefixedByteArray(certificates);
X509Certificate certificate;
try {
certificate = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
} catch (CertificateException e) {
signerInfo.addVerificationWarning(isV2Signer ? ApkVerificationIssue.V2_SIG_MALFORMED_CERTIFICATE : ApkVerificationIssue.V3_SIG_MALFORMED_CERTIFICATE);
return;
}
// Wrap the cert so that the result's getEncoded returns exactly the original encoded
// form. Without this, getEncoded may return a different form from what was stored in
// the signature. This is because some X509Certificate(Factory) implementations
// re-encode certificates.
certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedCert);
signerInfo.setSigningCertificate(certificate);
}
if (signerInfo.getSigningCertificate() == null) {
signerInfo.addVerificationWarning(isV2Signer ? ApkVerificationIssue.V2_SIG_NO_CERTIFICATES : ApkVerificationIssue.V3_SIG_NO_CERTIFICATES);
return;
}
}
use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class V3SchemeVerifier method parseSigner.
/**
* Parses the provided signer block and populates the {@code result}.
*
* <p>This verifies signatures over {@code signed-data} contained in this block, as well as
* the data contained therein, but does not verify the integrity of the rest of the APK. To
* facilitate APK integrity verification, this method adds the {@code contentDigestsToVerify}.
* These digests can then be used to verify the integrity of the APK.
*
* <p>This method adds one or more errors to the {@code result} if a verification error is
* expected to be encountered on an Android platform version in the
* {@code [minSdkVersion, maxSdkVersion]} range.
*/
private static void parseSigner(ByteBuffer signerBlock, CertificateFactory certFactory, ApkSigningBlockUtils.Result.SignerInfo result, Set<ContentDigestAlgorithm> contentDigestsToVerify) throws ApkFormatException, NoSuchAlgorithmException {
ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
byte[] signedDataBytes = new byte[signedData.remaining()];
signedData.get(signedDataBytes);
signedData.flip();
result.signedData = signedDataBytes;
int parsedMinSdkVersion = signerBlock.getInt();
int parsedMaxSdkVersion = signerBlock.getInt();
result.minSdkVersion = parsedMinSdkVersion;
result.maxSdkVersion = parsedMaxSdkVersion;
if (parsedMinSdkVersion < 0 || parsedMinSdkVersion > parsedMaxSdkVersion) {
result.addError(Issue.V3_SIG_INVALID_SDK_VERSIONS, parsedMinSdkVersion, parsedMaxSdkVersion);
}
ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
// Parse the signatures block and identify supported signatures
int signatureCount = 0;
List<ApkSigningBlockUtils.SupportedSignature> supportedSignatures = new ArrayList<>(1);
while (signatures.hasRemaining()) {
signatureCount++;
try {
ByteBuffer signature = getLengthPrefixedSlice(signatures);
int sigAlgorithmId = signature.getInt();
byte[] sigBytes = readLengthPrefixedByteArray(signature);
result.signatures.add(new ApkSigningBlockUtils.Result.SignerInfo.Signature(sigAlgorithmId, sigBytes));
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId);
if (signatureAlgorithm == null) {
result.addWarning(Issue.V3_SIG_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId);
continue;
}
// TODO consider dropping deprecated signatures for v3 or modifying
// getSignaturesToVerify (called below)
supportedSignatures.add(new ApkSigningBlockUtils.SupportedSignature(signatureAlgorithm, sigBytes));
} catch (ApkFormatException | BufferUnderflowException e) {
result.addError(Issue.V3_SIG_MALFORMED_SIGNATURE, signatureCount);
return;
}
}
if (result.signatures.isEmpty()) {
result.addError(Issue.V3_SIG_NO_SIGNATURES);
return;
}
// Verify signatures over signed-data block using the public key
List<ApkSigningBlockUtils.SupportedSignature> signaturesToVerify = null;
try {
signaturesToVerify = ApkSigningBlockUtils.getSignaturesToVerify(supportedSignatures, result.minSdkVersion, result.maxSdkVersion);
} catch (ApkSigningBlockUtils.NoSupportedSignaturesException e) {
result.addError(Issue.V3_SIG_NO_SUPPORTED_SIGNATURES);
return;
}
for (ApkSigningBlockUtils.SupportedSignature signature : signaturesToVerify) {
SignatureAlgorithm signatureAlgorithm = signature.algorithm;
String jcaSignatureAlgorithm = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst();
AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond();
String keyAlgorithm = signatureAlgorithm.getJcaKeyAlgorithm();
PublicKey publicKey;
try {
publicKey = KeyFactory.getInstance(keyAlgorithm).generatePublic(new X509EncodedKeySpec(publicKeyBytes));
} catch (Exception e) {
result.addError(Issue.V3_SIG_MALFORMED_PUBLIC_KEY, e);
return;
}
try {
Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
sig.initVerify(publicKey);
if (jcaSignatureAlgorithmParams != null) {
sig.setParameter(jcaSignatureAlgorithmParams);
}
signedData.position(0);
sig.update(signedData);
byte[] sigBytes = signature.signature;
if (!sig.verify(sigBytes)) {
result.addError(Issue.V3_SIG_DID_NOT_VERIFY, signatureAlgorithm);
return;
}
result.verifiedSignatures.put(signatureAlgorithm, sigBytes);
contentDigestsToVerify.add(signatureAlgorithm.getContentDigestAlgorithm());
} catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) {
result.addError(Issue.V3_SIG_VERIFY_EXCEPTION, signatureAlgorithm, e);
return;
}
}
// At least one signature over signedData has verified. We can now parse signed-data.
signedData.position(0);
ByteBuffer digests = getLengthPrefixedSlice(signedData);
ByteBuffer certificates = getLengthPrefixedSlice(signedData);
int signedMinSdkVersion = signedData.getInt();
if (signedMinSdkVersion != parsedMinSdkVersion) {
result.addError(Issue.V3_MIN_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD, parsedMinSdkVersion, signedMinSdkVersion);
}
int signedMaxSdkVersion = signedData.getInt();
if (signedMaxSdkVersion != parsedMaxSdkVersion) {
result.addError(Issue.V3_MAX_SDK_VERSION_MISMATCH_BETWEEN_SIGNER_AND_SIGNED_DATA_RECORD, parsedMaxSdkVersion, signedMaxSdkVersion);
}
ByteBuffer additionalAttributes = getLengthPrefixedSlice(signedData);
// Parse the certificates block
int certificateIndex = -1;
while (certificates.hasRemaining()) {
certificateIndex++;
byte[] encodedCert = readLengthPrefixedByteArray(certificates);
X509Certificate certificate;
try {
certificate = X509CertificateUtils.generateCertificate(encodedCert, certFactory);
} catch (CertificateException e) {
result.addError(Issue.V3_SIG_MALFORMED_CERTIFICATE, certificateIndex, certificateIndex + 1, e);
return;
}
// Wrap the cert so that the result's getEncoded returns exactly the original encoded
// form. Without this, getEncoded may return a different form from what was stored in
// the signature. This is because some X509Certificate(Factory) implementations
// re-encode certificates.
certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedCert);
result.certs.add(certificate);
}
if (result.certs.isEmpty()) {
result.addError(Issue.V3_SIG_NO_CERTIFICATES);
return;
}
X509Certificate mainCertificate = result.certs.get(0);
byte[] certificatePublicKeyBytes;
try {
certificatePublicKeyBytes = ApkSigningBlockUtils.encodePublicKey(mainCertificate.getPublicKey());
} catch (InvalidKeyException e) {
System.out.println("Caught an exception encoding the public key: " + e);
e.printStackTrace();
certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
}
if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
result.addError(Issue.V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD, ApkSigningBlockUtils.toHex(certificatePublicKeyBytes), ApkSigningBlockUtils.toHex(publicKeyBytes));
return;
}
// Parse the digests block
int digestCount = 0;
while (digests.hasRemaining()) {
digestCount++;
try {
ByteBuffer digest = getLengthPrefixedSlice(digests);
int sigAlgorithmId = digest.getInt();
byte[] digestBytes = readLengthPrefixedByteArray(digest);
result.contentDigests.add(new ApkSigningBlockUtils.Result.SignerInfo.ContentDigest(sigAlgorithmId, digestBytes));
} catch (ApkFormatException | BufferUnderflowException e) {
result.addError(Issue.V3_SIG_MALFORMED_DIGEST, digestCount);
return;
}
}
List<Integer> sigAlgsFromSignaturesRecord = new ArrayList<>(result.signatures.size());
for (ApkSigningBlockUtils.Result.SignerInfo.Signature signature : result.signatures) {
sigAlgsFromSignaturesRecord.add(signature.getAlgorithmId());
}
List<Integer> sigAlgsFromDigestsRecord = new ArrayList<>(result.contentDigests.size());
for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest digest : result.contentDigests) {
sigAlgsFromDigestsRecord.add(digest.getSignatureAlgorithmId());
}
if (!sigAlgsFromSignaturesRecord.equals(sigAlgsFromDigestsRecord)) {
result.addError(Issue.V3_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS, sigAlgsFromSignaturesRecord, sigAlgsFromDigestsRecord);
return;
}
// Parse the additional attributes block.
int additionalAttributeCount = 0;
while (additionalAttributes.hasRemaining()) {
additionalAttributeCount++;
try {
ByteBuffer attribute = getLengthPrefixedSlice(additionalAttributes);
int id = attribute.getInt();
byte[] value = ByteBufferUtils.toByteArray(attribute);
result.additionalAttributes.add(new ApkSigningBlockUtils.Result.SignerInfo.AdditionalAttribute(id, value));
if (id == V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID) {
try {
// SigningCertificateLineage is verified when built
result.signingCertificateLineage = SigningCertificateLineage.readFromV3AttributeValue(value);
// make sure that the last cert in the chain matches this signer cert
SigningCertificateLineage subLineage = result.signingCertificateLineage.getSubLineage(result.certs.get(0));
if (result.signingCertificateLineage.size() != subLineage.size()) {
result.addError(Issue.V3_SIG_POR_CERT_MISMATCH);
}
} catch (SecurityException e) {
result.addError(Issue.V3_SIG_POR_DID_NOT_VERIFY);
} catch (IllegalArgumentException e) {
result.addError(Issue.V3_SIG_POR_CERT_MISMATCH);
} catch (Exception e) {
result.addError(Issue.V3_SIG_MALFORMED_LINEAGE);
}
} else {
result.addWarning(Issue.V3_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE, id);
}
} catch (ApkFormatException | BufferUnderflowException e) {
result.addError(Issue.V3_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE, additionalAttributeCount);
return;
}
}
}
use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class V4SchemeSigner method pickBestDigest.
private static byte[] pickBestDigest(List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> contentDigests) throws SignatureException {
if (contentDigests == null || contentDigests.isEmpty()) {
throw new SignatureException("Should have at least one digest");
}
int bestAlgorithmOrder = -1;
byte[] bestDigest = null;
for (ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest : contentDigests) {
final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(contentDigest.getSignatureAlgorithmId());
final ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm();
if (!isSupported(contentDigestAlgorithm, true)) {
continue;
}
final int algorithmOrder = digestAlgorithmSortingOrder(contentDigestAlgorithm);
if (bestAlgorithmOrder < algorithmOrder) {
bestAlgorithmOrder = algorithmOrder;
bestDigest = contentDigest.getValue();
}
}
if (bestDigest == null) {
throw new SignatureException("Failed to find a supported digest in the source APK");
}
return bestDigest;
}
use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class V4SchemeVerifier method parseAndVerifySignatureBlock.
/**
* Parses the provided signature block and populates the {@code result}.
* <p>
* This verifies {@signingInfo} over {@code signedData}, as well as parsing the certificate
* contained in the signature block. This method adds one or more errors to the {@code result}.
*/
private static ApkSigningBlockUtils.Result.SignerInfo parseAndVerifySignatureBlock(V4Signature.SigningInfo signingInfo, final byte[] signedData) throws NoSuchAlgorithmException {
final ApkSigningBlockUtils.Result.SignerInfo result = new ApkSigningBlockUtils.Result.SignerInfo();
result.index = 0;
final int sigAlgorithmId = signingInfo.signatureAlgorithmId;
final byte[] sigBytes = signingInfo.signature;
result.signatures.add(new ApkSigningBlockUtils.Result.SignerInfo.Signature(sigAlgorithmId, sigBytes));
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId);
if (signatureAlgorithm == null) {
result.addError(Issue.V4_SIG_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId);
return result;
}
String jcaSignatureAlgorithm = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst();
AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond();
String keyAlgorithm = signatureAlgorithm.getJcaKeyAlgorithm();
final byte[] publicKeyBytes = signingInfo.publicKey;
PublicKey publicKey;
try {
publicKey = KeyFactory.getInstance(keyAlgorithm).generatePublic(new X509EncodedKeySpec(publicKeyBytes));
} catch (Exception e) {
result.addError(Issue.V4_SIG_MALFORMED_PUBLIC_KEY, e);
return result;
}
try {
Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
sig.initVerify(publicKey);
if (jcaSignatureAlgorithmParams != null) {
sig.setParameter(jcaSignatureAlgorithmParams);
}
sig.update(signedData);
if (!sig.verify(sigBytes)) {
result.addError(Issue.V4_SIG_DID_NOT_VERIFY, signatureAlgorithm);
return result;
}
result.verifiedSignatures.put(signatureAlgorithm, sigBytes);
} catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) {
result.addError(Issue.V4_SIG_VERIFY_EXCEPTION, signatureAlgorithm, e);
return result;
}
if (signingInfo.certificate == null) {
result.addError(Issue.V4_SIG_NO_CERTIFICATE);
return result;
}
final X509Certificate certificate;
try {
// Wrap the cert so that the result's getEncoded returns exactly the original encoded
// form. Without this, getEncoded may return a different form from what was stored in
// the signature. This is because some X509Certificate(Factory) implementations
// re-encode certificates.
certificate = new GuaranteedEncodedFormX509Certificate(X509CertificateUtils.generateCertificate(signingInfo.certificate), signingInfo.certificate);
} catch (CertificateException e) {
result.addError(Issue.V4_SIG_MALFORMED_CERTIFICATE, e);
return result;
}
result.certs.add(certificate);
byte[] certificatePublicKeyBytes;
try {
certificatePublicKeyBytes = ApkSigningBlockUtils.encodePublicKey(certificate.getPublicKey());
} catch (InvalidKeyException e) {
System.out.println("Caught an exception encoding the public key: " + e);
e.printStackTrace();
certificatePublicKeyBytes = certificate.getPublicKey().getEncoded();
}
if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
result.addError(Issue.V4_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD, ApkSigningBlockUtils.toHex(certificatePublicKeyBytes), ApkSigningBlockUtils.toHex(publicKeyBytes));
return result;
}
// Add apk digest from the file to the result.
ApkSigningBlockUtils.Result.SignerInfo.ContentDigest contentDigest = new ApkSigningBlockUtils.Result.SignerInfo.ContentDigest(0, /* signature algorithm id doesn't matter here */
signingInfo.apkDigest);
result.contentDigests.add(contentDigest);
return result;
}
Aggregations