gt: 				signatureBlock = generateSignatureBlock(signerConfig, sfBytes);
context: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.netas.mgp.apkverifier.internal.apk.v1;
  
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.security.InvalidKeyException;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.security.PrivateKey;
  import java.security.PublicKey;
  import java.security.SignatureException;
  import java.security.cert.CertificateEncodingException;
  import java.security.cert.X509Certificate;
  import java.util.ArrayList;
  import java.util.Base64;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Set;
  import java.util.SortedMap;
  import java.util.TreeMap;
  import java.util.jar.Attributes;
  import java.util.jar.Manifest;
  
  import org.bouncycastle.asn1.ASN1InputStream;
  import org.bouncycastle.asn1.ASN1ObjectIdentifier;
  import org.bouncycastle.asn1.DERNull;
  import org.bouncycastle.asn1.DEROutputStream;
  import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
  import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
  import org.bouncycastle.cert.jcajce.JcaCertStore;
  import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
  import org.bouncycastle.cms.CMSException;
  import org.bouncycastle.cms.CMSProcessableByteArray;
  import org.bouncycastle.cms.CMSSignatureEncryptionAlgorithmFinder;
  import org.bouncycastle.cms.CMSSignedData;
  import org.bouncycastle.cms.CMSSignedDataGenerator;
  import org.bouncycastle.cms.DefaultCMSSignatureEncryptionAlgorithmFinder;
  import org.bouncycastle.cms.SignerInfoGeneratorBuilder;
  import org.bouncycastle.operator.ContentSigner;
  import org.bouncycastle.operator.OperatorCreationException;
  import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
  import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
  
  import com.netas.mgp.apkverifier.internal.jar.ManifestWriter;
  import com.netas.mgp.apkverifier.internal.jar.SignatureFileWriter;
  import com.netas.mgp.apkverifier.internal.util.Pair;
  
  /**
   * APK signer which uses JAR signing (aka v1 signing scheme).
   *
   * @see <a href=
   *      "https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">Signed
   *      JAR File</a>
   */
  public abstract class V1SchemeSigner {
  
  	public static final String MANIFEST_ENTRY_NAME = "META-INF/MANIFEST.MF";
  
  	private static final Attributes.Name ATTRIBUTE_NAME_CREATED_BY = new Attributes.Name("Created-By");
  	private static final String ATTRIBUTE_DEFALT_VALUE_CREATED_BY = "1.0 (Android apksigner)";
  	private static final String ATTRIBUTE_VALUE_MANIFEST_VERSION = "1.0";
  	private static final String ATTRIBUTE_VALUE_SIGNATURE_VERSION = "1.0";
  
  	private static final Attributes.Name SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME = new Attributes.Name("X-Android-APK-Signed");
  
  	/**
  	 * Signer configuration.
  	 */
  	public static class SignerConfig {
  		/** Name. */
  		public String name;
  
  		/** Private key. */
  		public PrivateKey privateKey;
  
  		/**
  		 * Certificates, with the first certificate containing the public key corresponding to
  		 * {@link #privateKey}.
  		 */
  		public List<X509Certificate> certificates;
  
  		/**
  		 * Digest algorithm used for the signature.
  		 */
  		public DigestAlgorithm signatureDigestAlgorithm;
  
  		/**
  		 * Digest algorithm used for digests of JAR entries and MANIFEST.MF.
  		 */
  		public DigestAlgorithm contentDigestAlgorithm;
  	}
  
  	/** Hidden constructor to prevent instantiation. */
  	private V1SchemeSigner() {
  	}
  
  	/**
  	 * Gets the JAR signing digest algorithm to be used for signing an APK using the provided key.
  	 *
  	 * @param minSdkVersion
  	 *            minimum API Level of the platform on which the APK may be installed (see
  	 *            AndroidManifest.xml minSdkVersion attribute)
  	 *
  	 * @throws InvalidKeyException
  	 *             if the provided key is not suitable for signing APKs using JAR signing (aka v1
  	 *             signature scheme)
  	 */
  	public static DigestAlgorithm getSuggestedSignatureDigestAlgorithm(PublicKey signingKey, int minSdkVersion) throws InvalidKeyException {
  		String keyAlgorithm = signingKey.getAlgorithm();
  		if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
  			// Prior to API Level 18, only SHA-1 can be used with RSA.
  			if (minSdkVersion < 18) {
  				return DigestAlgorithm.SHA1;
  			}
  			return DigestAlgorithm.SHA256;
  		} else if ("DSA".equalsIgnoreCase(keyAlgorithm)) {
  			// Prior to API Level 21, only SHA-1 can be used with DSA
  			if (minSdkVersion < 21) {
  				return DigestAlgorithm.SHA1;
  			} else {
  				return DigestAlgorithm.SHA256;
  			}
  		} else if ("EC".equalsIgnoreCase(keyAlgorithm)) {
  			if (minSdkVersion < 18) {
  				throw new InvalidKeyException("ECDSA signatures only supported for minSdkVersion 18 and higher");
  			}
  			// Prior to API Level 21, only SHA-1 can be used with ECDSA
  			if (minSdkVersion < 21) {
  				return DigestAlgorithm.SHA1;
  			} else {
  				return DigestAlgorithm.SHA256;
  			}
  		} else {
  			throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
  		}
  	}
  
  	/**
  	 * Returns the JAR signing digest algorithm to be used for JAR entry digests.
  	 *
  	 * @param minSdkVersion
  	 *            minimum API Level of the platform on which the APK may be installed (see
  	 *            AndroidManifest.xml minSdkVersion attribute)
  	 */
  	public static DigestAlgorithm getSuggestedContentDigestAlgorithm(int minSdkVersion) {
  		return (minSdkVersion >= 18) ? DigestAlgorithm.SHA256 : DigestAlgorithm.SHA1;
  	}
  
  	/**
  	 * Returns a new {@link MessageDigest} instance corresponding to the provided digest algorithm.
  	 */
  	public static MessageDigest getMessageDigestInstance(DigestAlgorithm digestAlgorithm) {
  		String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm();
  		try {
  			return MessageDigest.getInstance(jcaAlgorithm);
  		} catch (NoSuchAlgorithmException e) {
  			throw new RuntimeException("Failed to obtain " + jcaAlgorithm + " MessageDigest", e);
  		}
  	}
  
  	/**
  	 * Returns the JCA {@link MessageDigest} algorithm corresponding to the provided digest
  	 * algorithm.
  	 */
  	public static String getJcaMessageDigestAlgorithm(DigestAlgorithm digestAlgorithm) {
  		return digestAlgorithm.getJcaMessageDigestAlgorithm();
  	}
  
  	/**
  	 * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's
  	 * manifest.
  	 */
  	public static boolean isJarEntryDigestNeededInManifest(String entryName) {
  		// See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File
  
  		// Entries outside of META-INF must be listed in the manifest.
  		if (!entryName.startsWith("META-INF/")) {
  			return true;
  		}
  		// Entries in subdirectories of META-INF must be listed in the manifest.
  		if (entryName.indexOf('/', "META-INF/".length()) != -1) {
  			return true;
  		}
  
  		// Ignored file names (case-insensitive) in META-INF directory:
  		// MANIFEST.MF
  		// *.SF
  		// *.RSA
  		// *.DSA
  		// *.EC
  		// SIG-*
  		String fileNameLowerCase = entryName.substring("META-INF/".length()).toLowerCase(Locale.US);
  		if (("manifest.mf".equals(fileNameLowerCase)) || (fileNameLowerCase.endsWith(".sf")) || (fileNameLowerCase.endsWith(".rsa")) || (fileNameLowerCase.endsWith(".dsa")) || (fileNameLowerCase.endsWith(".ec"))
  				|| (fileNameLowerCase.startsWith("sig-"))) {
  			return false;
  		}
  		return true;
  	}
  
  	/**
  	 * 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[]>> sign(List<SignerConfig> signerConfigs, DigestAlgorithm jarEntryDigestAlgorithm, Map<String, byte[]> jarEntryDigests, List<Integer> apkSigningSchemeIds,
  			byte[] sourceManifestBytes) throws InvalidKeyException, CertificateEncodingException, SignatureException {
  		if (signerConfigs.isEmpty()) {
  			throw new IllegalArgumentException("At least one signer config must be provided");
  		}
  		OutputManifestFile manifest = generateManifestFile(jarEntryDigestAlgorithm, jarEntryDigests, sourceManifestBytes);
  
  		return signManifest(signerConfigs, jarEntryDigestAlgorithm, apkSigningSchemeIds, manifest);
  	}
  
  	/**
  	 * 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 {
  