将Android apk SHA256与SafetyNet apkCertificateDigestShA256匹配

5uzkadbs  于 2023-05-21  发布在  Android
关注(0)|答案(5)|浏览(360)

我正在使用SafetyNet来验证Android应用程序的完整性。
这是目前的流程。
1.我在服务器中生成一个nonce值,并将其发送到SafetyNet服务以获取响应。
1.我得到了服务器的响应。现在我想在服务器上验证结果。
我得到一个base64字符串。我解码它并得到如下响应。

{
    "evaluationType": "BASIC",
    "ctsProfileMatch": false,
    "apkPackageName": "com.test.safetynetproject",
    "apkDigestSha256": "CbU9JzwRzQneYqnEXewB56ZzPm1DgQ4LGUK0eGlWmyM=",
    "nonce": "U2FnYXI=",
    "apkCertificateDigestSha256": [
        "AJRBzWCfJIY7QD2cp4sv9t0cCGMRGdxuID9VdPLV1H4="
    ],
    "timestampMs": 1624099377557,
    "basicIntegrity": false
}

现在我想验证apkCertificateDigestSha 256。从我的系统使用cmd创建的sha 256是-

C:\Program Files\Java\jdk-11.0.11\bin>keytool -list -v -alias androiddebugkey -keystore C:\Users\.android\debug.keystore
Enter keystore password:
Alias name: androiddebugkey
Creation date: May 25, 2021
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: C=US, O=Android, CN=Android Debug
Issuer: C=US, O=Android, CN=Android Debug
Serial number: 1
Valid from: Tue May 25 11:48:00 IST 2021 until: Thu May 18 11:48:00 IST 2051
Certificate fingerprints:
         SHA1: 43:16:E2:63:DB:2A:53:7C:7D:BB:E9:80:7B:05:1C:74:7C:84:66:A2
         SHA256: 00:94:41:CD:60:9F:24:86:3B:40:3D:9C:A7:8B:2F:F6:DD:1C:08:63:11:19:DC:6E:20:3F:55:74:F2:D5:D4:7E
Signature algorithm name: SHA1withRSA (weak)
Subject Public Key Algorithm: 2048-bit RSA key
Version: 1

Warning:
The certificate uses the SHA1withRSA signature algorithm which is considered a security risk. This algorithm will be disabled in a future update.

SHA 256战斗机

00:94:41:CD:60:9F:24:86:3B:40:3D:9C:A7:8B:2F:F6:DD:1C:08:63:11:19:DC:6E:20:3F:55:74:F2:D5:D4:7E

问题-我想验证apkCertificateDigestSha 256是否与应用证书相同。但找不到任何方法去做这件事。
尝试-我尝试对AJRBzWCfJIY 7 QD 2cp 4sv 9 t0 cCGMRGdxuID 9VdPLV 1H 4 =进行base64解码,得到了一个与cmd中创建的sha 256不匹配的随机字节数组。
密码-

val decode =
    String(
        Base64.decode(
            responseJws!!.apkCertificateDigestSha256!![0],
            Base64.DEFAULT
        ),
        StandardCharsets.UTF_8
    )

输出-

