use of com.android.apksigner.core.internal.util.Pair in project walle by Meituan-Dianping.
the class V1SchemeSigner method signManifest.
/**
* Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of
* JAR entries which need to be added to the APK as part of the signature.
*
* @param signerConfigs signer configurations, one for each signer. At least one signer config
* must be provided.
*
* @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
* signatures
*/
public static List<Pair<String, byte[]>> signManifest(List<SignerConfig> signerConfigs, DigestAlgorithm digestAlgorithm, List<Integer> apkSigningSchemeIds, OutputManifestFile manifest) throws InvalidKeyException, CertificateEncodingException, SignatureException {
if (signerConfigs.isEmpty()) {
throw new IllegalArgumentException("At least one signer config must be provided");
}
// For each signer output .SF and .(RSA|DSA|EC) file, then output MANIFEST.MF.
List<Pair<String, byte[]>> signatureJarEntries = new ArrayList<>(2 * signerConfigs.size() + 1);
byte[] sfBytes = generateSignatureFile(apkSigningSchemeIds, digestAlgorithm, manifest);
for (SignerConfig signerConfig : signerConfigs) {
String signerName = signerConfig.name;
byte[] signatureBlock;
try {
signatureBlock = generateSignatureBlock(signerConfig, sfBytes);
} catch (InvalidKeyException e) {
throw new InvalidKeyException("Failed to sign using signer \"" + signerName + "\"", e);
} catch (CertificateEncodingException e) {
throw new CertificateEncodingException("Failed to sign using signer \"" + signerName + "\"", e);
} catch (SignatureException e) {
throw new SignatureException("Failed to sign using signer \"" + signerName + "\"", e);
}
signatureJarEntries.add(Pair.of("META-INF/" + signerName + ".SF", sfBytes));
PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
String signatureBlockFileName = "META-INF/" + signerName + "." + publicKey.getAlgorithm().toUpperCase(Locale.US);
signatureJarEntries.add(Pair.of(signatureBlockFileName, signatureBlock));
}
signatureJarEntries.add(Pair.of(MANIFEST_ENTRY_NAME, manifest.contents));
return signatureJarEntries;
}
use of com.android.apksigner.core.internal.util.Pair in project walle by Meituan-Dianping.
the class DefaultApkSignerEngine method outputJarEntries.
@Override
public OutputJarSignatureRequest outputJarEntries() throws InvalidKeyException, SignatureException {
checkNotClosed();
if (!mV1SignaturePending) {
return null;
}
if ((mInputJarManifestEntryDataRequest != null) && (!mInputJarManifestEntryDataRequest.isDone())) {
throw new IllegalStateException("Still waiting to inspect input APK's " + mInputJarManifestEntryDataRequest.getEntryName());
}
for (GetJarEntryDataDigestRequest digestRequest : mOutputJarEntryDigestRequests.values()) {
String entryName = digestRequest.getEntryName();
if (!digestRequest.isDone()) {
throw new IllegalStateException("Still waiting to inspect output APK's " + entryName);
}
mOutputJarEntryDigests.put(entryName, digestRequest.getDigest());
}
mOutputJarEntryDigestRequests.clear();
for (GetJarEntryDataRequest dataRequest : mOutputSignatureJarEntryDataRequests.values()) {
if (!dataRequest.isDone()) {
throw new IllegalStateException("Still waiting to inspect output APK's " + dataRequest.getEntryName());
}
}
List<Integer> apkSigningSchemeIds = (mV2SigningEnabled) ? Collections.singletonList(2) : Collections.emptyList();
byte[] inputJarManifest = (mInputJarManifestEntryDataRequest != null) ? mInputJarManifestEntryDataRequest.getData() : null;
// Check whether the most recently used signature (if present) is still fine.
List<Pair<String, byte[]>> signatureZipEntries;
if ((mAddV1SignatureRequest == null) || (!mAddV1SignatureRequest.isDone())) {
try {
signatureZipEntries = V1SchemeSigner.sign(mV1SignerConfigs, mV1ContentDigestAlgorithm, mOutputJarEntryDigests, apkSigningSchemeIds, inputJarManifest);
} catch (CertificateEncodingException e) {
throw new SignatureException("Failed to generate v1 signature", e);
}
} else {
V1SchemeSigner.OutputManifestFile newManifest = V1SchemeSigner.generateManifestFile(mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest);
byte[] emittedSignatureManifest = mEmittedSignatureJarEntryData.get(V1SchemeSigner.MANIFEST_ENTRY_NAME);
if (!Arrays.equals(newManifest.contents, emittedSignatureManifest)) {
// Emitted v1 signature is no longer valid.
try {
signatureZipEntries = V1SchemeSigner.signManifest(mV1SignerConfigs, mV1ContentDigestAlgorithm, apkSigningSchemeIds, newManifest);
} catch (CertificateEncodingException e) {
throw new SignatureException("Failed to generate v1 signature", e);
}
} else {
// Emitted v1 signature is still valid. Check whether the signature is there in the
// output.
signatureZipEntries = new ArrayList<>();
for (Map.Entry<String, byte[]> expectedOutputEntry : mEmittedSignatureJarEntryData.entrySet()) {
String entryName = expectedOutputEntry.getKey();
byte[] expectedData = expectedOutputEntry.getValue();
GetJarEntryDataRequest actualDataRequest = mOutputSignatureJarEntryDataRequests.get(entryName);
if (actualDataRequest == null) {
// This signature entry hasn't been output.
signatureZipEntries.add(Pair.of(entryName, expectedData));
continue;
}
byte[] actualData = actualDataRequest.getData();
if (!Arrays.equals(expectedData, actualData)) {
signatureZipEntries.add(Pair.of(entryName, expectedData));
}
}
if (signatureZipEntries.isEmpty()) {
// v1 signature in the output is valid
return null;
}
// v1 signature in the output is not valid.
}
}
if (signatureZipEntries.isEmpty()) {
// v1 signature in the output is valid
mV1SignaturePending = false;
return null;
}
List<OutputJarSignatureRequest.JarEntry> sigEntries = new ArrayList<>(signatureZipEntries.size());
for (Pair<String, byte[]> entry : signatureZipEntries) {
String entryName = entry.getFirst();
byte[] entryData = entry.getSecond();
sigEntries.add(new OutputJarSignatureRequest.JarEntry(entryName, entryData));
mEmittedSignatureJarEntryData.put(entryName, entryData);
}
mAddV1SignatureRequest = new OutputJarSignatureRequestImpl(sigEntries);
return mAddV1SignatureRequest;
}
use of com.android.apksigner.core.internal.util.Pair in project walle by Meituan-Dianping.
the class V2SchemeSigner method generateSignerBlock.
private static byte[] generateSignerBlock(SignerConfig signerConfig, Map<ContentDigestAlgorithm, byte[]> contentDigests) throws 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;
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), // additional attributes
new byte[0] });
signer.publicKey = encodedPublicKey;
signer.signatures = new ArrayList<>(signerConfig.signatureAlgorithms.size());
for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
Pair<String, ? extends AlgorithmParameterSpec> sigAlgAndParams = signatureAlgorithm.getJcaSignatureAlgorithmAndParams();
String jcaSignatureAlgorithm = sigAlgAndParams.getFirst();
AlgorithmParameterSpec jcaSignatureAlgorithmParams = sigAlgAndParams.getSecond();
byte[] signatureBytes;
try {
Signature signature = Signature.getInstance(jcaSignatureAlgorithm);
signature.initSign(signerConfig.privateKey);
if (jcaSignatureAlgorithmParams != null) {
signature.setParameter(jcaSignatureAlgorithmParams);
}
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);
}
try {
Signature signature = Signature.getInstance(jcaSignatureAlgorithm);
signature.initVerify(publicKey);
if (jcaSignatureAlgorithmParams != null) {
signature.setParameter(jcaSignatureAlgorithmParams);
}
signature.update(signer.signedData);
if (!signature.verify(signatureBytes)) {
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);
}
signer.signatures.add(Pair.of(signatureAlgorithm.getId(), signatureBytes));
}
// * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded)
return encodeAsSequenceOfLengthPrefixedElements(new byte[][] { signer.signedData, encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(signer.signatures), signer.publicKey });
}
Aggregations