I would like to know what exactly means the hashes from the field PackageSignatures when you obtain it with
adb shell dumpsys package com.myapp
for a given installed app.
I see two hashes:
signatures=PackageSignatures{abcabca [xyzxyzxy]}
The first one (abc) is different in each installation.
The second one (xyz) is fixed for a given apk.
Moreover, I would like to know whether the second one has any relationship with the public signature of the APK. I have several apks with the same public signature, but the second hash is different. Is that normal?
The signatures line on a recent Android version looks as follows:
signatures=PackageSignatures{9122424 version:2, signatures:[bbb2e2d2], past signatures:[]}
To understand this line we just have to look into the AOSP source code: PackageSignatures.java
First part
The first part abcabca comes from System.identityHashCode(this). Hence it generated the Java hashcode fro the current PackageSignatures instance. As this class has only one filed PackageParser.SigningDetails mSigningDetails; the hash code totally depends on the fields of the class SigningDetails:
Signature[] signatures;
int signatureSchemeVersion;
ArraySet publicKeys;
Version part (newer Android versions)
Your used Android version seems to be a bit old, as nowadays the second part is the signatureSchemeVersion - at the moment there are three signature schemes known: V1 (jarsigner), v2 and v3.
Third part (signatures)
The signatures you are interested in is generated by the following code:
Integer.toHexString(mSigningDetails.signatures[i].hashCode())
Where signatures[i] is an instance of the class [android.content.pm.Signature](https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/content/pm/Signature.java
).
The hashCode() is defined in this class to be generated by Arrays.hashCode(mSignature); where mSignature is a byte array that seem to contain the encoded version of the used X.509 signature certificate. Alternatively the raw byte[] can be supplied, hence it is difficult to tell the concrete content.
But from my understanding on your device this part should be directly linked to the signature of the APK file.
Related
What I Have
I have an Android app with some sensitive data. I want to make sure that the client app calls the server only if it has not been tampered with in any way. Basically, I want to check the integrity of the Android app.
What I Have Done
For that, I have implemented a method that can check if the signature is same as the signature I have signed the APK with. Here is a code sample,
fun isApkSignatureBroken(): Boolean {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, GET_SIGNATURES)
val signatures = packageInfo.signatures
if (signatures == null || signatures.isEmpty()) {
return true
}
return signatures
.map { it.toByteArray() }
.map { hash.getSha1(it) }
.none { it.equals(getRealAppSignature(), true) }
}
Here, getRealAppSignature() returns my actual signature with which I have signed the app.
Observations
I have seen this method to work well sometimes. Whenever a tampered app is discovered, this method returns true and the user is blocked from using the app and I am notified by an event. I have found many users being blocked in this way.
I have seen many other cases being reported in forums and other social media sites, that people are using my app even if they are having tampered apps. I have observed many of my apps having malformed version names and application names being used by many users. Like, if my original apps version is 2.2 then they will create a malformed version 2.2-TERMINATOR.
Analysis
I did some analysis on the apps and found some tampered apps from some forums. Those are clearly modded apps having malformed version and application names. Some even have minor UI changes. I tried to install those apps, but they can't be installed with the message "Package is corrupted".
I tried to run keytool on those apps to check their signature, like this,
keytool -list -printcert -jarfile TAMPERED_APP.apk
but I always got,
keytool error: java.lang.SecurityException: SHA1 digest error for classes.dex
I finally rooted my device, installed Xposed Framework and disabled "App Signature Verification" and then was able to install those apps. What I found was that the app was not getting blocked as my signature checking method always returned "false". That means, it always found the original signature inside the apk.
More Analysis
I spend some more time and was able to unpackage the APK using ApkTool. Inside the META-INF folder I found the CERT.RSA to only contain my original signature and no other signature. I opened the MANIFEST.MF file and found that all the entries have different SHA1 than my original APKs manifest file.
It clearly means that the app was modified and was signed by another signature (hence the change in MANIFEST.MF) but my CERTIFICATE.RSA only had my original signature because of which my app always got the original SHA from PackageManager.
Questions
How is the app being re-signed but the new signature is not stored in CERTIFICATE.RSA?
Why is my original certificate still present in CERTIFICATE.RSA?
If the app was resigned, then multiple signatures should be present? But that is true here.
In this situation, how do I detect that the app has been tampered with? Is there any way I can compute the SHA of the app myself instead of querying the PackageManager?
EDIT #1: The entire code is obfuscated using DexGuard. So, the chance of the tamper detection logic getting messed up is pretty less.
I'm trying check my app's release signature at runtime to prevent tampering. However, what I've been reading is that I need to log the signature during a run so that I can hard code it into my app for runtime comparison. But I see two problems with this:
1) The signature obtained while debugging (reading the log) will be different than the release signature.
2) If I get the release signature and then put it into my code for runtime comparison, won't that change the signature for the next release build... hence a chicken and egg problem?
What am I missing? Do static final strings not change the signature? Likewise, if I log release output does the log code not change the signature? What does and doesn't impact the release signature?
If compute the signature over bytes that include the signature itself then yes, you will have chicken and egg problem. But you don't have to: compute the signature over everything but the signature. That's in fact how signatures generated by jarsigner are done.
But what I think you really want, since apk files are already signed, is to check if the signer information matches your own. And that can be done by comparing the package's signer certificate with your own certificate.
You can see an adopted example from Android Security Cookbook that performs such check here.
If I want to change your apk here are the steps:
Open your apk with a an unzip program
Get one or two resources out of it ( ex: class files).
Make changes to it ( de-copmile, change, recompile)
Put it back in the APK.
Sign it
Publish it
I will have some problem At step 5, as I don't have your private key and your signature, I should create my own and sign it with my key. Then I should hope that no one checks the sign before installing the app ( which no one does! ).
So my tampered app has two differences with your app, changed resources AND changed sign certificate.
If you want to check the changed resources, ( signature over bytes) you will have the chicken and egg problem.
The easiest way to stop me is to hard code your app signature and check if it is changed at run time.
The sample code is at https://gist.github.com/scottyab/b849701972d57cf9562e
A simple method for this check:
private boolean validateAppSignature() {
APP_SIGN= "f10e2821bbbea527ea02200352313bc059445190";
PackageInfo packageInfo = this.getPackageManager().getPackageInfo(
getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature : packageInfo.signatures) {
// SHA256 the signature
String AppSign = DigestUtils.sha256Hex(signature.toByteArray());
return APP_SIGN.equalsIgnoreCase(AppSign);
}
return false;
}
Of course this is not a 100% solution even the obfuscated code can be changed. So I may find a way to by pass this check. But you make tampering harder for me :)
I'm trying to create my own version of Gesture Builder. In eclipse I selected android project from existing code and I renamed the project and package name to new gesture. Then I added in android:fadeOffset = "1000" in create gesture xml(so that I can create gestures for letters like t and f) and in AndroidManifest.xml I set the version name to NewGestures and I set a different icon but when I try to run it I get this error message:
"Re-installation failed due to different application signatures. You must perform a full uninstall of the application. WARNING: This will remove the application data! Do you want to uninstall?"
From what I've seen online I need to match the signature used originally on Gesture Builder, but I've no idea how to do this on eclipse, shouldn't the signature have been handled properly when I created from existing code? Any help would be very much appreciated. I just need this app working so I can get a gestures library for a different application I'm working on for college.
This message concerns the application signature. This happens when you are trying to install an application on your device while an application of the same package name is already installed, but signed with a different certificate (see details here).
For example:
You have exported and installed your application using your Google Play keystore (so using your actual developer's certificate), and now you are running/debugging it from Eclipse, implicitely using the debug certificate (which is different)
You have runned/debugged your application from Eclipse at home on this device, and now your are running it/debugging it from Eclipse with another computer (which is using a different implicit debug certificate)
etc
Normally, below the error message, you have a button that allows uninstalling/reinstalling. If not, just uninstall your app manually and everything will be fine again.
versionName:
The version number shown to users. This attribute can be set as a raw
string or as a reference to a string resource. The string has no other
purpose than to be displayed to users.
package:
The package name serves as a unique identifier for the application.
The package name declared in your manifest.xml is what makes your application unique. Therefore, there can not be two application installed with the same package name at the same time. If you try this, your error occurs.
I’m trying to sign an android .apk file on the server completely with php.
For this I need to generate the files contained in META-INF directory inside the .apk file(which is essentially a ZIP file). Creating the .MF and .SF files in php is fairly simple. However, I'm struggling with the .RSA file.
Unfortunately, I don’t know anything about cryptography and don’t even understand the basic terms. I only know some PHP basics, but I’m far from experienced and have no overview of the necessary libraries/functions. Therefore even after many hours of research, I still wasn’t able to create anything useful.
From what I understand, the .RSA file should contain:
digital signature of the .SF file
certificate with signers public key
This file should be PKCS7 formatted.
I was trying to use the phpseclib for this. I was able to create a private/public key/certificate from the examples on the internet, but I’m absolutely not able to put it all together to form the .RSA file.
Stackoverflow has been a great source of information and I probably found most of the answers to my questions here. However, now I’m stuck.
Could you guys please give me some php example code(ideally)? Or even some pseudocode/algorithm…
Is this even possible to accomplish with phpseclib/php? Will there be some “bit shifting” necessary?
If possible, please don’t point me to the source code of jarsigner…I looked at it, but don’t know much about java and it only brought more questions.
Update:
It’s hard to move in the right direction when you have no idea what you are actually doing :D…
This is what I tried so far, but without any success:
I generated public/private key pair with ssh
ssh-keygen -t rsa1
I used the previously generated keypair to create a self-signed certificate with phpsceclib as described here:
http://phpseclib.sourceforge.net/new/x509/tutorial.html#selfsigning
I passed the certificate ($signcert), private key ($privkey - from ssh generated file) and data for signing ($infilename –> the .SF file) to the openssl function openssl_pkcs7_sign():
openssl_pkcs7_sign ($infilename , $outfilename , $signcert , $privkey , array(), PKCS7_DETACHED|PKCS7_BINARY);
However, the generated result seems to be in something called PEM format (human readable). Besides the fact that it’s not in binary DER (don’t even know what that is…just guessing that it should be DER) which the final .RSA file should be, there are also some other issues with it:
the result also contains the content of the .SF file (the original data to be signed itself)
it contains some unnecessary header information as regular text string
lines end with “\n” and not with “\r\n”
the signature (not really sure what the other data in the result is, but probably the signature) is base64 encoded
Am I doing at least something right here? What other steps have to be taken to make it work? What properties should I set when creating the certificate according to the mentioned link? How could I transfer the result to binary DER-formatted .RSA file containing the .SF signature and certificate with signers public key?
Update 2:
I finally had some time to continue with my little experiment. I went through my code and tried to change different parameters. Finally I found the issue. It seems that I only needed to turn on the PKCS7_NOATTR flag in the openssl_pkc7_sign().
Here is the code
$configs = array('digest_alg' => 'sha1',
'x509_extensions' => 'v3_ca',
'req_extensions' => 'v3_req',
'private_key_bits' => 1024,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'encrypt_key' => true,
'encrypt_key_cipher' => OPENSSL_CIPHER_3DES);
// Generate private/public key pair and certificate
$privkey = openssl_pkey_new($configs);
$dn = array("commonName" => "name",
"emailAddress" => "me#example.com");
$csr = openssl_csr_new($dn, $privkey, $configs);
$sscert = openssl_csr_sign($csr, NULL, $privkey, 999, $configs);
// Sign the .SF file
openssl_pkcs7_sign ( $sfFile , $rsaFile , $sscert , $privkey, array(),
PKCS7_DETACHED|PKCS7_BINARY|PKCS7_NOATTR);
I only had time to check it on a couple of samples, but it seems to be working now. Here is a very simple demonstration www.balabeng.com/?q=appsigner
This question already has answers here:
How to handle a lost KeyStore password in Android?
(42 answers)
Closed 4 years ago.
I want to update my Application to playstore and I lost my keystore password.Now I am trying to create a new one . Will there be any problem if I build APK with another keystore password ?
As #CRUSADER said, Android Keystore Password Recovery might work for you. I could retrieve my password with it. Here is how to do that. I was on Windows 7.
Run the following command,
java -jar AndroidKeystoreBrute_v1.05.jar
Then you will get the following guide,
AndroidKeystorePasswordRecoveryTool by M#xiking
Version 1.03
There are 3 Methods to recover the key for your Keystore:
1: simply bruteforce - good luck
2: dictionary attack - your password has to be in the dictionary
3: smart dictionary attack - you specify a dictionary with regular pieces you use in your passwords. Numbers are automat
icly added and first letter will tested uppercase and lowercase
args:
-m <1..3> Method
-k <path> path to your keystore
-d <path> dictionary (for method 2 and 3)
-w saves the certificate in a new Keystore with same passwort than key
-start <String> sets start String of the word (for method 1)
-p use common replacements like '#' for 'a'(for method 3) WARNING - very slow!!
-h prints this helpscreen
Max memory: 247M
I've used method 2 because I had some guess of my password. I typed the following command,
java -jar AndroidKeystoreBrute_v1.05.jar -m 2 -k android.keystore -d dict.txt
In case you might wonder, my dict.txt was something like this,
gussedpassword1
gussedpassword2
gussedpassword3
I could successfully retrieve my password and alias.
Well, you are not the first to lose keystore password..
I use this one for bruteforce when I got stuck: Android Keystore recover
You can go with few guesses.
And as #StinePike mentioned.. you cannot continue updating your existing applications
There will be no problem for uploading new application. But you can not continue updating your existing application with new keystore. You need to create new applications by resubmitting them if you want to update your previous applications.
Always preserve the keystore with a best possible way :(
If you have lost the password to the keystore, then the only option is to create a new signing key. There will be no problem doing so.
However, you will not be able to update the existing application. And neither can you upload another application with the same package name.
For example, if you current package is:
com.example.mycurrentapp
You will need to use a different package name to go along with your new signing key. For example, you can change the package name to:
com.example.mynewapp
The above, are of course, illustrations. ;-)
The Google Play ecosystem identifies different applications using their package names and therefore, they have to be unique. Unfortunately, if you have a good user base, unless they install your new app, they will not be able to get updates to the older app.
It's unfortunate, but when you lose your keystore, or the password to your keystore, your application is orphaned. The only thing you can do is resubmit your app to the market under a new key. You will not be able to update your app without the key.
You will have to publish your app again with a new keystore and under a different package name, or remember your password.