rea-reverse

Get the SHA-1 and SHA-256 Fingerprint from an Android Keystore

Published by rea-reverseUpdated 8 min read
On this page

Short answer: for a keystore, run keytool -list -v -keystore your.jks -alias your-alias and copy the SHA1: and SHA256: lines. For a finished APK, run apksigner verify --print-certs app.apk. In an Android project, ./gradlew signingReport prints both for every build variant. All three gave me the same digests on the same key. The traps are elsewhere: a PKCS#12 keystore shows no fingerprint at all without its password, keytool -printcert -jarfile says “Not a signed jar file” on a perfectly signed modern APK, apksigner refuses an AAB, and the Facebook key hash one-liner happily hashes an error message. Each of those is shown below with the output I got.

If you only want the numbers, drop the keystore, APK or AAB on the signing certificate fingerprint viewer. It runs in your browser, nothing is uploaded, and it prints the colon hex form Firebase wants, plain hex and the Base64 form Facebook wants.

Who this is for: Android developers wiring up Firebase, Google Sign-In, the Maps SDK, API key restrictions, Facebook Login or Android App Links, who need to know which fingerprint to paste and where it comes from.

Find the Certificate That Signed the AppSilent walkthrough with English captions.

What I tested on

Everything below was run on 2026-10-20 on macOS 27.0.1 with:

  • Temurin JDK 17.0.18 (keytool from that JDK)
  • Android build-tools 36.1.0 (apksigner --version prints 0.9)
  • Gradle 9.6.1 with Android Gradle Plugin 9.4.1
  • OpenSSL 4.0.3

The keys are throwaway keys I generated for this article: an RSA 2048 “upload” key stored once as JKS and once as PKCS#12, and a debug keystore created the way the Android docs describe (alias androiddebugkey, password android). The app is an empty project (com.reareverse.certdemo, minSdk 24) signed with those keys. I did not read the real ~/.android/debug.keystore on the machine; for the Gradle runs I pointed ANDROID_USER_HOME at a scratch folder and configured the debug signing config explicitly.

First pitfall, before any fingerprint: keytool -version does not exist in JDK 17. It prints Illegal option: -version plus the help text (in my case first in Chinese, 非法选项: -version, because the system locale is Chinese). Use java -version to see which JDK’s keytool you are running. The locale problem comes back later: keytool and Gradle localize their output, so add -J-Duser.language=en to keytool if you want to grep for SHA1:.

Method 1: keytool on the keystore

keytool -J-Duser.language=en -list -v -keystore upload.jks -alias upload -storepass sample123

Output (trimmed):

Alias name: upload
Entry type: PrivateKeyEntry
Owner: CN=REA Sample Upload, O=rea-reverse.com, C=US
Serial number: dc473b319601792c
Certificate fingerprints:
	 SHA1: 0F:9C:63:67:43:7A:67:69:81:F2:D5:6F:5C:A8:64:F4:90:47:AA:36
	 SHA256: 11:82:12:DD:1B:8D:76:BD:E7:4F:F4:81:80:47:ED:4F:43:2E:BF:21:D8:A0:A8:9D:18:84:2B:69:C1:FB:40:10
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key

Two details matter.

-v is not optional. Without it, JDK 17’s keytool -list prints a single line per entry, Certificate fingerprint (SHA-256): 11:82:12:…. There is no SHA-1 in the short listing, which is the value Google Sign-In’s Android OAuth client asks for.

The keystore format decides if the password is needed. A JKS keystore stores certificates in clear. Run the same command without -storepass and press Enter at the prompt: keytool prints a “WARNING WARNING WARNING … integrity … has NOT been verified” box and then the same SHA-1 and SHA-256. A PKCS#12 keystore encrypts the certificates. Without the password, I got this for upload.p12:

Keystore type: PKCS12
Alias name: upload
Entry type: PrivateKeyEntry
Certificate chain length: 0

No fingerprint, and no error either. A wrong password at least fails loudly: keytool error: java.io.IOException: keystore password was incorrect. And the file name does not tell you which format you have: keytool -genkeypair -keystore defaulttype.jks without -storetype gave me Keystore type: PKCS12, because PKCS#12 is keytool’s default since JDK 9. The fingerprint viewer behaves the same way, because the data is genuinely encrypted: for the JKS it lists the certificates without a password (integrity “unchecked”), for the PKCS#12 it lists the alias and reports the certificates as locked until you type the password.

Method 2: ./gradlew signingReport

