Android APK signature comparison
Users who have published Android apps should know that the release of Android APK requires signatures. The signature mechanism plays an important role in Android applications and frameworks.
For example, the Android system prohibits updating and installing APK with inconsistent signatures. If the application requires the system permission, ensure that the APK signature is consistent with the Framework signature. InAPK CrackIn this article, we learned that to crack an APK, you must sign the APK again. This signature cannot be consistent with the original APK signature. (Unless the private key of the original APK author leaks, it is another layer of software security issues .)
In short, the signature Mechanism indicates the APK issuer. Therefore, from the perspective of software security, we can compare the APK signature to determine whether the APK is officially released, instead of the pirated software that has been cracked and tampered with and re-signed and packaged ".
Android signature mechanism
To demonstrate the effectiveness of APK Signature Comparison on software security, we need to understand the signature mechanism of Android APK. For better understanding, let's start with a batch processing command of the Auto-Sign tool.
InAPK CrackIn this article, we learned that to sign an unsigned APK, you can use a tool called Auto-sign. The Auto-sign tool actually runs a batch processing command called Sign. bat. Open the batch file in a text editor and we can find that the command to implement the signature function is mainly this line of command:
Java-jar signapk. jar testkey. x509.pem testkey. pk8 update.apk update_signed.apk
The significance of this command is: Use the executable jar package signapk. jar to
Testkey. x509.pem
"This public key file and"
Testkey. pk8
"This private key file pair"
Update.apk
". The signed file is saved as"
Update_signed.apk
".
The generation of the private key and public key used here is not described here. You can find many materials in this area. Here we will talk about what signapk. jar has done.
Signapk. jar is a signature tool in the Android source code package. As Android is an open-source project, we are very happy to find the source code of signapk. jar! Path:/build/tools/signapk/SignApk. java.
Comparing an unsigned APK and a signed APK, we will find that there is a folder named META-INF in the signed APK package. There are three files named MANIFEST. MF, CERT. SF, and CERT. RSA. Signapk. jar generates these files (other files have not changed. So we can easily remove the original signature information ).
By reading the signapk source code, we can clarify the entire process of signing the APK package.
1,GenerateMANIFEST. MFFile:
The program traverses all the files (entries) in the update.apk package. For non-Folder unsigned files, generate the digital signature information of SHA1 one by one, and then encode it with base64. For specific code, see this method:
Private static Manifest addDigestsToManifest (JarFile jar)
The key code is as follows:
1 for (JarEntry entry: byName. values ()){
2 String name = entry. getName ();
3 if (! Entry. isDirectory ()&&! Name. equals (JarFile. MANIFEST_NAME )&&
4! Name. equals (CERT_SF_NAME )&&! Name. equals (CERT_RSA_NAME )&&
5 (stripPattern = null |! StripPattern. matcher (name). matches ())){
6 InputStream data = jar. getInputStream (entry );
7 while (num = data. read (buffer)> 0 ){
8 md. update (buffer, 0, num );
9}
10 Attributes attr = null;
11 if (input! = Null) attr = input. getAttributes (name );
12 attr = attr! = Null? New Attributes (attr): new Attributes ();
13 attr. putValue ("SHA1-Digest", base64.encode (md. digest ()));
14 output. getEntries (). put (name, attr );
15}
16}
Then, write the generated signature to the MANIFEST. MF file. The key code is as follows:
1 Manifest manifest = addDigestsToManifest (inputJar );
2 je = new JarEntry (JarFile. MANIFEST_NAME );
3 je. setTime (timestamp );
4 outputJar. putNextEntry (je );
5 manifest. write (outputJar );
Here is a brief introduction to the SHA1 digital signature. In short, it is a secure hash algorithm, similar to the MD5 algorithm. It converts an input of any length into an output of a fixed length using the hash algorithm (this is called "abstract information "). You cannot use this abstract information to restore the original information. In addition, it ensures that the abstract information of different information is different from each other. Therefore, if you change the file in the apk package, the modified file summary information is different from the MANIFEST. MF check information during apk installation verification, so the program cannot be successfully installed.
2,GenerateCERT. SFFile:
The Manifest generated in the previous step is signed with the private key using the SHA1-RSA algorithm. The key code is as follows:
1 Signature signature = Signature. getInstance ("SHA1withRSA ");
2 signature. initSign (privateKey );
3 je = new JarEntry (CERT_SF_NAME );
4 je. setTime (timestamp );
5 outputJar. putNextEntry (je );
6 writeSignatureFile (manifest,
7 new SignatureOutputStream (outputJar, signature ));
RSA is an asymmetric encryption algorithm. Use the RSA algorithm to encrypt the digest information. Only the public key can be used for decryption during installation. After decryption, it is compared with the unencrypted summary information. If it is consistent, it indicates that the content is not modified abnormally.
3,GenerateCERT. RSAFile:
No key information is used to generate MANIFEST. MF. The private key file is used to generate the CERT. SF file. We can easily guess that the generation of the CERT. RSA file must be related to the public key.
The CERT. RSA file stores information such as the public key and the encryption algorithm used. The core code is as follows:
1 je = new JarEntry (CERT_RSA_NAME );
2 je. setTime (timestamp );
3 outputJar. putNextEntry (je );
4 writeSignatureBlock (signature, publicKey, outputJar );
The writeSignatureBlock code is as follows:
1 private static void writeSignatureBlock (
2 Signature signature, X509Certificate publicKey, OutputStream out)
3 throws IOException, GeneralSecurityException {
4 SignerInfo signerInfo = new SignerInfo (
5 new X500Name (publicKey. getIssuerX500Principal (). getName ()),
6 publicKey. getSerialNumber (),
7 AlgorithmId. get ("SHA1 "),
8 AlgorithmId. get ("RSA "),
9 signature. sign ());
10
11 PKCS7 pkcs7 = new PKCS7 (
12 new AlgorithmId [] {AlgorithmId. get ("SHA1 ")},
13 new ContentInfo (ContentInfo. DATA_OID, null ),
14 new X509Certificate [] {publicKey },
15 new SignerInfo [] {signerInfo });
16
17 pkcs7.encodeSignedData (out );
18}
Now, after analyzing the signature process of the APK package, we can clearly realize that:
1. the Android signature mechanism is actually a verification mechanism for the integrity of the APK package and the uniqueness of the publishing organization.
2. the Android signature mechanism cannot prevent the APK package from being modified, but the modified signature cannot be consistent with the original signature. (Except for private keys ).
3. The public key encrypted in the APK package is packaged in the APK package, and different private keys correspond to different public keys. In other words, the APK public keys of different private key signatures must also be different. Therefore, we can compare the public key to determine whether the private key is consistent.
APK signature comparison Implementation Method
Now, through the analysis of the Android signature mechanism, we theoretically prove that an APK publishing organization can be determined by comparing the APK public key. And this publishing organization is difficult to disguise. We can think of it as not camouflage for the time being.
With the theoretical foundation, we can start to practice. So how can I obtain the public key information of the APK file? Because the Android system installer will certainly obtain the APK information for comparison, we can get some ideas and help through the Android source code.
There is a hidden class in the source code for parsing the APK package. This class is called PackageParser and the path is frameworks \ base \ core \ java \ android \ content \ pm \ PackageParser. java. When we need to obtain information about the APK package, we can directly use this class. The following code is an example function:
1 private PackageInfo parsePackage (String archiveFilePath, int flags ){
2
3 PackageParser packageParser = new PackageParser (archiveFilePath );
4 DisplayMetrics metrics = new DisplayMetrics ();
5 metrics. setToDefaults ();
6 final File sourceFile = new File (archiveFilePath );
7 PackageParser. Package pkg = packageParser. parsePackage (
8 sourceFile, archiveFilePath, metrics, 0 );
9 if (pkg = null ){
10 return null;
11}
12
13 packageParser. collectCertificates (pkg, 0 );
14
15 return PackageParser. generatePackageInfo (pkg, null, flags, 0, 0 );
16}
The archiveFilePath parameter specifies the APK file path. flags must set the PackageManager. GET_SIGNATURES bit to ensure that the certificate signature information is returned.
For more information about how to obtain signature information through PackageParser, see public boolean collectCertificates (Package pkg, int flags) and private Certificate [] loadCertificates (JarFile jarFile, jarEntry je, byte [] readBuffer) method. For how to use hidden classes and methods in Android Application Development, refer to my article:How to Use hidden APIs in Android Application Development.
Then, we can use packageInfo. signatures to access the APK signature information. It is also worth noting the correspondence between Signature in Android and Certificate in Java. The relationship between them is shown in the code below:
1 pkg. mSignatures = new Signature [certs. length];
2 for (int I = 0; I 3 pkg. mSignatures [I] = new Signature (
4 certs [I]. getEncoded ());
5}
That is, signature = new Signature (certificate. getEncoded (); The certificate contains the public key and other basic information about the certificate. Certificates must be different for different public keys. We can use the getPublicKey method of certificate to obtain public key information. Therefore, comparing the signature certificate is essentially comparing the public key information.
OK. After obtaining the APK signature certificate, the comparison is correct. This is simple. The function is as follows:
1 private boolean IsSignaturesSame (Signature [] s1, Signature [] s2 ){
2 if (s1 = null ){
3 return false;
4}
5 if (s2 = null ){
6 return false;
7}
8. HashSet Set1 = new HashSet ();
9 for (Signature sig: s1 ){
10 set1.add (sig );
11}
12 HashSet Set2 = new HashSet ();
13 for (Signature sig: s2 ){
14 set2.add (sig );
15}
16 // Make sure s2 contains all signatures in s1.
17 if (set1.equals (set2 )){
18 return true;
19}
20 return false;
21}
Application scenarios of APK signature comparison
After the above discussion, we must have understood the principles of signature comparison and my implementation methods. So when can we use signature comparison to ensure the security of Android APK software?
I personally think there are three main scenarios:
1. program self-detection. When the program is running, self-Signed comparison is performed. Comparison samples can be stored in the APK package or on the cloud. The disadvantage is that when a program is cracked, the self-detection function may also be damaged, making it invalid.
2. Reliable third-party detection. Trusted Third-party programs are responsible for APK software security issues. Comparison samples are collected by a third party and placed on the cloud. This method is applicable to software download markets such as anti-virus security software or APP Market. The disadvantage is that it requires network connection detection and cannot implement functions without a network. (It is impossible to store a large amount of signature data on a mobile device ).
3. Limited system installation. This involves modifying the Android system. Only APK with certain certificates can be installed. The software publisher must apply for a certificate from the system release. If problems are found, the responsibility of the software publisher can be tracked. Applicable to system providers or end product manufacturers. The disadvantage is that it is too closed, which is not conducive to the openness of the system.
The preceding three scenarios have their own shortcomings, but they are not insurmountable. For example, we can use native method to implement the program self-detection function. Software Security is a complex issue. It often requires the joint use of multiple technologies to better protect the software from malicious destruction.