Android:如何以编程方式获取 SHA1/MD5 指纹?

Posted

技术标签:

【中文标题】Android:如何以编程方式获取 SHA1/MD5 指纹?【英文标题】:Android: How to get SHA1/MD5 fingerprint programmatically? 【发布时间】:2018-03-06 17:40:19 【问题描述】:

我正在尝试实现一种与我的后端服务器通信的方法,并确保我的后端只回答,如果它是我的应用程序正在调用。

所以我的想法是,我只需发送带有 HTTPS POST 请求的 SHA1/MD5 指纹并在后端服务器上验证它。如果指纹匹配,服务器会回答。

所以我的第一个问题是:如何在运行时以编程方式获取这些信息?有没有可能?

第二个问题是:有那么容易吗?还是我真的必须设置 OAuth-Server(或使用 google-api)?...问题是,我认为 OAuth 对我的用例来说有点矫枉过正,我不想处理过期/刷新令牌的东西。

【问题讨论】:

"如何在运行时以编程方式获取这些信息?" -- 你还没有说你想得到什么 SHA1/MD5 值。 “有那么容易吗?” - 可能不是。如果这是一个固定值,其他任何人都可以硬编码相同的值。如果这是某种挑战-响应,其他任何人都可以使用相同的数据实现相同的算法。任何人都可以阅读您的 APK。 SHA1 指纹可能就足够了 - 请参阅我的评论 @GabeSechan 回答 @CommonsWare 我有一个要求,我需要放弃我的 api 以便在其他应用程序中使用。从安全和计费的角度来看,我需要确保经过身份验证的应用程序正在访问 API。我想知道 Facebook 和 Google 如何在应用程序创建过程中使用我们提供给他们的 SHA-1。你能分享一些关于实现这一目标的事情吗? @Calvin:我不知道,抱歉。 【参考方案1】:

我已经对 Zulqumain Jutt 提出的解决方案进行了补充,以便能够以通用形式获得结果,例如:

KeyHelper:MD5 56:ff:2f:1f:55:fa:79:3b:2c:ba:c9:7d:e3:b1:d2:af

public class KeyHelper 

    /**
     * @param key string like: SHA1, SHA256, MD5.
     */
    @SuppressLint("PackageManagerGetSignatures") // test purpose
    static void get(Context context, String key) 
        try 
            final PackageInfo info = context.getPackageManager()
                    .getPackageInfo(BuildConfig.APPLICATION_ID, PackageManager.GET_SIGNATURES);

            for (Signature signature : info.signatures) 
                final MessageDigest md = MessageDigest.getInstance(key);
                md.update(signature.toByteArray());

                final byte[] digest = md.digest();
                final StringBuilder toRet = new StringBuilder();
                for (int i = 0; i < digest.length; i++) 
                    if (i != 0) toRet.append(":");
                    int b = digest[i] & 0xff;
                    String hex = Integer.toHexString(b);
                    if (hex.length() == 1) toRet.append("0");
                    toRet.append(hex);
                

                Log.e(KeyHelper.class.getSimpleName(), key + " " + toRet.toString());
            
         catch (PackageManager.NameNotFoundException e1) 
            Log.e("name not found", e1.toString());
         catch (NoSuchAlgorithmException e) 
            Log.e("no such an algorithm", e.toString());
         catch (Exception e) 
            Log.e("exception", e.toString());
        
    

【讨论】:

【参考方案2】:

您可以生成如下示例中的内容:

private void getKeyHash(String hashStretagy) 
        PackageInfo info;
        try 
            info = getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, PackageManager.GET_SIGNATURES);
            for (Signature signature : info.signatures) 
                MessageDigest md;
                md = MessageDigest.getInstance(hashStretagy);
                md.update(signature.toByteArray());
                String something = new String(Base64.encode(md.digest(), 0));
                Log.e("KeyHash  -->>>>>>>>>>>>" , something);

               // Notification.registerGCM(this);
            
         catch (PackageManager.NameNotFoundException e1) 
            Log.e("name not found" , e1.toString());
         catch (NoSuchAlgorithmException e) 
            Log.e("no such an algorithm" , e.toString());
         catch (Exception e) 
            Log.e("exception" , e.toString());
        
    

像这样使用:

getKeyHash("SHA");
getKeyHash("MD5");