If the keystore is wired into the project, Gradle prints every variant’s signing certificate. My first attempt failed before reaching the task:

Failed to apply plugin 'com.android.internal.version-check'.
   > Minimum supported Gradle version is 9.6.0. Current version is 9.4.1.

That was the Homebrew gradle (9.4.1) against AGP 9.4.1. In a real project you run the wrapper, ./gradlew, which pins a matching Gradle version. With Gradle 9.6.1 the task ran (BUILD SUCCESSFUL in 447ms):

> Task :app:signingReport
Variant: debug
Config: debug
Store: …/app/debug.keystore
Alias: androiddebugkey
MD5: 4D:4D:46:26:76:83:1F:42:54:3F:50:73:C2:AD:FD:97
SHA1: 06:AC:D5:48:20:5B:9B:28:55:82:C8:D8:47:D5:D1:41:A3:68:5B:BE
SHA-256: 84:EA:8F:71:7D:45:10:4E:05:F7:68:5D:AA:B4:8C:B4:DF:A6:45:51:3A:D8:5E:E3:85:77:5B:05:D1:BC:D7:A7
Valid until: 2054年3月6日星期五
----------
Variant: release
Config: release
Store: …/app/upload.jks
Alias: upload
SHA1: 0F:9C:63:67:43:7A:67:69:81:F2:D5:6F:5C:A8:64:F4:90:47:AA:36
SHA-256: 11:82:12:DD:1B:8D:76:BD:E7:4F:F4:81:80:47:ED:4F:43:2E:BF:21:D8:A0:A8:9D:18:84:2B:69:C1:FB:40:10

The release digests are identical to keytool’s. The date is localized again, which is harmless but shows the report is meant for eyes, not scripts. signingReport has one more limit: it reads the signing configs in your build files. It cannot tell you what Google Play signs with (see “Which key” below), and it says nothing about a build someone else produced.

Building the release APK offline also hit an unrelated wall: lintVitalAnalyzeRelease needed com.android.tools.lint:lint-gradle:32.4.1, which was not in my Gradle cache (“No cached version … available for offline mode”). lint { checkReleaseBuilds = false } got me a signed APK for this test; in a normal online build, leave lint on.

Method 3: apksigner on the built APK

The keystore tells you what you intend to sign with. The APK tells you what you actually shipped. apksigner comes with the Android SDK build-tools:

~/Library/Android/sdk/build-tools/36.1.0/apksigner verify --print-certs -v app-release.apk
Verifies
Verified using v1 scheme (JAR signing): false
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): false
Number of signers: 1
Signer #1 certificate DN: CN=REA Sample Upload, O=rea-reverse.com, C=US
Signer #1 certificate SHA-256 digest: 118212dd1b8d76bde74ff4818047ed4f432ebf21d8a0a89d18842b69c1fb4010
Signer #1 certificate SHA-1 digest: 0f9c6367437a676981f2d56f5ca864f49047aa36
Signer #1 certificate MD5 digest: cefddf40e5ac53c760ad45660de935f3

Same SHA-1 and SHA-256, but lowercase and without colons. Firebase and assetlinks.json expect the 0F:9C:… form, so you have to reformat it. The fingerprint viewer shows both forms with a copy button each.

Notice also v1 scheme: false. With minSdk 24, AGP 9.4.1 signed my APK with v2 only. That is why the older keytool route fails on it:

keytool -printcert -jarfile app-release.apk
Not a signed jar file

keytool -printcert -jarfile only understands JAR (v1) signatures. The APK is signed; keytool just cannot see the v2 block. On the AAB the situation is reversed. bundleRelease produced a 643,520-byte app-release.aab, which Gradle signs with JAR signing, so keytool reads it fine and prints the same SHA1: 0F:9C:63:…. apksigner on the same AAB throws:

Exception in thread "main" com.android.apksig.apk.ApkFormatException: Missing AndroidManifest.xml

So: apksigner for APKs, keytool -printcert -jarfile for AABs, or the fingerprint viewer for both. On these files it reported v2 for the APK and v1 for the AAB, with the same digests.

Which key’s fingerprint goes where

Three keys can be involved, and pasting the wrong one is the most common reason Google Sign-In fails with a developer error while everything “looks right”.

Build you test Key that signed it Where to get its fingerprint
Debug build from Android Studio Debug keystore of that machine signingReport, debug variant
Release APK you sideload or upload Your upload / release key keytool -list -v on the keystore, or apksigner on the APK
App installed from Google Play with Play App Signing Google-held app signing key Play Console, App signing page; it is not in any file you have

