use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class V2SchemeSigner method generateSignerBlock.
private static byte[] generateSignerBlock(SignerConfig signerConfig, Map<ContentDigestAlgorithm, byte[]> contentDigests, boolean v3SigningEnabled) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
if (signerConfig.certificates.isEmpty()) {
throw new SignatureException("No certificates configured for signer");
}
PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
byte[] encodedPublicKey = encodePublicKey(publicKey);
V2SignatureSchemeBlock.SignedData signedData = new V2SignatureSchemeBlock.SignedData();
try {
signedData.certificates = encodeCertificates(signerConfig.certificates);
} catch (CertificateEncodingException e) {
throw new SignatureException("Failed to encode certificates", e);
}
List<Pair<Integer, byte[]>> digests = new ArrayList<>(signerConfig.signatureAlgorithms.size());
for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
ContentDigestAlgorithm contentDigestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm();
byte[] contentDigest = contentDigests.get(contentDigestAlgorithm);
if (contentDigest == null) {
throw new RuntimeException(contentDigestAlgorithm + " content digest for " + signatureAlgorithm + " not computed");
}
digests.add(Pair.of(signatureAlgorithm.getId(), contentDigest));
}
signedData.digests = digests;
signedData.additionalAttributes = generateAdditionalAttributes(v3SigningEnabled);
V2SignatureSchemeBlock.Signer signer = new V2SignatureSchemeBlock.Signer();
// FORMAT:
// * 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).
// * length-prefixed sequence of length-prefixed additional attributes:
// * uint32: ID
// * (length - 4) bytes: value
signer.signedData = encodeAsSequenceOfLengthPrefixedElements(new byte[][] { encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(signedData.digests), encodeAsSequenceOfLengthPrefixedElements(signedData.certificates), signedData.additionalAttributes, new byte[0] });
signer.publicKey = encodedPublicKey;
signer.signatures = new ArrayList<>();
signer.signatures = ApkSigningBlockUtils.generateSignaturesOverData(signerConfig, signer.signedData);
// * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded)
return encodeAsSequenceOfLengthPrefixedElements(new byte[][] { signer.signedData, encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(signer.signatures), signer.publicKey });
}
use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class SigningCertificateLineage method spawnDescendant.
/**
* Add a new signing certificate to the lineage. This effectively creates a signing certificate
* rotation event, forcing APKs which include this lineage to be signed by the new signer.
*
* @param parent current signing certificate of the containing APK
* @param child new signing certificate which will sign the APK contents
* @param childCapabilities flags
*/
public SigningCertificateLineage spawnDescendant(SignerConfig parent, SignerConfig child, SignerCapabilities childCapabilities) throws CertificateEncodingException, InvalidKeyException, NoSuchAlgorithmException, SignatureException {
if (parent == null) {
throw new NullPointerException("parent == null");
}
if (child == null) {
throw new NullPointerException("child == null");
}
if (childCapabilities == null) {
throw new NullPointerException("childCapabilities == null");
}
if (mSigningLineage.isEmpty()) {
throw new IllegalArgumentException("Cannot spawn descendant signing certificate on an" + " empty SigningCertificateLineage: no parent node");
}
// make sure that the parent matches our newest generation (leaf node/sink)
SigningCertificateNode currentGeneration = mSigningLineage.get(mSigningLineage.size() - 1);
if (!Arrays.equals(currentGeneration.signingCert.getEncoded(), parent.getCertificate().getEncoded())) {
throw new IllegalArgumentException("SignerConfig Certificate containing private key" + " to sign the new SigningCertificateLineage record does not match the" + " existing most recent record");
}
// create data to be signed, including the algorithm we're going to use
SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm(parent);
ByteBuffer prefixedSignedData = ByteBuffer.wrap(V3SigningCertificateLineage.encodeSignedData(child.getCertificate(), signatureAlgorithm.getId()));
prefixedSignedData.position(4);
ByteBuffer signedDataBuffer = ByteBuffer.allocate(prefixedSignedData.remaining());
signedDataBuffer.put(prefixedSignedData);
byte[] signedData = signedDataBuffer.array();
// create SignerConfig to do the signing
List<X509Certificate> certificates = new ArrayList<>(1);
certificates.add(parent.getCertificate());
ApkSigningBlockUtils.SignerConfig newSignerConfig = new ApkSigningBlockUtils.SignerConfig();
newSignerConfig.privateKey = parent.getPrivateKey();
newSignerConfig.certificates = certificates;
newSignerConfig.signatureAlgorithms = Collections.singletonList(signatureAlgorithm);
// sign it
List<Pair<Integer, byte[]>> signatures = ApkSigningBlockUtils.generateSignaturesOverData(newSignerConfig, signedData);
// finally, add it to our lineage
SignatureAlgorithm sigAlgorithm = SignatureAlgorithm.findById(signatures.get(0).getFirst());
byte[] signature = signatures.get(0).getSecond();
currentGeneration.sigAlgorithm = sigAlgorithm;
SigningCertificateNode childNode = new SigningCertificateNode(child.getCertificate(), sigAlgorithm, null, signature, childCapabilities.getFlags());
List<SigningCertificateNode> lineageCopy = new ArrayList<>(mSigningLineage);
lineageCopy.add(childNode);
return new SigningCertificateLineage(mMinSdkVersion, lineageCopy);
}
use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class SigningCertificateLineage method getSignatureAlgorithm.
private SignatureAlgorithm getSignatureAlgorithm(SignerConfig parent) throws InvalidKeyException {
PublicKey publicKey = parent.getCertificate().getPublicKey();
// TODO switch to one signature algorithm selection, or add support for multiple algorithms
List<SignatureAlgorithm> algorithms = V3SchemeSigner.getSuggestedSignatureAlgorithms(publicKey, mMinSdkVersion, false, /* verityEnabled */
false);
return algorithms.get(0);
}
use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class SourceStampCertificateLineage method readSigningCertificateLineage.
/**
* Deserializes the binary representation of a SourceStampCertificateLineage. 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;
}
ApkSigningBlockUtilsLite.checkByteOrderLittleEndian(inputBytes);
CertificateFactory certFactory;
try {
certFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
throw new IllegalStateException("Failed to obtain X.509 CertificateFactory", e);
}
// 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" + " SourceStampCertificateLineage 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 SourceStampCertificateLineage object");
}
lastCert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(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 SourceStampCertificateLineage object", e);
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) {
throw new SecurityException("Failed to verify signature over signed data for certificate #" + nodeCount + " when parsing SourceStampCertificateLineage object", e);
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + nodeCount + " when parsing SourceStampCertificateLineage object", e);
}
return result;
}
use of com.android.apksig.internal.apk.SignatureAlgorithm in project apksig by venshine.
the class SourceStampVerifier method verifySourceStampSignature.
private static void verifySourceStampSignature(byte[] data, int minSdkVersion, int maxSdkVersion, X509Certificate sourceStampCertificate, ByteBuffer signatures, ApkSignerInfo result) {
// Parse the signatures block and identify supported signatures
int signatureCount = 0;
List<ApkSupportedSignature> supportedSignatures = new ArrayList<>(1);
while (signatures.hasRemaining()) {
signatureCount++;
try {
ByteBuffer signature = getLengthPrefixedSlice(signatures);
int sigAlgorithmId = signature.getInt();
byte[] sigBytes = readLengthPrefixedByteArray(signature);
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId);
if (signatureAlgorithm == null) {
result.addWarning(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId);
continue;
}
supportedSignatures.add(new ApkSupportedSignature(signatureAlgorithm, sigBytes));
} catch (ApkFormatException | BufferUnderflowException e) {
result.addWarning(ApkVerificationIssue.SOURCE_STAMP_MALFORMED_SIGNATURE, signatureCount);
return;
}
}
if (supportedSignatures.isEmpty()) {
result.addWarning(ApkVerificationIssue.SOURCE_STAMP_NO_SIGNATURE);
return;
}
// Verify signatures over digests using the SourceStamp's certificate.
List<ApkSupportedSignature> signaturesToVerify;
try {
signaturesToVerify = getSignaturesToVerify(supportedSignatures, minSdkVersion, maxSdkVersion, true);
} catch (NoApkSupportedSignaturesException e) {
// To facilitate debugging capture the signature algorithms and resulting exception in
// the warning.
StringBuilder signatureAlgorithms = new StringBuilder();
for (ApkSupportedSignature supportedSignature : supportedSignatures) {
if (signatureAlgorithms.length() > 0) {
signatureAlgorithms.append(", ");
}
signatureAlgorithms.append(supportedSignature.algorithm);
}
result.addWarning(ApkVerificationIssue.SOURCE_STAMP_NO_SUPPORTED_SIGNATURE, signatureAlgorithms.toString(), e);
return;
}
for (ApkSupportedSignature signature : signaturesToVerify) {
SignatureAlgorithm signatureAlgorithm = signature.algorithm;
String jcaSignatureAlgorithm = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst();
AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond();
PublicKey publicKey = sourceStampCertificate.getPublicKey();
try {
Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
sig.initVerify(publicKey);
if (jcaSignatureAlgorithmParams != null) {
sig.setParameter(jcaSignatureAlgorithmParams);
}
sig.update(data);
byte[] sigBytes = signature.signature;
if (!sig.verify(sigBytes)) {
result.addWarning(ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY, signatureAlgorithm);
return;
}
} catch (InvalidKeyException | InvalidAlgorithmParameterException | SignatureException | NoSuchAlgorithmException e) {
result.addWarning(ApkVerificationIssue.SOURCE_STAMP_VERIFY_EXCEPTION, signatureAlgorithm, e);
return;
}
}
}
Aggregations