第一个答案:您可以使用上述方法,它安全且独特,我一直在使用它。

第二个答案:您可以使用身份验证密钥,但这完全取决于您,您对什么感到满意

【讨论】:

我还没有尝试过,但我想问题是,正如@GabeSechan 所提到的,任何应用程序都可以要求我提供指纹,这对我的目的非常不利。 @Nico 是的,任何人都可以。这实际上就是散列函数的用处——我可以计算文件的 SHA 散列(或 md5 散列)并检查它是否是你所说的,并合理地确定它是同一个文件。它通过公开来进行验证,但出于同样的原因,它无法进行身份验证。 我怎样才能获得发布密钥库哈希来比较?【参考方案3】:

你想做的事是不可能的。您作为 id 发送到服务器的任何内容都可以被另一个应用程序复制。这就是为什么您的用户的密码不在应用程序中 - 来自外部来源的密码是确保请求有效的唯一方法。这只能证明 user 是有效的,而不是它来自您的应用程序。

【讨论】:

从数学上讲,仅根据外部代理提供给您的信息,无法证明外部代理的身份。其他任何人都可以通过获取您的应用程序的 sha 指纹并使用它来谎称他们的 sha 指纹是什么。 正确的做法是对您的用户进行身份验证,并提供一组 API,只允许他们执行您希望他们能够执行的操作(每个都进行适当的授权检查)。然后,您无需关心他们是否在使用您的应用程序。如果他们真的想全力以赴写自己的客户,谁在乎呢? @GabeSechan 在我的工作流程中类似的情况下,我无法让用户输入密码。我需要应用程序供应商访问我的服务器 API,但是,我的服务器根本没有用户信息。我有点许可应用程序供应商将我的 API 用于他们特定的 1 应用程序。我不愿意在应用程序中硬编码一些东西。我可以从 android 应用程序密钥库中派生一些东西,并且服务器上已经有一些东西来知道请求来自真实的供应商。 @GabeSechan 要获取 SHA-1 指纹并在后端注册它,您需要用于签署应用程序的密钥库文件、密钥库密码和别名/密码的组合。如果攻击者设法窃取您的密钥库,您可能会遇到更大的问题,这就像从服务器窃取私有证书一样。我并不是说这是最终的身份验证协议,但在我看来它非常安全,这就是 GCP 使用它的原因。 @GabeSechan 如果这正如你所说的那样简单,那么为什么没有很多报告谈论使用 GCP 的大型应用程序的 API 配额泄漏?如果窃取 API 配额如此容易,没有人会使用 GCP,如果是这种情况,为什么 Google 会选择这种身份验证方法?【参考方案4】:

Kotlin 版本的 Artur 密钥字符串示例:“SHA1”或“SHA256”或“MD5”。

fun getSig(context: Context, key: String) 
            try 
                val info = context.packageManager.getPackageInfo(
                    BuildConfig.APPLICATION_ID,
                    PackageManager.GET_SIGNATURES
                )
                for (signature in info.signatures) 
                    val md = MessageDigest.getInstance(key)
                    md.update(signature.toByteArray())
                    val digest = md.digest()
                    val toRet = StringBuilder()
                    for (i in digest.indices) 
                        if (i != 0) toRet.append(":")
                        val b = digest[i].toInt() and 0xff
                        val hex = Integer.toHexString(b)
                        if (hex.length == 1) toRet.append("0")
                        toRet.append(hex)
                    
                    val s = toRet.toString()
                    Log.e("sig", s)
                    
                
             catch (e1: PackageManager.NameNotFoundException) 
                Log.e("name not found", e1.toString())
             catch (e: NoSuchAlgorithmException) 
                Log.e("no such an algorithm", e.toString())
             catch (e: Exception) 
                Log.e("exception", e.toString())
            

【讨论】:

以上是关于Android:如何以编程方式获取 SHA1/MD5 指纹?的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式获取 Android 手机型号,如何在 android 中以编程方式获取设备名称和型号?

使用CMD命令查看Android应用签名证书的SHA1、MD5、SHA256值

如何以编程方式获取 android 设备屏幕截图? [复制]

如何以编程方式获取android手机的IP地址......? [复制]

如何以编程方式在 Android 设备中获取设备 (AOSP) 内部版本号?

如何以编程方式在 Android 中获取图像按钮的宽度和高度