���A�`�$�;@=���/��c�n ?Ut���~

这与43:16:E2:63:DB:2A:53:7 C:7 D:BB:E9:80:7 B:05:1C:74:7 C:84:66:A2不匹配。
最新消息-
找到了一些裁判,但真的不知道如何实现这一点。Ref1

如何匹配?

aij0ehis

aij0ehis1#

我使用SafetyNet API访问设备的运行时环境。我一直在服务器上签署应用程序的证书,以验证其sha 256对我们在SafetyNet响应中得到的内容。下面是你可以参考的步骤,如果也适用于你。
1.获取签署X509证书的SHA 256指纹
MessageDigest md = MessageDigest.getInstance(“SHA-256”); int n = nums.getEncoded(); int n. add(); byte[]sha256= md.digest();
1.将sha 256编码为base64字符串
Stringchecksum= Base64.getEncoder().encodeToString(sha256
1.将checksum与SafetyNet响应的apkCertificateDigestSha 256匹配
根据要求,下面是我用于获取证书的SHA 256的方法。此处cert为应用签名证书

public static byte[] getSHA256Fingerprint(X509Certificate 
    cert) throws NoSuchAlgorithmException, 
    CertificateEncodingException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] der = cert.getEncoded();
        md.update(der);
        return md.digest();
     }

使用上述方法计算hashappSigningKeystoreCertHash,并转换为base64字符串与SafetyNet响应值进行比较

byte[] shaFingerprint = 
    CertificateUtility.getSHA256Fingerprint(certificate);
                String appSigningKeystoreCertHash = 
    java.util.Base64.getEncoder().encodeToString(shaFingerprint);
    //Base64 encoded SHA256 certificate fingerprint;
ercv8c1e

ercv8c1e2#

我想这个能帮到你
1.在GG示例中查找AttestationStatement文件。并添加此功能:

public  String bytesToHex(byte[] bytes) {
    StringBuffer result = new StringBuffer();
    for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
    return result.toString();
}

2.找到getApkCertificateDigestSha 256函数并像这样编辑:

public byte[][] getApkCertificateDigestSha256() {
    byte[][] certs = new byte[apkCertificateDigestSha256.length][];
    for (int i = 0; i < apkCertificateDigestSha256.length; i++) {
        certs[i] = Base64.decodeBase64(apkCertificateDigestSha256[i]);
        System.out.println(bytesToHex(certs[i]));
    }
    return certs;
}

3.在OnlineVerrify中找到process()函数,并添加如下内容:

if (stmt.getApkPackageName() != null && stmt.getApkDigestSha256() != null) {
        System.out.println("APK package name: " + stmt.getApkPackageName());
        System.out.println("APK digest SHA256: " + Arrays.toString(stmt.getApkDigestSha256()));
        System.out.println("APK certificate digest SHA256: " +
                Arrays.deepToString(stmt.getApkCertificateDigestSha256()));
    }

1.现在,运行,您将看到SHA-256并进行比较。
不是:没有“:”字符bettwen sha-256生成的原因,我是懒惰的。^^.

wmvff8tz

wmvff8tz3#

检查这里的代码作为如何进行验证的参考:https://github.com/Gralls/SafetyNetSample/blob/master/Server/src/main/java/pl/patryk/springer/safetynet/Main.kt
我只是在搜索同样的东西时找到了它,所有的功劳都归于拥有回购的人。

1hdlvixo

1hdlvixo4#

public class Starter {

static String keystore_location = "C:\\Users\\<your_user>\\.android\\debug.keystore";

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}

public static void main(String[] args) {
    
    try {
        File file = new File(keystore_location);
        InputStream is = new FileInputStream(file);
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        String password = "android"; // This is the default password
        keystore.load(is, password.toCharArray());

        Enumeration<String> enumeration = keystore.aliases();
        while(enumeration.hasMoreElements()) {
            String alias = enumeration.nextElement();
            System.out.println("alias name: " + alias);
            Certificate certificate = keystore.getCertificate(alias);
            System.out.println(certificate.toString());
            System.out.println(certificate.getEncoded());
            
            final MessageDigest md = MessageDigest.getInstance("SHA-1");
            final MessageDigest md2 = MessageDigest.getInstance("SHA-256");
            final byte[] der = certificate.getEncoded();
            md.update(der);
            md2.update(der);
            final byte[] digest = md.digest();
            final byte[] digest2 = md2.digest();
            System.out.println(bytesToHex(digest));
            System.out.println(bytesToHex(digest2));
            
            byte[] encoded = Base64.getEncoder().encode(digest2);
            System.out.println(encoded);
            
            String checksum = Base64.getEncoder().encodeToString(digest2);
            System.out.println(checksum); // This should match apkCertificateDigestSha256
        }
    } catch (java.security.cert.CertificateException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
sqougxex

sqougxex5#

现在Google贬低了SafetyAPI,并引入了PlayIntegrity API用于认证。PlayIntegrity服务提供如下响应。

{
"tokenPayloadExternal": {
    "accountDetails": {
        "appLicensingVerdict": "LICENSED"
    },
    "appIntegrity": {
        "appRecognitionVerdict": "PLAY_RECOGNIZED",
        "certificateSha256Digest": ["pnpa8e8eCArtvmaf49bJE1f5iG5-XLSU6w1U9ZvI96g"],
        "packageName": "com.test.android.safetynetsample",
        "versionCode": "4"
    },
    "deviceIntegrity": {
        "deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"]
    },
    "requestDetails": {
        "nonce": "SafetyNetSample1654058651834",
        "requestPackageName": "com.test.android.safetynetsample",
        "timestampMillis": "1654058657132"
    }
}}

响应仅包含应用的 certificateSha256Digest(应用证书的sha256摘要),而不是包含 apkDigestSha256apkCertificateDigestSha256
我们如何在服务器上验证收到的 certificateSha256Digest
如果应用程序部署在Google PlayStore中,请按照以下步骤操作
从Google Play控制台下载 * 应用程序签名密钥证书 *(如果您使用的是托管签名密钥),否则下载 * 上传密钥证书 *,然后查找证书的校验和。

public static Certificate getCertificate(String certificatePath)throws Exception {
  CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
  FileInputStream in = new FileInputStream(certificatePath);
  Certificate certificate = certificateFactory.generateCertificate(in);
  in.close();
 return certificate;
}

生成证书的校验和

Certificate x509Cert = getCertificate("<Path of file>/deployment_cert.der");
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] x509Der = x509Cert.getEncoded();
md.update(x509Der);
byte[] sha256 = md.digest();
String checksum = Base64.getEncoder().encodeToString(sha256);

然后将校验和与接收到的 certificateSha256Digest 进行比较

String digest = jwsResponse.tokenPayloadExternal.appIntegrity.certificateSha256Digest;
if(checksum.contains(digest)){
  //
}

相关问题