Search in sources :

Example 1 with Pair

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;
}
Also used : PublicKey(java.security.PublicKey) ArrayList(java.util.ArrayList) CertificateEncodingException(java.security.cert.CertificateEncodingException) SignatureException(java.security.SignatureException) InvalidKeyException(java.security.InvalidKeyException) Pair(com.android.apksigner.core.internal.util.Pair)

Example 2 with Pair

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;
}
Also used : ArrayList(java.util.ArrayList) CertificateEncodingException(java.security.cert.CertificateEncodingException) SignatureException(java.security.SignatureException) V1SchemeSigner(com.android.apksigner.core.internal.apk.v1.V1SchemeSigner) HashMap(java.util.HashMap) Map(java.util.Map) Pair(com.android.apksigner.core.internal.util.Pair)

Example 3 with Pair

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 });
}
Also used : InvalidAlgorithmParameterException(java.security.InvalidAlgorithmParameterException) PublicKey(java.security.PublicKey) ArrayList(java.util.ArrayList) CertificateEncodingException(java.security.cert.CertificateEncodingException) SignatureException(java.security.SignatureException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) InvalidKeyException(java.security.InvalidKeyException) Signature(java.security.Signature) AlgorithmParameterSpec(java.security.spec.AlgorithmParameterSpec) Pair(com.android.apksigner.core.internal.util.Pair)

Aggregations

Pair (com.android.apksigner.core.internal.util.Pair)3 SignatureException (java.security.SignatureException)3 CertificateEncodingException (java.security.cert.CertificateEncodingException)3 ArrayList (java.util.ArrayList)3 InvalidKeyException (java.security.InvalidKeyException)2 PublicKey (java.security.PublicKey)2 V1SchemeSigner (com.android.apksigner.core.internal.apk.v1.V1SchemeSigner)1 InvalidAlgorithmParameterException (java.security.InvalidAlgorithmParameterException)1 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)1 Signature (java.security.Signature)1 AlgorithmParameterSpec (java.security.spec.AlgorithmParameterSpec)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1