├── LICENSE ├── README.md ├── smshash.py └── xxd_w.exe /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Funambol 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # android-sms-hash-generator 2 | Small and effective python script to compute the android app's hash to properly configure the SMS Signup with autoverification code. 3 | 4 | # Download 5 | Download the latest version from [Releases](https://github.com/funambol/android-sms-hash-generator/releases) page. 6 | 7 | # Requirements 8 | - JDK 9 | - Python3 10 | 11 | # Usage 12 | smshash.py needs four parameters: 13 | - keystore: the absolute or relative path of your Android keystore 14 | - alias: the alias of the keystore 15 | - keypass: the passphrase of the keystore 16 | - appid: your app's package name 17 | 18 | Example: 19 | ```python 20 | python3 smshash.py path/keystore MyAndroidKey xxxxxxxxxxxx com.example.myapp 21 | ``` 22 | 23 | For help: 24 | ```python 25 | python3 smshash.py -h 26 | ``` 27 | 28 | >**Please note:**
29 | >On Windows, smshash.py and xxd_w.exe must be placed in the same folder. 30 | >
xxd_w.exe is not used on other platforms. 31 | 32 | # Why 33 | Google Play services uses the hash string to determine which verification messages to send to your Android app. The hash string is made of your app's package name and your app's public key certificate. 34 | According to Google [documentation](https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string) it can be generated with this command: 35 | ```bash 36 | keytool -exportcert -alias MyAndroidKey -keystore MyProductionKeys.keystore | xxd -p | tr -d "[:space:]" | echo -n com.example.myapp `cat` | sha256sum | tr -d "[:space:]-" | xxd -r -p | base64 | cut -c1-11 37 | ``` 38 | But that command has some issues: 39 | - It's not multi platform: it works fine on Linux, it could work on Mac with a small change, it does not work at all on Windows. 40 | - It does not emit an error if the keytool command fails and it generates the hash of the error message instead. 41 | 42 | This python script does not have those issues, it is multi platform and if the keytool command fails (for instance because the keystore passphrase is wrong), the script stops and emit an error message. -------------------------------------------------------------------------------- /smshash.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import os 3 | import sys 4 | import argparse 5 | import subprocess 6 | from subprocess import Popen, PIPE 7 | import base64 8 | from shutil import which 9 | try: 10 | import hashlib 11 | except ImportError: 12 | sys.exit("please install 'hashlib' module: pip install hashlib") 13 | 14 | #the parser 15 | cmd_parser = argparse.ArgumentParser(description="Computing app's hash string for Android SMS handling") 16 | cmd_parser.add_argument('keystore', type=str, help='Keystore file') 17 | cmd_parser.add_argument('alias', type=str, help='Keystore alias') 18 | cmd_parser.add_argument('keypass', type=str, help='Key password') 19 | cmd_parser.add_argument('appid', type=str, help='Package name of the Android app') 20 | args = cmd_parser.parse_args() 21 | 22 | __encoding_name__ = "iso-8859-1" # Latin 1 23 | 24 | def isWindows(): 25 | return platform.system() == "Windows" 26 | 27 | def cmdExist(program): 28 | return which(program) is not None 29 | 30 | def exitWithError(error): 31 | print(error, file=sys.stderr) 32 | sys.exit(1) 33 | 34 | def call(cmd): 35 | cmd = subprocess.Popen(cmd, shell=True, universal_newlines=True, stdout=PIPE, stderr=PIPE, encoding=__encoding_name__) 36 | cmdResult = cmd.communicate() 37 | return (cmd.returncode, cmdResult[0], cmdResult[1]) 38 | 39 | def getKeytoolCommand(withxxd = False): 40 | keytoolName = "keytool" 41 | if not cmdExist(keytoolName): 42 | exitWithError("Error: keytool command not found. Be sure the JDK is installed and available in the PATH") 43 | 44 | if withxxd: 45 | return keytoolName + " " + "-alias " + args.alias + " -exportcert -keystore " + args.keystore + " -storepass " + args.keypass + " | " + getxxdName() + " -p" 46 | else: 47 | return keytoolName + " " + "-alias " + args.alias + " -exportcert -keystore " + args.keystore + " -storepass " + args.keypass 48 | 49 | def getxxdName(): 50 | xxdName = "xxd" 51 | if isWindows(): 52 | xxdName = "xxd_w" 53 | if not cmdExist(xxdName): 54 | exitWithError("Error: " + xxdName + " not found. If you are on Windows, the program xxd_w.exe must be placed in the current folder") 55 | return xxdName 56 | 57 | def getSignature(): 58 | keytoolCommand = getKeytoolCommand() 59 | returncode, out, err = call(keytoolCommand) 60 | if returncode != 0: 61 | print(out) 62 | print(err) 63 | exitWithError("keytool command failed. Please check the alias and the password are correct") 64 | return out 65 | 66 | def getHexSignature(): 67 | keytoolWithxxd = getKeytoolCommand(True) 68 | returncode, out, err = call(keytoolWithxxd) 69 | if returncode != 0: 70 | print(out) 71 | print(err) 72 | exitWithError("keytool | xxd command failed") 73 | return out 74 | 75 | def removeWhitespaces(value): 76 | return "".join(value.split()) 77 | 78 | def appendApplicationId(value): 79 | return args.appid + " " + value 80 | 81 | def computeSha256(value): 82 | m = hashlib.sha256() 83 | m.update(value.encode(__encoding_name__)) 84 | return m.digest() 85 | 86 | def formatSignature(signature): 87 | signatureNoSpaces = removeWhitespaces(signature) 88 | return appendApplicationId(signatureNoSpaces) 89 | 90 | # Call getSignature() to check if the alias and password provided are correct: if not correct the program exits with error 91 | signature = getSignature() 92 | hexSignature = getHexSignature() 93 | 94 | formattedSignature = formatSignature(hexSignature) 95 | sha256 = computeSha256(formattedSignature) 96 | base64 = base64.b64encode(sha256) 97 | 98 | # The hash to use for the SMS is the first 11 chars 99 | print(base64.decode(__encoding_name__)[0:11]) 100 | -------------------------------------------------------------------------------- /xxd_w.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/funambol/android-sms-hash-generator/9b88cdd570ea4a041aa6e4b2aa6134c4c3b6b5b4/xxd_w.exe --------------------------------------------------------------------------------