1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.apksig.internal.apk.v1; 18 19 import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoDigestAlgorithmOid; 20 import static com.android.apksig.internal.pkcs7.AlgorithmIdentifier.getSignerInfoSignatureAlgorithm; 21 22 import com.android.apksig.apk.ApkFormatException; 23 import com.android.apksig.internal.apk.ApkSigningBlockUtils; 24 import com.android.apksig.internal.asn1.Asn1EncodingException; 25 import com.android.apksig.internal.jar.ManifestWriter; 26 import com.android.apksig.internal.jar.SignatureFileWriter; 27 import com.android.apksig.internal.pkcs7.AlgorithmIdentifier; 28 import com.android.apksig.internal.util.Pair; 29 30 import java.io.ByteArrayInputStream; 31 import java.io.ByteArrayOutputStream; 32 import java.io.IOException; 33 import java.security.InvalidKeyException; 34 import java.security.MessageDigest; 35 import java.security.NoSuchAlgorithmException; 36 import java.security.PrivateKey; 37 import java.security.PublicKey; 38 import java.security.Signature; 39 import java.security.SignatureException; 40 import java.security.cert.CertificateEncodingException; 41 import java.security.cert.CertificateException; 42 import java.security.cert.X509Certificate; 43 import java.util.ArrayList; 44 import java.util.Base64; 45 import java.util.Collections; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.Locale; 49 import java.util.Map; 50 import java.util.Set; 51 import java.util.SortedMap; 52 import java.util.TreeMap; 53 import java.util.jar.Attributes; 54 import java.util.jar.Manifest; 55 56 /** 57 * APK signer which uses JAR signing (aka v1 signing scheme). 58 * 59 * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">Signed JAR File</a> 60 */ 61 public abstract class V1SchemeSigner { 62 public static final String MANIFEST_ENTRY_NAME = V1SchemeConstants.MANIFEST_ENTRY_NAME; 63 64 private static final Attributes.Name ATTRIBUTE_NAME_CREATED_BY = 65 new Attributes.Name("Created-By"); 66 private static final String ATTRIBUTE_VALUE_MANIFEST_VERSION = "1.0"; 67 private static final String ATTRIBUTE_VALUE_SIGNATURE_VERSION = "1.0"; 68 69 private static final Attributes.Name SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME = 70 new Attributes.Name(V1SchemeConstants.SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR); 71 72 /** 73 * Signer configuration. 74 */ 75 public static class SignerConfig { 76 /** Name. */ 77 public String name; 78 79 /** Private key. */ 80 public PrivateKey privateKey; 81 82 /** 83 * Certificates, with the first certificate containing the public key corresponding to 84 * {@link #privateKey}. 85 */ 86 public List<X509Certificate> certificates; 87 88 /** 89 * Digest algorithm used for the signature. 90 */ 91 public DigestAlgorithm signatureDigestAlgorithm; 92 93 /** 94 * If DSA is the signing algorithm, whether or not deterministic DSA signing should be used. 95 */ 96 public boolean deterministicDsaSigning; 97 } 98 99 /** Hidden constructor to prevent instantiation. */ V1SchemeSigner()100 private V1SchemeSigner() {} 101 102 /** 103 * Gets the JAR signing digest algorithm to be used for signing an APK using the provided key. 104 * 105 * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see 106 * AndroidManifest.xml minSdkVersion attribute) 107 * 108 * @throws InvalidKeyException if the provided key is not suitable for signing APKs using 109 * JAR signing (aka v1 signature scheme) 110 */ getSuggestedSignatureDigestAlgorithm( PublicKey signingKey, int minSdkVersion)111 public static DigestAlgorithm getSuggestedSignatureDigestAlgorithm( 112 PublicKey signingKey, int minSdkVersion) throws InvalidKeyException { 113 String keyAlgorithm = signingKey.getAlgorithm(); 114 if ("RSA".equalsIgnoreCase(keyAlgorithm)) { 115 // Prior to API Level 18, only SHA-1 can be used with RSA. 116 if (minSdkVersion < 18) { 117 return DigestAlgorithm.SHA1; 118 } 119 return DigestAlgorithm.SHA256; 120 } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) { 121 // Prior to API Level 21, only SHA-1 can be used with DSA 122 if (minSdkVersion < 21) { 123 return DigestAlgorithm.SHA1; 124 } else { 125 return DigestAlgorithm.SHA256; 126 } 127 } else if ("EC".equalsIgnoreCase(keyAlgorithm)) { 128 if (minSdkVersion < 18) { 129 throw new InvalidKeyException( 130 "ECDSA signatures only supported for minSdkVersion 18 and higher"); 131 } 132 return DigestAlgorithm.SHA256; 133 } else { 134 throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm); 135 } 136 } 137 138 /** 139 * Returns a safe version of the provided signer name. 140 */ getSafeSignerName(String name)141 public static String getSafeSignerName(String name) { 142 if (name.isEmpty()) { 143 throw new IllegalArgumentException("Empty name"); 144 } 145 146 // According to https://docs.oracle.com/javase/tutorial/deployment/jar/signing.html, the 147 // name must not be longer than 8 characters and may contain only A-Z, 0-9, _, and -. 148 StringBuilder result = new StringBuilder(); 149 char[] nameCharsUpperCase = name.toUpperCase(Locale.US).toCharArray(); 150 for (int i = 0; i < Math.min(nameCharsUpperCase.length, 8); i++) { 151 char c = nameCharsUpperCase[i]; 152 if (((c >= 'A') && (c <= 'Z')) 153 || ((c >= '0') && (c <= '9')) 154 || (c == '-') 155 || (c == '_')) { 156 result.append(c); 157 } else { 158 result.append('_'); 159 } 160 } 161 return result.toString(); 162 } 163 164 /** 165 * Returns a new {@link MessageDigest} instance corresponding to the provided digest algorithm. 166 */ getMessageDigestInstance(DigestAlgorithm digestAlgorithm)167 private static MessageDigest getMessageDigestInstance(DigestAlgorithm digestAlgorithm) 168 throws NoSuchAlgorithmException { 169 String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm(); 170 return MessageDigest.getInstance(jcaAlgorithm); 171 } 172 173 /** 174 * Returns the JCA {@link MessageDigest} algorithm corresponding to the provided digest 175 * algorithm. 176 */ getJcaMessageDigestAlgorithm(DigestAlgorithm digestAlgorithm)177 public static String getJcaMessageDigestAlgorithm(DigestAlgorithm digestAlgorithm) { 178 return digestAlgorithm.getJcaMessageDigestAlgorithm(); 179 } 180 181 /** 182 * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's 183 * manifest. 184 */ isJarEntryDigestNeededInManifest(String entryName)185 public static boolean isJarEntryDigestNeededInManifest(String entryName) { 186 // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File 187 188 // Entries which represent directories sould not be listed in the manifest. 189 if (entryName.endsWith("/")) { 190 return false; 191 } 192 193 // Entries outside of META-INF must be listed in the manifest. 194 if (!entryName.startsWith("META-INF/")) { 195 return true; 196 } 197 // Entries in subdirectories of META-INF must be listed in the manifest. 198 if (entryName.indexOf('/', "META-INF/".length()) != -1) { 199 return true; 200 } 201 202 // Ignored file names (case-insensitive) in META-INF directory: 203 // MANIFEST.MF 204 // *.SF 205 // *.RSA 206 // *.DSA 207 // *.EC 208 // SIG-* 209 String fileNameLowerCase = 210 entryName.substring("META-INF/".length()).toLowerCase(Locale.US); 211 if (("manifest.mf".equals(fileNameLowerCase)) 212 || (fileNameLowerCase.endsWith(".sf")) 213 || (fileNameLowerCase.endsWith(".rsa")) 214 || (fileNameLowerCase.endsWith(".dsa")) 215 || (fileNameLowerCase.endsWith(".ec")) 216 || (fileNameLowerCase.startsWith("sig-"))) { 217 return false; 218 } 219 return true; 220 } 221 222 /** 223 * Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of 224 * JAR entries which need to be added to the APK as part of the signature. 225 * 226 * @param signerConfigs signer configurations, one for each signer. At least one signer config 227 * must be provided. 228 * 229 * @throws ApkFormatException if the source manifest is malformed 230 * @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is 231 * missing 232 * @throws InvalidKeyException if a signing key is not suitable for this signature scheme or 233 * cannot be used in general 234 * @throws SignatureException if an error occurs when computing digests of generating 235 * signatures 236 */ sign( List<SignerConfig> signerConfigs, DigestAlgorithm jarEntryDigestAlgorithm, Map<String, byte[]> jarEntryDigests, List<Integer> apkSigningSchemeIds, byte[] sourceManifestBytes, String createdBy)237 public static List<Pair<String, byte[]>> sign( 238 List<SignerConfig> signerConfigs, 239 DigestAlgorithm jarEntryDigestAlgorithm, 240 Map<String, byte[]> jarEntryDigests, 241 List<Integer> apkSigningSchemeIds, 242 byte[] sourceManifestBytes, 243 String createdBy) 244 throws NoSuchAlgorithmException, ApkFormatException, InvalidKeyException, 245 CertificateException, SignatureException { 246 if (signerConfigs.isEmpty()) { 247 throw new IllegalArgumentException("At least one signer config must be provided"); 248 } 249 OutputManifestFile manifest = 250 generateManifestFile( 251 jarEntryDigestAlgorithm, jarEntryDigests, sourceManifestBytes); 252 253 return signManifest( 254 signerConfigs, jarEntryDigestAlgorithm, apkSigningSchemeIds, createdBy, manifest); 255 } 256 257 /** 258 * Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of 259 * JAR entries which need to be added to the APK as part of the signature. 260 * 261 * @param signerConfigs signer configurations, one for each signer. At least one signer config 262 * must be provided. 263 * 264 * @throws InvalidKeyException if a signing key is not suitable for this signature scheme or 265 * cannot be used in general 266 * @throws SignatureException if an error occurs when computing digests of generating 267 * signatures 268 */ signManifest( List<SignerConfig> signerConfigs, DigestAlgorithm digestAlgorithm, List<Integer> apkSigningSchemeIds, String createdBy, OutputManifestFile manifest)269 public static List<Pair<String, byte[]>> signManifest( 270 List<SignerConfig> signerConfigs, 271 DigestAlgorithm digestAlgorithm, 272 List<Integer> apkSigningSchemeIds, 273 String createdBy, 274 OutputManifestFile manifest) 275 throws NoSuchAlgorithmException, InvalidKeyException, CertificateException, 276 SignatureException { 277 if (signerConfigs.isEmpty()) { 278 throw new IllegalArgumentException("At least one signer config must be provided"); 279 } 280 281 // For each signer output .SF and .(RSA|DSA|EC) file, then output MANIFEST.MF. 282 List<Pair<String, byte[]>> signatureJarEntries = 283 new ArrayList<>(2 * signerConfigs.size() + 1); 284 byte[] sfBytes = 285 generateSignatureFile(apkSigningSchemeIds, digestAlgorithm, createdBy, manifest); 286 for (SignerConfig signerConfig : signerConfigs) { 287 String signerName = signerConfig.name; 288 byte[] signatureBlock; 289 try { 290 signatureBlock = generateSignatureBlock(signerConfig, sfBytes); 291 } catch (InvalidKeyException e) { 292 throw new InvalidKeyException( 293 "Failed to sign using signer \"" + signerName + "\"", e); 294 } catch (CertificateException e) { 295 throw new CertificateException( 296 "Failed to sign using signer \"" + signerName + "\"", e); 297 } catch (SignatureException e) { 298 throw new SignatureException( 299 "Failed to sign using signer \"" + signerName + "\"", e); 300 } 301 signatureJarEntries.add(Pair.of("META-INF/" + signerName + ".SF", sfBytes)); 302 PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); 303 String signatureBlockFileName = 304 "META-INF/" + signerName + "." 305 + publicKey.getAlgorithm().toUpperCase(Locale.US); 306 signatureJarEntries.add( 307 Pair.of(signatureBlockFileName, signatureBlock)); 308 } 309 signatureJarEntries.add(Pair.of(V1SchemeConstants.MANIFEST_ENTRY_NAME, manifest.contents)); 310 return signatureJarEntries; 311 } 312 313 /** 314 * Returns the names of JAR entries which this signer will produce as part of v1 signature. 315 */ getOutputEntryNames(List<SignerConfig> signerConfigs)316 public static Set<String> getOutputEntryNames(List<SignerConfig> signerConfigs) { 317 Set<String> result = new HashSet<>(2 * signerConfigs.size() + 1); 318 for (SignerConfig signerConfig : signerConfigs) { 319 String signerName = signerConfig.name; 320 result.add("META-INF/" + signerName + ".SF"); 321 PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey(); 322 String signatureBlockFileName = 323 "META-INF/" + signerName + "." 324 + publicKey.getAlgorithm().toUpperCase(Locale.US); 325 result.add(signatureBlockFileName); 326 } 327 result.add(V1SchemeConstants.MANIFEST_ENTRY_NAME); 328 return result; 329 } 330 331 /** 332 * Generated and returns the {@code META-INF/MANIFEST.MF} file based on the provided (optional) 333 * input {@code MANIFEST.MF} and digests of JAR entries covered by the manifest. 334 */ generateManifestFile( DigestAlgorithm jarEntryDigestAlgorithm, Map<String, byte[]> jarEntryDigests, byte[] sourceManifestBytes)335 public static OutputManifestFile generateManifestFile( 336 DigestAlgorithm jarEntryDigestAlgorithm, 337 Map<String, byte[]> jarEntryDigests, 338 byte[] sourceManifestBytes) throws ApkFormatException { 339 Manifest sourceManifest = null; 340 if (sourceManifestBytes != null) { 341 try { 342 sourceManifest = new Manifest(new ByteArrayInputStream(sourceManifestBytes)); 343 } catch (IOException e) { 344 throw new ApkFormatException("Malformed source META-INF/MANIFEST.MF", e); 345 } 346 } 347 ByteArrayOutputStream manifestOut = new ByteArrayOutputStream(); 348 Attributes mainAttrs = new Attributes(); 349 // Copy the main section from the source manifest (if provided). Otherwise use defaults. 350 // NOTE: We don't output our own Created-By header because this signer did not create the 351 // JAR/APK being signed -- the signer only adds signatures to the already existing 352 // JAR/APK. 353 if (sourceManifest != null) { 354 mainAttrs.putAll(sourceManifest.getMainAttributes()); 355 } else { 356 mainAttrs.put(Attributes.Name.MANIFEST_VERSION, ATTRIBUTE_VALUE_MANIFEST_VERSION); 357 } 358 359 try { 360 ManifestWriter.writeMainSection(manifestOut, mainAttrs); 361 } catch (IOException e) { 362 throw new RuntimeException("Failed to write in-memory MANIFEST.MF", e); 363 } 364 365 List<String> sortedEntryNames = new ArrayList<>(jarEntryDigests.keySet()); 366 Collections.sort(sortedEntryNames); 367 SortedMap<String, byte[]> invidualSectionsContents = new TreeMap<>(); 368 String entryDigestAttributeName = getEntryDigestAttributeName(jarEntryDigestAlgorithm); 369 for (String entryName : sortedEntryNames) { 370 checkEntryNameValid(entryName); 371 byte[] entryDigest = jarEntryDigests.get(entryName); 372 Attributes entryAttrs = new Attributes(); 373 entryAttrs.putValue( 374 entryDigestAttributeName, 375 Base64.getEncoder().encodeToString(entryDigest)); 376 ByteArrayOutputStream sectionOut = new ByteArrayOutputStream(); 377 byte[] sectionBytes; 378 try { 379 ManifestWriter.writeIndividualSection(sectionOut, entryName, entryAttrs); 380 sectionBytes = sectionOut.toByteArray(); 381 manifestOut.write(sectionBytes); 382 } catch (IOException e) { 383 throw new RuntimeException("Failed to write in-memory MANIFEST.MF", e); 384 } 385 invidualSectionsContents.put(entryName, sectionBytes); 386 } 387 388 OutputManifestFile result = new OutputManifestFile(); 389 result.contents = manifestOut.toByteArray(); 390 result.mainSectionAttributes = mainAttrs; 391 result.individualSectionsContents = invidualSectionsContents; 392 return result; 393 } 394 checkEntryNameValid(String name)395 private static void checkEntryNameValid(String name) throws ApkFormatException { 396 // JAR signing spec says CR, LF, and NUL are not permitted in entry names 397 // CR or LF in entry names will result in malformed MANIFEST.MF and .SF files because there 398 // is no way to escape characters in MANIFEST.MF and .SF files. NUL can, presumably, cause 399 // issues when parsing using C and C++ like languages. 400 for (char c : name.toCharArray()) { 401 if ((c == '\r') || (c == '\n') || (c == 0)) { 402 throw new ApkFormatException( 403 String.format( 404 "Unsupported character 0x%1$02x in ZIP entry name \"%2$s\"", 405 (int) c, 406 name)); 407 } 408 } 409 } 410 411 public static class OutputManifestFile { 412 public byte[] contents; 413 public SortedMap<String, byte[]> individualSectionsContents; 414 public Attributes mainSectionAttributes; 415 } 416 generateSignatureFile( List<Integer> apkSignatureSchemeIds, DigestAlgorithm manifestDigestAlgorithm, String createdBy, OutputManifestFile manifest)417 private static byte[] generateSignatureFile( 418 List<Integer> apkSignatureSchemeIds, 419 DigestAlgorithm manifestDigestAlgorithm, 420 String createdBy, 421 OutputManifestFile manifest) throws NoSuchAlgorithmException { 422 Manifest sf = new Manifest(); 423 Attributes mainAttrs = sf.getMainAttributes(); 424 mainAttrs.put(Attributes.Name.SIGNATURE_VERSION, ATTRIBUTE_VALUE_SIGNATURE_VERSION); 425 mainAttrs.put(ATTRIBUTE_NAME_CREATED_BY, createdBy); 426 if (!apkSignatureSchemeIds.isEmpty()) { 427 // Add APK Signature Scheme v2 (and newer) signature stripping protection. 428 // This attribute indicates that this APK is supposed to have been signed using one or 429 // more APK-specific signature schemes in addition to the standard JAR signature scheme 430 // used by this code. APK signature verifier should reject the APK if it does not 431 // contain a signature for the signature scheme the verifier prefers out of this set. 432 StringBuilder attrValue = new StringBuilder(); 433 for (int id : apkSignatureSchemeIds) { 434 if (attrValue.length() > 0) { 435 attrValue.append(", "); 436 } 437 attrValue.append(String.valueOf(id)); 438 } 439 mainAttrs.put( 440 SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME, 441 attrValue.toString()); 442 } 443 444 // Add main attribute containing the digest of MANIFEST.MF. 445 MessageDigest md = getMessageDigestInstance(manifestDigestAlgorithm); 446 mainAttrs.putValue( 447 getManifestDigestAttributeName(manifestDigestAlgorithm), 448 Base64.getEncoder().encodeToString(md.digest(manifest.contents))); 449 ByteArrayOutputStream out = new ByteArrayOutputStream(); 450 try { 451 SignatureFileWriter.writeMainSection(out, mainAttrs); 452 } catch (IOException e) { 453 throw new RuntimeException("Failed to write in-memory .SF file", e); 454 } 455 String entryDigestAttributeName = getEntryDigestAttributeName(manifestDigestAlgorithm); 456 for (Map.Entry<String, byte[]> manifestSection 457 : manifest.individualSectionsContents.entrySet()) { 458 String sectionName = manifestSection.getKey(); 459 byte[] sectionContents = manifestSection.getValue(); 460 byte[] sectionDigest = md.digest(sectionContents); 461 Attributes attrs = new Attributes(); 462 attrs.putValue( 463 entryDigestAttributeName, 464 Base64.getEncoder().encodeToString(sectionDigest)); 465 466 try { 467 SignatureFileWriter.writeIndividualSection(out, sectionName, attrs); 468 } catch (IOException e) { 469 throw new RuntimeException("Failed to write in-memory .SF file", e); 470 } 471 } 472 473 // A bug in the java.util.jar implementation of Android platforms up to version 1.6 will 474 // cause a spurious IOException to be thrown if the length of the signature file is a 475 // multiple of 1024 bytes. As a workaround, add an extra CRLF in this case. 476 if ((out.size() > 0) && ((out.size() % 1024) == 0)) { 477 try { 478 SignatureFileWriter.writeSectionDelimiter(out); 479 } catch (IOException e) { 480 throw new RuntimeException("Failed to write to ByteArrayOutputStream", e); 481 } 482 } 483 484 return out.toByteArray(); 485 } 486 487 488 489 /** 490 * Generates the CMS PKCS #7 signature block corresponding to the provided signature file and 491 * signing configuration. 492 */ generateSignatureBlock( SignerConfig signerConfig, byte[] signatureFileBytes)493 private static byte[] generateSignatureBlock( 494 SignerConfig signerConfig, byte[] signatureFileBytes) 495 throws NoSuchAlgorithmException, InvalidKeyException, CertificateException, 496 SignatureException { 497 // Obtain relevant bits of signing configuration 498 List<X509Certificate> signerCerts = signerConfig.certificates; 499 X509Certificate signingCert = signerCerts.get(0); 500 PublicKey publicKey = signingCert.getPublicKey(); 501 DigestAlgorithm digestAlgorithm = signerConfig.signatureDigestAlgorithm; 502 Pair<String, AlgorithmIdentifier> signatureAlgs = 503 getSignerInfoSignatureAlgorithm(publicKey, digestAlgorithm, 504 signerConfig.deterministicDsaSigning); 505 String jcaSignatureAlgorithm = signatureAlgs.getFirst(); 506 507 // Generate the cryptographic signature of the signature file 508 byte[] signatureBytes; 509 try { 510 Signature signature = Signature.getInstance(jcaSignatureAlgorithm); 511 signature.initSign(signerConfig.privateKey); 512 signature.update(signatureFileBytes); 513 signatureBytes = signature.sign(); 514 } catch (InvalidKeyException e) { 515 throw new InvalidKeyException("Failed to sign using " + jcaSignatureAlgorithm, e); 516 } catch (SignatureException e) { 517 throw new SignatureException("Failed to sign using " + jcaSignatureAlgorithm, e); 518 } 519 520 // Verify the signature against the public key in the signing certificate 521 try { 522 Signature signature = Signature.getInstance(jcaSignatureAlgorithm); 523 signature.initVerify(publicKey); 524 signature.update(signatureFileBytes); 525 if (!signature.verify(signatureBytes)) { 526 throw new SignatureException("Signature did not verify"); 527 } 528 } catch (InvalidKeyException e) { 529 throw new InvalidKeyException( 530 "Failed to verify generated " + jcaSignatureAlgorithm + " signature using" 531 + " public key from certificate", 532 e); 533 } catch (SignatureException e) { 534 throw new SignatureException( 535 "Failed to verify generated " + jcaSignatureAlgorithm + " signature using" 536 + " public key from certificate", 537 e); 538 } 539 540 AlgorithmIdentifier digestAlgorithmId = 541 getSignerInfoDigestAlgorithmOid(digestAlgorithm); 542 AlgorithmIdentifier signatureAlgorithmId = signatureAlgs.getSecond(); 543 try { 544 return ApkSigningBlockUtils.generatePkcs7DerEncodedMessage( 545 signatureBytes, 546 null, 547 signerCerts, digestAlgorithmId, 548 signatureAlgorithmId); 549 } catch (Asn1EncodingException | CertificateEncodingException ex) { 550 throw new SignatureException("Failed to encode signature block"); 551 } 552 } 553 554 555 getEntryDigestAttributeName(DigestAlgorithm digestAlgorithm)556 private static String getEntryDigestAttributeName(DigestAlgorithm digestAlgorithm) { 557 switch (digestAlgorithm) { 558 case SHA1: 559 return "SHA1-Digest"; 560 case SHA256: 561 return "SHA-256-Digest"; 562 default: 563 throw new IllegalArgumentException( 564 "Unexpected content digest algorithm: " + digestAlgorithm); 565 } 566 } 567 getManifestDigestAttributeName(DigestAlgorithm digestAlgorithm)568 private static String getManifestDigestAttributeName(DigestAlgorithm digestAlgorithm) { 569 switch (digestAlgorithm) { 570 case SHA1: 571 return "SHA1-Digest-Manifest"; 572 case SHA256: 573 return "SHA-256-Digest-Manifest"; 574 default: 575 throw new IllegalArgumentException( 576 "Unexpected content digest algorithm: " + digestAlgorithm); 577 } 578 } 579 } 580