publicstatic ByteBuffer[] sign( ByteBuffer inputApk, List<SignerConfig> signerConfigs) throws ApkParseException, InvalidKeyException, SignatureException { // Slice/create a view in the inputApk to make sure that: // 1. inputApk is what's between position and limit of the original inputApk, and // 2. changes to position, limit, and byte order are not reflected in the original. ByteBuffer originalInputApk = inputApk; inputApk = originalInputApk.slice(); inputApk.order(ByteOrder.LITTLE_ENDIAN);
// Locate ZIP End of Central Directory (EoCD), Central Directory, and check that Central // Directory is immediately followed by the ZIP End of Central Directory. int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(inputApk); if (eocdOffset == -1) { thrownew ApkParseException("Failed to locate ZIP End of Central Directory"); } if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(inputApk, eocdOffset)) { thrownew ApkParseException("ZIP64 format not supported"); } inputApk.position(eocdOffset); long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(inputApk); if (centralDirSizeLong > Integer.MAX_VALUE) { thrownew ApkParseException( "ZIP Central Directory size out of range: " + centralDirSizeLong); } int centralDirSize = (int) centralDirSizeLong; long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(inputApk); if (centralDirOffsetLong > Integer.MAX_VALUE) { thrownew ApkParseException( "ZIP Central Directory offset in file out of range: " + centralDirOffsetLong); } int centralDirOffset = (int) centralDirOffsetLong; int expectedEocdOffset = centralDirOffset + centralDirSize; if (expectedEocdOffset < centralDirOffset) { thrownew ApkParseException( "ZIP Central Directory extent too large. Offset: " + centralDirOffset + ", size: " + centralDirSize); } if (eocdOffset != expectedEocdOffset) { thrownew ApkParseException( "ZIP Central Directory not immeiately followed by ZIP End of" + " Central Directory. CD end: " + expectedEocdOffset + ", EoCD start: " + eocdOffset); }
// Create ByteBuffers holding the contents of everything before ZIP Central Directory, // ZIP Central Directory, and ZIP End of Central Directory. inputApk.clear(); ByteBuffer beforeCentralDir = getByteBuffer(inputApk, centralDirOffset); ByteBuffer centralDir = getByteBuffer(inputApk, eocdOffset - centralDirOffset); // Create a copy of End of Central Directory because we'll need modify its contents later. byte[] eocdBytes = newbyte[inputApk.remaining()]; inputApk.get(eocdBytes); ByteBuffer eocd = ByteBuffer.wrap(eocdBytes); eocd.order(inputApk.order());
// Figure which which digests to use for APK contents. Set<Integer> contentDigestAlgorithms = new HashSet<>(); for (SignerConfig signerConfig : signerConfigs) { for (int signatureAlgorithm : signerConfig.signatureAlgorithms) { contentDigestAlgorithms.add( getSignatureAlgorithmContentDigestAlgorithm(signatureAlgorithm)); } }
// Compute digests of APK contents. Map<Integer, byte[]> contentDigests; // digest algorithm ID -> digest try { contentDigests = computeContentDigests( contentDigestAlgorithms, new ByteBuffer[] {beforeCentralDir, centralDir, eocd}); } catch (DigestException e) { thrownew SignatureException("Failed to compute digests of APK", e); }
// Sign the digests and wrap the signatures and signer info into an APK Signing Block. ByteBuffer apkSigningBlock = ByteBuffer.wrap(generateApkSigningBlock(signerConfigs, contentDigests));
// Update Central Directory Offset in End of Central Directory Record. Central Directory // follows the APK Signing Block and thus is shifted by the size of the APK Signing Block. centralDirOffset += apkSigningBlock.remaining(); eocd.clear(); ZipUtils.setZipEocdCentralDirectoryOffset(eocd, centralDirOffset);
// Follow the Java NIO pattern for ByteBuffer whose contents have been consumed. originalInputApk.position(originalInputApk.limit());
// Reset positions (to 0) and limits (to capacity) in the ByteBuffers below to follow the // Java NIO pattern for ByteBuffers which are ready for their contents to be read by caller. // Contrary to the name, this does not clear the contents of these ByteBuffer. beforeCentralDir.clear(); centralDir.clear(); eocd.clear();
// Insert APK Signing Block immediately before the ZIP Central Directory. returnnew ByteBuffer[] { beforeCentralDir, apkSigningBlock, centralDir, eocd, }; }
//SIGNEDDATA 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 //BLOCK FORMAT: // * length-prefixed signed data // * length-prefixed sequence of length-prefixed signatures: // * uint32: signature algorithm ID // * length-prefixed bytes: signature of signed data // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded) //SIGNED BLOCK FORMAT: // uint64: size (excluding this field) // repeated ID-value pairs: // uint64: size (excluding this field) // uint32: ID // (size - 4) bytes: value // uint64: size (same as the one above) // uint128: magic
对于验证过程
APK Signature Scheme v2 verification
Locate the APK Signing Block and verify that:
Two size fields of APK Signing Block contain the same value.
ZIP Central Directory is immediately followed by ZIP End of Central Directory record.
ZIP End of Central Directory is not followed by more data.
Locate the first APK Signature Scheme v2 Block inside the APK Signing Block. If the v2 Block if present, proceed to step 3. Otherwise, fall back to verifying the APK using v1 scheme.
For each signer in the APK Signature Scheme v2 Block:Choose the strongest supported signature algorithm ID from signatures. The strength ordering is up to each implementation/platform version.Verify the corresponding signature from signatures against signed data using public key. (It is now safe to parse signed data.)Verify that the ordered list of signature algorithm IDs in digests and signatures is identical. (This is to prevent signature stripping/addition.)Compute the digest of APK contents using the same digest algorithm as the digest algorithm used by the signature algorithm.Verify that the computed digest is identical to the corresponding digest from digests.Verify that SubjectPublicKeyInfo of the first certificate of certificates is identical to public key.
Verification succeeds if at least one signer was found and step 3 succeeded for each found signer.