【问题标题】:Android: How to get SHA1/MD5 fingerprint programmatically?Android:如何以编程方式获取 SHA1/MD5 指纹?
【发布时间】: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:我不知道,抱歉。

标签: android oauth backend verification


【解决方案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())
                }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-03-30
      • 2021-07-30
      • 1970-01-01
      • 2015-12-17
      • 2013-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多