So using the keytool command I was able to generate a hash key for my Android app which uses Facebook login. For purposes of this question, the hash the keytool outputted was "abcdefg=". But when I try to sign on to Facebook from my app, the error says "Key hash abcdefg does not match any stored key hashes" and shows the same exact key I got from keytool just without the equal sign at the end. Why is it not working? Also, when I try to manually type in the key hash on my Facebook developer console (instead of copy/paste), it won't take the key without the equals sign because it only takes keys whose character count is divisible by 4 (my key with equals sign has 28 chars, the key without has only 27 chars). Can someone help?
updateLanguage(getApplicationContext(), "en");
printHashKey(getApplicationContext(),"ur application package name here");
Note1: Create apk file with release keystore.then run on device the keyhash will print logcat. copy the keyhash and place it on facebook app edit setting page.
Note2: correct keyhash generate only on device. dont use simulator for getting keyhash.
public static String printHashKey(Context context, String packagename)
{
String TAG = packagename;
try
{
Log.d(TAG, "keyHash: start");
PackageInfo info = context.getPackageManager().getPackageInfo(TAG,PackageManager.GET_SIGNATURES);
for (Signature signature: info.signatures)
{
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
String keyHash = Base64.encodeToString(md.digest(), Base64.DEFAULT);
Log.d(TAG, "keyHash: " + keyHash);
return keyHash;
}
Log.d(TAG, "keyHash: end");
}
catch (NameNotFoundException e)
{
Log.d(TAG, "keyHash: name:"+e);
}
catch (NoSuchAlgorithmException e)
{
Log.d(TAG, "keyHash: name:"+e);
}
return "error";
}
public static void updateLanguage(Context context, String code)
{
Locale locale = new Locale(code);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
}
Related
We've got an android app with sms login feature. When user gets an sms message in the format of, for example "Your auth code: 1234 KLa37su2s0g", then the code "1234" is automatically inserted in the code prompt field.
For android devices with google services we use google's SMS Retriever API.
For huawei devices (which dont support google services) we use Huawei SMS Retriever API.
The main issue is that we get different hash codes (in the above example: "KLa37su2s0g") for google and huawei SMS Retriever APIs when trying to calculate those hash codes for release builds.
So the question is: do these sms hash codes should be the same or different for google and huawei?
There is no problem with the difference between the hash codes of the SMS messages provided by Google and Huawei.
The hash_value field in the SMS message is generated by the HMS Core SDK to uniquely identify the current application. Huawei hash codes are generated by the HMS Core SDK.
For Google SMS Hashcode: https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string
Convert the certificate used to sign the app to lower-case hex
string
Append that hex string to the app package name, separated by
a single space
Compute the SHA-256 of the combined string and
convert the result to a Base64 string
The first 11 characters of the
Base64 string is the hash to use in the SMS
For HMS SMS Hash code: You get your hash value by implementing the following class:
public class hashcodeHMS extends ContextWrapper {
public static final String TAG = hashcodeHMS.class.getSimpleName();
public hashcodeHMS(Context context) {
super(context);
}
public MessageDigest getMessageDigest() {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "No Such Algorithm.", e);
}
return messageDigest;
}
public String getSignature(Context context, String packageName) {
PackageManager packageManager = context.getPackageManager();
Signature[] signatureArrs;
try {
signatureArrs = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Package name inexistent.");
return "";
}
if (null == signatureArrs || 0 == signatureArrs.length) {
Log.e(TAG, "signature is null.");
return "";
}
Log.e("hashhms =>", signatureArrs[0].toCharsString());
return signatureArrs[0].toCharsString();
}
public String getHashCode(String packageName, MessageDigest messageDigest, String signature) {
String appInfo = packageName + " " + signature;
messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
byte[] hashSignature = messageDigest.digest();
hashSignature = Arrays.copyOfRange(hashSignature, 0, 9);
String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
base64Hash = base64Hash.substring(0, 11);
return base64Hash;
}
}
Is there a way to retrieve the signature of the key used to sign an APK? I signed my APK with my key from my keystore. How can I retrieve it programmatically?
You can access the APK's signing signature like this using the PackageManager class
http://developer.android.com/reference/android/content/pm/PackageManager.html
Signature[] sigs = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature sig : sigs)
{
Trace.i("MyApp", "Signature hashcode : " + sig.hashCode());
}
I've used this to compare with the hashcode for my debug key, as a way to identify whether the APK is a debug APK or a release APK.
The package manager will give you the signing certificate for any installed package. You can then print out the details of the signing key, e.g.
final PackageManager packageManager = context.getPackageManager();
final List<PackageInfo> packageList = packageManager.getInstalledPackages(PackageManager.GET_SIGNATURES);
for (PackageInfo p : packageList) {
final String strName = p.applicationInfo.loadLabel(packageManager).toString();
final String strVendor = p.packageName;
sb.append("<br>" + strName + " / " + strVendor + "<br>");
final Signature[] arrSignatures = p.signatures;
for (final Signature sig : arrSignatures) {
/*
* Get the X.509 certificate.
*/
final byte[] rawCert = sig.toByteArray();
InputStream certStream = new ByteArrayInputStream(rawCert);
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
X509Certificate x509Cert = (X509Certificate) certFactory.generateCertificate(certStream);
sb.append("Certificate subject: " + x509Cert.getSubjectDN() + "<br>");
sb.append("Certificate issuer: " + x509Cert.getIssuerDN() + "<br>");
sb.append("Certificate serial number: " + x509Cert.getSerialNumber() + "<br>");
sb.append("<br>");
}
catch (CertificateException e) {
// e.printStackTrace();
}
}
}
My situation is that I have a pre-installed apk which use a wrong key-store . So directly install will give a fail because of inconsistent signature.I need to check the signature first to make sure it can be install smoothly.
Here is my solution.
As this code says, you can get the signature from an installed apk.
details:
Signature sig = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
Secondly: get the releaseApk hashCode to compare. In my case, I downloaded this apk from my server, and put it in the sd_card.
Signature releaseSig = context.getPackageManager().getPackageArchiveInfo("/mnt/sdcard/myReleaseApk.apk", PackageManager.GET_SIGNATURES).signatures[0];
Finally,compare the hashCode.
return sig.hashCode() == releaseSig.hashCode;
I have tried the code above, it works just fine. If the hashCode is different, you should just uninstall the old apk or if it's a system app and the device is rooted, you can just use runtime to remove it,and then install the new signature apk.
I know that's a question answered before, but no one seems to work and i spend a week on int.
I tried to get my Key Hash from key tool and put them on Facebook app Key hashes, release and debug. No one worked.
"(404) Key hash xxxxxxxxxxxx does not match any storeed key hashes"
That's rare, my keys are different.
So i tried:
PackageInfo info = getPackageManager().getPackageInfo(
"com.pakage.example",
PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
String result = Base64.encodeToString(md.digest(), Base64.DEFAULT);
Toast.makeText(MainActivity.this , result + result.toUpperCase(), Toast.LENGTH_LONG).show();
}
That return me the same Key Has request by Facebook (xxxxxxxxxxxx), so i added them to Facebook App, but Facebook return me "(404) Key hash xxxxxxxxxxxx does not match any storeed key hashes".
When i removed Facebook App from my device, i was avaliebe to share status via WebDialog.
My final code seems to:
if (FacebookDialog.canPresentShareDialog(parent, FacebookDialog.ShareDialogFeature.SHARE_DIALOG))
{
publishFacebookDialog(points); //FacebookDialog
}
else
{
Session session = Session.getActiveSession();
if (session != null && session.isOpened()) {
publishFeedDialog(points); //WebDialog
}
else
{
Intent intent = new Intent(parent, LoginUsingCustomFragmentActivity.class); //From FacebookSDK demo
parent.startActivityForResult(intent, 666);
}
}
PublishFacebookDialog function:
private void publishFacebookDialog(String puntuacion) {
FacebookDialog shareDialog = new FacebookDialog.ShareDialogBuilder(parent)
.setLink("bla")
.setCaption("bla")
.setName("bla")
.build();
uiHelper.trackPendingDialogCall(shareDialog.present());
}
PublishFeedDialog function:
private void publishFeedDialog(String puntuacion) {
Bundle params = new Bundle();
params.putString("name", "bla");
params.putString("caption", "bla");
params.putString("link", "bla");
WebDialog feedDialog = ( new WebDialog.FeedDialogBuilder(parent,Session.getActiveSession(),params))
.setOnCompleteListener(new OnCompleteListener() {
#Override
public void onComplete(Bundle values, FacebookException error) {
if (error == null) {
final String postId = values.getString("post_id");
if (postId != null) {
} else {
}
} else if (error instanceof FacebookOperationCanceledException) {
// User clicked the "x" button
} else {
// Generic, ex: network error
}
}
})
.build();
feedDialog.show();
}
The LoginUsingCustomFragmentActivity class is the same that FacebookSDK
Tried:
Remove app from facebook and re add.
Replace symbols like /_+ etc, like from other answered posts.
Use the same key hash reported by "(404) Key hash xxxxxxxxxxxx does not ...." on Facebook App.
Any idea?
Lot of thanks.
I was facing the same problem, the hash code that i generated through openssl was wrong i then used this function and it help. Hope it help you all too.
private void printKeyHash() {
// Add code to print out the key hash
try {
PackageInfo info = getPackageManager().getPackageInfo("YOUR PACKAGE NAME", PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
Log.d("KeyHash:", Base64.encodeToString(md.digest(), Base64.DEFAULT));
}
} catch (NameNotFoundException e) {
Log.e("KeyHash:", e.toString());
} catch (NoSuchAlgorithmException e) {
Log.e("KeyHash:", e.toString());
}
}
Use the hash code that is printed in logs and enjoy. Happy Coding :)
See my answer here, there are couple of things to be aware of when adding your hashkey :
https://stackoverflow.com/a/27290250/1495261
As all we do I have application which is signed by debug.keystore (by default) when it is in development mode (build). When it goes production we sign it with our private key.
Is there any way to determine at runtime that current package is signed with debug.keystore (is in development mode) or is signed with our private key (is in production mode).
I have tried something like
PackageManager packageManager = getPackageManager();
try {
Signature[] signs = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature signature : signs) {
Log.d(TAG, "sign = " + signature.toCharsString());
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
I don't know what to do next? Is this right way of doing this? How to obtain comparable debug.keystore signature?
I know that exists MD5 Fingerprint keytool -list -keystore ~/.android/debug.keystore but in Signature class there is not "md5 fingerprint"-like method.
I want to do this because of MapView Key, Logging, LicenseChecker and stuff like this.
The signature in PackageInfo does not seem to be well named since tha field does not contain the package signature but the signer X509 certificate chain. Note that (most of the time) this chain seems to be limited to one single self-signed certificate.
According to the Android developer page Signing Your Applications the debug signature certificate is generated with this DN: CN=Android Debug,O=Android,C=US
Therefore it is easy to test if the application has been signed in debug mode:
private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
/* ... */
Signature raw = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));
boolean debug = cert.getSubjectX500Principal().equals(DEBUG_DN);
Based on Jcs' answer, we use this to find out at runtime who built the running package:
private enum BuildSigner {
unknown,
Joe,
Carl,
Linda
}
private BuildSigner whoBuiltThis() {
try {
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(getPackageName(),
PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(signs[0].toByteArray()));
PublicKey key = cert.getPublicKey();
int modulusHash = ((RSAPublicKey)key).getModulus().hashCode();
switch (modulusHash) {
case 123456789:
return BuildSigner.Joe;
case 424242424:
return BuildSigner.Carl;
case -975318462:
return BuildSigner.Linda;
}
} catch (Exception e) {
}
return BuildSigner.unknown;
}
For any involved certificate, you then just have to find the hash once and add it to the list.
The simplest way to "find the hash once" may be to just add a popup toast before the switch statement that displays modulusHash, compile your app, run it, write down the hash, remove the toast code and add the hash to the list.
Alternatively, when I implemented this, I created a little boilerplate app with a single activity and a single TextView with the ID tv in the main layout, put this into the activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int hash = 0;
try{
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(
"com.stackexchange.marvin", PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(signs[0].toByteArray()));
PublicKey key = cert.getPublicKey();
hash = ((RSAPublicKey) key).getModulus().hashCode();
}catch(Exception e){}
TextView tv = ((TextView)findViewById(R.id.tv));
tv.setText("The Stack Exchange app's signature hash is " + hash + ".");
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24);
}
(change com.stackexchange.marvin to your app's name), compiled this mini-app, and sent the APK to all involved developers, asking them to run it on their dev device and let me know the displayed hash.
As all we do I have application which is signed by debug.keystore (by default) when it is in development mode (build). When it goes production we sign it with our private key.
Is there any way to determine at runtime that current package is signed with debug.keystore (is in development mode) or is signed with our private key (is in production mode).
I have tried something like
PackageManager packageManager = getPackageManager();
try {
Signature[] signs = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature signature : signs) {
Log.d(TAG, "sign = " + signature.toCharsString());
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
I don't know what to do next? Is this right way of doing this? How to obtain comparable debug.keystore signature?
I know that exists MD5 Fingerprint keytool -list -keystore ~/.android/debug.keystore but in Signature class there is not "md5 fingerprint"-like method.
I want to do this because of MapView Key, Logging, LicenseChecker and stuff like this.
The signature in PackageInfo does not seem to be well named since tha field does not contain the package signature but the signer X509 certificate chain. Note that (most of the time) this chain seems to be limited to one single self-signed certificate.
According to the Android developer page Signing Your Applications the debug signature certificate is generated with this DN: CN=Android Debug,O=Android,C=US
Therefore it is easy to test if the application has been signed in debug mode:
private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
/* ... */
Signature raw = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));
boolean debug = cert.getSubjectX500Principal().equals(DEBUG_DN);
Based on Jcs' answer, we use this to find out at runtime who built the running package:
private enum BuildSigner {
unknown,
Joe,
Carl,
Linda
}
private BuildSigner whoBuiltThis() {
try {
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(getPackageName(),
PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(signs[0].toByteArray()));
PublicKey key = cert.getPublicKey();
int modulusHash = ((RSAPublicKey)key).getModulus().hashCode();
switch (modulusHash) {
case 123456789:
return BuildSigner.Joe;
case 424242424:
return BuildSigner.Carl;
case -975318462:
return BuildSigner.Linda;
}
} catch (Exception e) {
}
return BuildSigner.unknown;
}
For any involved certificate, you then just have to find the hash once and add it to the list.
The simplest way to "find the hash once" may be to just add a popup toast before the switch statement that displays modulusHash, compile your app, run it, write down the hash, remove the toast code and add the hash to the list.
Alternatively, when I implemented this, I created a little boilerplate app with a single activity and a single TextView with the ID tv in the main layout, put this into the activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int hash = 0;
try{
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(
"com.stackexchange.marvin", PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(signs[0].toByteArray()));
PublicKey key = cert.getPublicKey();
hash = ((RSAPublicKey) key).getModulus().hashCode();
}catch(Exception e){}
TextView tv = ((TextView)findViewById(R.id.tv));
tv.setText("The Stack Exchange app's signature hash is " + hash + ".");
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24);
}
(change com.stackexchange.marvin to your app's name), compiled this mini-app, and sent the APK to all involved developers, asking them to run it on their dev device and let me know the displayed hash.