Every developer machine has its own debug keystore, so a teammate’s debug SHA-1 is different from yours. For a Play install, the upload key fingerprint is the wrong one; the app signing key on the Play Console page is the one that signs what users install. I could not demonstrate that step here, since it needs a Play Console app; the point is that no local command can print it.

Firebase and Google Sign-In

Firebase project settings accept SHA-1 and SHA-256 certificate fingerprints in colon hex; you can add several (debug, upload, Play app signing). The Android OAuth client used by Google Sign-In is keyed on the package name plus a SHA-1. I did not register these in a live Firebase project for this article, so check the Firebase docs for the current console wording.

App Links verification wants the SHA-256, in uppercase colon hex, in https://your-domain/.well-known/assetlinks.json:

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.reareverse.certdemo",
    "sha256_cert_fingerprints": [
      "11:82:12:DD:1B:8D:76:BD:E7:4F:F4:81:80:47:ED:4F:43:2E:BF:21:D8:A0:A8:9D:18:84:2B:69:C1:FB:40:10"
    ]
  }
}]

With Play App Signing, put the app signing key’s SHA-256 there, and add the upload key’s as a second entry if you also install release builds signed with it. See Verify Android App Links.

Facebook key hash: the one-liner that lies

Facebook Login asks for a “key hash”, the Base64 of the SHA-1. The usual command is:

keytool -exportcert -alias upload -keystore upload.jks -storepass sample123 | openssl sha1 -binary | openssl base64
D5xjZ0N6Z2mB8tVvXKhk9JBHqjY=

That matches the Base64 column of the fingerprint viewer, and 0F:9C:63:… decoded from hex. Now the same command with an alias that does not exist in that keystore (I used androiddebugkey by mistake):

xSLzBBuLJunOQPB89rtzM54FXx4=

A plausible 28-character key hash, and wrong. keytool printed keytool error: java.lang.Exception: Alias <androiddebugkey> does not exist to stdout, and openssl sha1 hashed that sentence. The pipeline exits without complaint. Run the keytool -exportcert half alone first, or check the result against the SHA-1: Base64-decode the key hash and it should be the same 20 bytes.

The browser route, and what I measured

The signing certificate fingerprint viewer reads JKS, JCEKS and PKCS#12 keystores, APK and AAB signer blocks (v1, v2, v3, v3.1) and certificate files. I ran the page’s parsing code under Node 22 on the files from this article:

File Size Result Parse time
upload.jks, no password 2,169 B 1 key entry, SHA-1 0F:9C:63:…, integrity unchecked 2.8 ms
upload.p12, no password 2,664 B alias listed, certificates locked 28 ms
upload.p12, password 2,664 B SHA-1 E3:59:F5:…, PBES2 / AES-256-CBC, HMAC-SHA256 MAC ok 26 ms
app-release.apk 642,546 B v2 signer, same digests as apksigner 2.7 ms
app-release.aab 643,520 B v1 signer, same digests as keytool 11.9 ms

The PKCS#12 time is the 10,000-iteration PBKDF2 and MAC, not parsing. In the browser this runs in a Web Worker with no network access; the password is only used to check the MAC and decrypt the certificate bags, and private keys are never decrypted.

While testing, I also re-encoded the PKCS#12 file as BER (indefinite lengths, the authenticated-safe split into a chunked OCTET STRING, the way some Windows and NSS exports look). openssl pkcs12 and keytool both still read it, and so did the viewer, with the MAC verified. The tool page used to list BER PKCS#12 as unsupported; that limitation is gone and the page is updated.

One macOS-specific note from the same session: importing the JDK 17 default PKCS#12 into a temporary keychain with security import failed with MAC verification failed during PKCS12 import (wrong password?) although the password was right. The legacy format (keytool -J-Dkeystore.pkcs12.legacy -importkeystore …) imported fine. If Keychain Access rejects a keystore your tools accept, try that before doubting the password.

Where REA fits

REA, the open-source project by morluto, handles the code side of an APK: in 6.7.0 its Android tools report manifest declarations, classes and decompiled methods through headless JADX (Android guide). I checked the published [email protected] package: it has no signing certificate reader and no reference to apksigner or keytool. So read the fingerprint with the tools above and use REA when the question becomes “what does this build do”. Only analyze apps you build or are authorized to test; see REA’s DISCLAIMER.md.

Next steps