use of com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate in project apksig by venshine.
the class Certificate method parseCertificates.
public static List<X509Certificate> parseCertificates(List<Asn1OpaqueObject> encodedCertificates) throws CertificateException {
if (encodedCertificates.isEmpty()) {
return Collections.emptyList();
}
List<X509Certificate> result = new ArrayList<>(encodedCertificates.size());
for (int i = 0; i < encodedCertificates.size(); i++) {
Asn1OpaqueObject encodedCertificate = encodedCertificates.get(i);
X509Certificate certificate;
byte[] encodedForm = ByteBufferUtils.toByteArray(encodedCertificate.getEncoded());
try {
certificate = X509CertificateUtils.generateCertificate(encodedForm);
} catch (CertificateException e) {
throw new CertificateException("Failed to parse certificate #" + (i + 1), e);
}
// 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 and/or some implementations of
// X509Certificate.getEncoded() re-encode certificates.
certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedForm);
result.add(certificate);
}
return result;
}
use of com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate in project apksig by venshine.
the class V2SchemeVerifier 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, CertificateFactory certFactory, ApkSigningBlockUtils.Result.SignerInfo result, Set<ContentDigestAlgorithm> contentDigestsToVerify, Map<Integer, String> supportedApkSigSchemeNames, Set<Integer> foundApkSigSchemeIds, int minSdkVersion, int maxSdkVersion) throws ApkFormatException, NoSuchAlgorithmException {
ByteBuffer signedData = ApkSigningBlockUtils.getLengthPrefixedSlice(signerBlock);
byte[] signedDataBytes = new byte[signedData.remaining()];
signedData.get(signedDataBytes);
signedData.flip();
result.signedData = signedDataBytes;
ByteBuffer signatures = ApkSigningBlockUtils.getLengthPrefixedSlice(signerBlock);
byte[] publicKeyBytes = ApkSigningBlockUtils.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 = ApkSigningBlockUtils.getLengthPrefixedSlice(signatures);
int sigAlgorithmId = signature.getInt();
byte[] sigBytes = ApkSigningBlockUtils.readLengthPrefixedByteArray(signature);
result.signatures.add(new ApkSigningBlockUtils.Result.SignerInfo.Signature(sigAlgorithmId, sigBytes));
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId);
if (signatureAlgorithm == null) {
result.addWarning(Issue.V2_SIG_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId);
continue;
}
supportedSignatures.add(new ApkSigningBlockUtils.SupportedSignature(signatureAlgorithm, sigBytes));
} catch (ApkFormatException | BufferUnderflowException e) {
result.addError(Issue.V2_SIG_MALFORMED_SIGNATURE, signatureCount);
return;
}
}
if (result.signatures.isEmpty()) {
result.addError(Issue.V2_SIG_NO_SIGNATURES);
return;
}
// Verify signatures over signed-data block using the public key
List<ApkSigningBlockUtils.SupportedSignature> signaturesToVerify = null;
try {
signaturesToVerify = ApkSigningBlockUtils.getSignaturesToVerify(supportedSignatures, minSdkVersion, maxSdkVersion);
} catch (ApkSigningBlockUtils.NoSupportedSignaturesException e) {
result.addError(Issue.V2_SIG_NO_SUPPORTED_SIGNATURES, e);
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.V2_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.V2_SIG_DID_NOT_VERIFY, signatureAlgorithm);
return;
}
result.verifiedSignatures.put(signatureAlgorithm, sigBytes);
contentDigestsToVerify.add(signatureAlgorithm.getContentDigestAlgorithm());
} catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) {
result.addError(Issue.V2_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 = ApkSigningBlockUtils.getLengthPrefixedSlice(signedData);
ByteBuffer certificates = ApkSigningBlockUtils.getLengthPrefixedSlice(signedData);
ByteBuffer additionalAttributes = ApkSigningBlockUtils.getLengthPrefixedSlice(signedData);
// Parse the certificates block
int certificateIndex = -1;
while (certificates.hasRemaining()) {
certificateIndex++;
byte[] encodedCert = ApkSigningBlockUtils.readLengthPrefixedByteArray(certificates);
X509Certificate certificate;
try {
certificate = X509CertificateUtils.generateCertificate(encodedCert, certFactory);
} catch (CertificateException e) {
result.addError(Issue.V2_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.V2_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.V2_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 = ApkSigningBlockUtils.getLengthPrefixedSlice(digests);
int sigAlgorithmId = digest.getInt();
byte[] digestBytes = ApkSigningBlockUtils.readLengthPrefixedByteArray(digest);
result.contentDigests.add(new ApkSigningBlockUtils.Result.SignerInfo.ContentDigest(sigAlgorithmId, digestBytes));
} catch (ApkFormatException | BufferUnderflowException e) {
result.addError(Issue.V2_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.V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS, sigAlgsFromSignaturesRecord, sigAlgsFromDigestsRecord);
return;
}
// Parse the additional attributes block.
int additionalAttributeCount = 0;
Set<Integer> supportedApkSigSchemeIds = supportedApkSigSchemeNames.keySet();
Set<Integer> supportedExpectedApkSigSchemeIds = new HashSet<>(1);
while (additionalAttributes.hasRemaining()) {
additionalAttributeCount++;
try {
ByteBuffer attribute = ApkSigningBlockUtils.getLengthPrefixedSlice(additionalAttributes);
int id = attribute.getInt();
byte[] value = ByteBufferUtils.toByteArray(attribute);
result.additionalAttributes.add(new ApkSigningBlockUtils.Result.SignerInfo.AdditionalAttribute(id, value));
switch(id) {
case V2SchemeConstants.STRIPPING_PROTECTION_ATTR_ID:
// stripping protection added when signing with a newer scheme
int foundId = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).getInt();
if (supportedApkSigSchemeIds.contains(foundId)) {
supportedExpectedApkSigSchemeIds.add(foundId);
} else {
result.addWarning(Issue.V2_SIG_UNKNOWN_APK_SIG_SCHEME_ID, result.index, foundId);
}
break;
default:
result.addWarning(Issue.V2_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE, id);
}
} catch (ApkFormatException | BufferUnderflowException e) {
result.addError(Issue.V2_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE, additionalAttributeCount);
return;
}
}
// make sure that all known IDs indicated in stripping protection have already verified
for (int id : supportedExpectedApkSigSchemeIds) {
if (!foundApkSigSchemeIds.contains(id)) {
String apkSigSchemeName = supportedApkSigSchemeNames.get(id);
result.addError(Issue.V2_SIG_MISSING_APK_SIG_REFERENCED, result.index, apkSigSchemeName);
}
}
}
use of com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate in project apksig by venshine.
the class ApkSigningBlockUtils method getApkSignatureBlockSigners.
/**
* Returns the individual APK signers within the provided {@code signatureBlock} in a {@code
* List} of {@code Pair} instances where the first element is a {@code List} of {@link
* X509Certificate}s and the second element is a byte array of the individual signer's block.
*
* <p>This method supports any signature block that adheres to the following format up to the
* signing certificate(s):
* <pre>
* * length-prefixed sequence of length-prefixed signers
* * 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).
* </pre>
*
* <p>Note, this is a convenience method to obtain any signers from an existing signature block;
* the signature of each signer will not be verified.
*
* @throws ApkFormatException if an error is encountered while parsing the provided {@code
* signatureBlock}
* @throws CertificateException if the signing certificate(s) within an individual signer block
* cannot be parsed
*/
public static List<Pair<List<X509Certificate>, byte[]>> getApkSignatureBlockSigners(byte[] signatureBlock) throws ApkFormatException, CertificateException {
ByteBuffer signatureBlockBuffer = ByteBuffer.wrap(signatureBlock);
signatureBlockBuffer.order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer signersBuffer = getLengthPrefixedSlice(signatureBlockBuffer);
List<Pair<List<X509Certificate>, byte[]>> signers = new ArrayList<>();
while (signersBuffer.hasRemaining()) {
// Parse the next signer block, save all of its bytes for the resulting List, and
// rewind the buffer to allow the signing certificate(s) to be parsed.
ByteBuffer signer = getLengthPrefixedSlice(signersBuffer);
byte[] signerBytes = new byte[signer.remaining()];
signer.get(signerBytes);
signer.rewind();
ByteBuffer signedData = getLengthPrefixedSlice(signer);
// The first length prefixed slice is the sequence of digests which are not required
// when obtaining the signing certificate(s).
getLengthPrefixedSlice(signedData);
ByteBuffer certificatesBuffer = getLengthPrefixedSlice(signedData);
List<X509Certificate> certificates = new ArrayList<>();
while (certificatesBuffer.hasRemaining()) {
int certLength = certificatesBuffer.getInt();
byte[] certBytes = new byte[certLength];
if (certLength > certificatesBuffer.remaining()) {
throw new IllegalArgumentException("Cert index " + (certificates.size() + 1) + " under signer index " + (signers.size() + 1) + " size out of range: " + certLength);
}
certificatesBuffer.get(certBytes);
GuaranteedEncodedFormX509Certificate signerCert = new GuaranteedEncodedFormX509Certificate(X509CertificateUtils.generateCertificate(certBytes), certBytes);
certificates.add(signerCert);
}
signers.add(Pair.of(certificates, signerBytes));
}
return signers;
}
use of com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate in project apksig by venshine.
the class V3SigningCertificateLineage method readSigningCertificateLineage.
/**
* Deserializes the binary representation of an {@link V3SigningCertificateLineage}. Also
* verifies that the structure is well-formed, e.g. that the signature for each node is from its
* parent.
*/
public static List<SigningCertificateNode> readSigningCertificateLineage(ByteBuffer inputBytes) throws IOException {
List<SigningCertificateNode> result = new ArrayList<>();
int nodeCount = 0;
if (inputBytes == null || !inputBytes.hasRemaining()) {
return null;
}
ApkSigningBlockUtils.checkByteOrderLittleEndian(inputBytes);
// FORMAT (little endian):
// * uint32: version code
// * sequence of length-prefixed (uint32): nodes
// * length-prefixed bytes: signed data
// * length-prefixed bytes: certificate
// * uint32: signature algorithm id
// * uint32: flags
// * uint32: signature algorithm id (used by to sign next cert in lineage)
// * length-prefixed bytes: signature over above signed data
X509Certificate lastCert = null;
int lastSigAlgorithmId = 0;
try {
int version = inputBytes.getInt();
if (version != CURRENT_VERSION) {
// we only have one version to worry about right now, so just check it
throw new IllegalArgumentException("Encoded SigningCertificateLineage has a version" + " different than any of which we are aware");
}
HashSet<X509Certificate> certHistorySet = new HashSet<>();
while (inputBytes.hasRemaining()) {
nodeCount++;
ByteBuffer nodeBytes = getLengthPrefixedSlice(inputBytes);
ByteBuffer signedData = getLengthPrefixedSlice(nodeBytes);
int flags = nodeBytes.getInt();
int sigAlgorithmId = nodeBytes.getInt();
SignatureAlgorithm sigAlgorithm = SignatureAlgorithm.findById(lastSigAlgorithmId);
byte[] signature = readLengthPrefixedByteArray(nodeBytes);
if (lastCert != null) {
// Use previous level cert to verify current level
String jcaSignatureAlgorithm = sigAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst();
AlgorithmParameterSpec jcaSignatureAlgorithmParams = sigAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond();
PublicKey publicKey = lastCert.getPublicKey();
Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
sig.initVerify(publicKey);
if (jcaSignatureAlgorithmParams != null) {
sig.setParameter(jcaSignatureAlgorithmParams);
}
sig.update(signedData);
if (!sig.verify(signature)) {
throw new SecurityException("Unable to verify signature of certificate #" + nodeCount + " using " + jcaSignatureAlgorithm + " when verifying" + " V3SigningCertificateLineage object");
}
}
signedData.rewind();
byte[] encodedCert = readLengthPrefixedByteArray(signedData);
int signedSigAlgorithm = signedData.getInt();
if (lastCert != null && lastSigAlgorithmId != signedSigAlgorithm) {
throw new SecurityException("Signing algorithm ID mismatch for certificate #" + nodeBytes + " when verifying V3SigningCertificateLineage object");
}
lastCert = X509CertificateUtils.generateCertificate(encodedCert);
lastCert = new GuaranteedEncodedFormX509Certificate(lastCert, encodedCert);
if (certHistorySet.contains(lastCert)) {
throw new SecurityException("Encountered duplicate entries in " + "SigningCertificateLineage at certificate #" + nodeCount + ". All " + "signing certificates should be unique");
}
certHistorySet.add(lastCert);
lastSigAlgorithmId = sigAlgorithmId;
result.add(new SigningCertificateNode(lastCert, SignatureAlgorithm.findById(signedSigAlgorithm), SignatureAlgorithm.findById(sigAlgorithmId), signature, flags));
}
} catch (ApkFormatException | BufferUnderflowException e) {
throw new IOException("Failed to parse V3SigningCertificateLineage object", e);
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) {
throw new SecurityException("Failed to verify signature over signed data for certificate #" + nodeCount + " when parsing V3SigningCertificateLineage object", e);
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + nodeCount + " when parsing V3SigningCertificateLineage object", e);
}
return result;
}
Aggregations