eocd的倒数第三部分[offset of start of central directory with respect to the starting disk number]标记了central directory的偏移:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
End of central directory record:
end of central dir signature 4 bytes (0x06054b50) number of this disk 2 bytes number of the disk with the start of the central directory 2 bytes total number of entries in the central directory on this disk 2 bytes total number of entries in the central directory 2 bytes size of the central directory 4 bytes offset of start of central directory with respect to the starting disk number 4 bytes .ZIP file comment length 2 bytes .ZIP file comment (variable size)
central file header signature 4 bytes (0x02014b50) version made by 2 bytes version needed to extract 2 bytes general purpose bit flag 2 bytes compression method 2 bytes last mod file time 2 bytes last mod file date 2 bytes crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes file name length 2 bytes extra field length 2 bytes file comment length 2 bytes disk number start 2 bytes internal file attributes 2 bytes external file attributes 4 bytes relative offset of local header 4 bytes
file name (variable size) extra field (variable size) file comment (variable size)
Digital signature:
header signature 4 bytes (0x05054b50) size of data 2 bytes signature data (variable size)
所以我们找到第一个文件AndroidManifest.xml的[relative offset of local header]为0x00000000,即local file header 1的地址是0x00000000。
Local file header
0x00000000的内容如下:
Local file header的格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Local file header:
local file header signature 4 bytes (0x04034b50) version needed to extract 2 bytes general purpose bit flag 2 bytes compression method 2 bytes last mod file time 2 bytes last mod file date 2 bytes crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes file name length 2 bytes extra field length 2 bytes
file name (variable size) extra field (variable size)
// Proof-of-rotation struct: // A uint32 version code followed by basically a singly linked list of nodes, called levels // here, each of which have the following structure: // * length-prefix for the entire level // - length-prefixed signed data (if previous level exists) // * length-prefixed X509 Certificate // * uint32 signature algorithm ID describing how this signed data was signed // - uint32 flags describing how to treat the cert contained in this level // - uint32 signature algorithm ID to use to verify the signature of the next level. The // algorithm here must match the one in the signed data section of the next level. // - length-prefixed signature over the signed data in this level. The signature here // is verified using the certificate from the previous level. // The linking is provided by the certificate of each level signing the one of the next.
try {
// get the version code, but don't do anything with it: creator knew about all our flags porBuf.getInt(); HashSet<X509Certificate> certHistorySet = newHashSet<>(); while (porBuf.hasRemaining()) { levelCount++; ByteBufferlevel= getLengthPrefixedSlice(porBuf); ByteBuffersignedData= getLengthPrefixedSlice(level); // 获取当前level证书的信息 intflags= level.getInt(); intsigAlgorithm= level.getInt(); byte[] signature = readLengthPrefixedByteArray(level); // 获取上一level证书为当前level证书生成的签名
// 使用上一个level的证书去验证下一个level的证书 if (lastCert != null) { // 获取上一个证书的数据 Pair<String, ? extendsAlgorithmParameterSpec> sigAlgParams = getSignatureAlgorithmJcaSignatureAlgorithm(lastSigAlgorithm); // 获取上一个证书的公钥 PublicKeypublicKey= lastCert.getPublicKey(); // 初始化签名信息 Signaturesig= Signature.getInstance(sigAlgParams.first); sig.initVerify(publicKey); if (sigAlgParams.second != null) { sig.setParameter(sigAlgParams.second); } // 设置当前level证书的数据 sig.update(signedData); // 使用上一level证书为当前level证书生成的签名去验证当前level证书是否有效 if (!sig.verify(signature)) { thrownewSecurityException("Unable to verify signature of certificate #" + levelCount + " using " + sigAlgParams.first + " when verifying" + " Proof-of-rotation record"); } } // 使用证书信息去创建证书,将其赋值给lastCert并将其丢入certs队列 signedData.rewind(); byte[] encodedCert = readLengthPrefixedByteArray(signedData); intsignedSigAlgorithm= signedData.getInt(); if (lastCert != null && lastSigAlgorithm != signedSigAlgorithm) { thrownewSecurityException("Signing algorithm ID mismatch for certificate #" + levelCount + " when verifying Proof-of-rotation record"); } lastCert = (X509Certificate) certFactory.generateCertificate(newByteArrayInputStream(encodedCert)); lastCert = newVerbatimX509Certificate(lastCert, encodedCert);
lastSigAlgorithm = sigAlgorithm; if (certHistorySet.contains(lastCert)) { thrownewSecurityException("Encountered duplicate entries in " + "Proof-of-rotation record at certificate #" + levelCount + ". All " + "signing certificates should be unique"); } certHistorySet.add(lastCert); certs.add(lastCert); flagsList.add(flags); } } catch (IOException | BufferUnderflowException e) { thrownewIOException("Failed to parse Proof-of-rotation record", e); } catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | SignatureException e) { thrownewSecurityException( "Failed to verify signature over signed data for certificate #" + levelCount + " when verifying Proof-of-rotation record", e); } catch (CertificateException e) { thrownewSecurityException("Failed to decode certificate #" + levelCount + " when verifying Proof-of-rotation record", e); } returnnewVerifiedProofOfRotation(certs, flagsList); }