我正在使用 SafetyNet API 来检查设备是否已植根 并使用以下有用的代码,但这使用Android验证API 验证JWT签名:
https://github.com/scottyab/safetynethelper
我想在客户端验证只是为了减少所有其他Web服务的开销,而且每天只有10k的请求限制。
因此在解码JWS后我得到以下信息
示例JWS消息响应
xxxx.yyy.zzzz
标题数据
{"alg":"RS256","x5c":["<certificate1 string>","<certificate2 string>"]}
有效载荷数据
{"nonce":"<nounce>",
"timestampMs":1472794339527,
"apkPackageName":"<apkPackageName>",
"apkDigestSha256":"<sha digest string>",
"ctsProfileMatch":true,
"extension":"<extension string>",
"apkCertificateDigestSha256":["<apkCertificateDigestSha256 string>"],"basicIntegrity":true}
签名 在这一部分,如果执行Base64解码,它变得不可读,所以下面是在JWS最后一个元素中收到的签名字符串
Gw09rv1aBbtd4Er7F5ww_3TT1mPRD5YouMkPkwnRXJq8XW_cxlO4428DHTJdD8Tbep-Iv3nrVRWt2t4pH1uSr2kJ9budQJuXqzOUhN93r2Hfk-UAKUYQYhp89_wOWjSCG4ySVHD4jc9S1HrZlngaUosocOmhN4SzLZN5o8BXyBdXkjhWwgArd4bcLhCWJzmxz5iZfkhDiAyeNRq09CeqjRx_plqAy8eR_OaI_2idZBNIGfd2KmLK_CKaeVjDxuC4BzJsIlVRiuLrvP362Wwhz4r1bHh8flmHr88nK99apP2jkQD2l7lPv8y5F3FN3DKhJ15CzHR6ZbiTOw1fUteifg
现在按照谷歌
“验证兼容性检查响应:提取SSL证书 来自JWS消息的链。验证SSL证书链并使用 SSL主机名匹配以验证是否已颁发叶证书 到主机名attest.android.com。使用证书验证 JWS消息的签名。“
我确实有cert字符串和签名我该如何验证SSL证书,这是第二个证书上的字符串和主机名匹配 如何验证签名。
我需要关于此的指针,并且代码剪切将非常有用。
答案 0 :(得分:2)
您希望在设备上验证JWT签名的方式并不安全。想想下一个案例:
设备已植根,具有root权限的恶意软件应用程序 将您的请求发送到Google的SafetyNet并返回自签名 响应。
当您使用自己的服务器服务验证响应时,您会得到Google未提供的回复。如果您在设备上本地执行此操作 - 相同的恶意软件应用程序可能会抓住您验证JWT签名并使用true
进行回复的请求。
无论如何,你可以在本地做到这一点:
注意:验证响应消息的API方法具有每个项目每天10,000个请求的固定速率限制。您应该仅在初始开发阶段使用verify()方法进行测试。 您不应该在生产方案中调用该方法。
[...]
要使用Android设备验证API:
创建包含JWS全部内容的JSON消息 消息采用以下格式:
{ "signedAttestation": "<output of> getJwsResult()>" }
使用HTTP POST请求发送带有的消息 以下网址的内容类型
"application/json"
: https://www.googleapis.com/androidcheck/v1/attestations/verify?key=<your API key>
该服务验证消息的完整性,如果 消息有效,它返回带有以下内容的JSON消息 内容:
{ “isValidSignature”: true }
实际上(来自SafetyNet Helper的代码):
/**
*
* Validates the result with Android Device Verification API.
*
* Note: This only validates that the provided JWS (JSON Web Signature) message was received from the actual SafetyNet service.
* It does *not* verify that the payload data matches your original compatibility check request.
* POST to https://www.googleapis.com/androidcheck/v1/attestations/verify?key=<your API key>
*
* More info see {link https://developer.android.com/google/play/safetynet/start.html#verify-compat-check}
*
* Created by scottab on 27/05/2015.
*/
public class AndroidDeviceVerifier {
private static final String TAG = AndroidDeviceVerifier.class.getSimpleName();
//used to verifiy the safety net response - 10,000 requests/day free
private static final String GOOGLE_VERIFICATION_URL = "https://www.googleapis.com/androidcheck/v1/attestations/verify?key=";
private final String apiKey;
private final String signatureToVerify;
private AndroidDeviceVerifierCallback callback;
public interface AndroidDeviceVerifierCallback{
void error(String s);
void success(boolean isValidSignature);
}
public AndroidDeviceVerifier(@NonNull String apiKey, @NonNull String signatureToVerify) {
this.apiKey = apiKey;
this.signatureToVerify = signatureToVerify;
}
public void verify(AndroidDeviceVerifierCallback androidDeviceVerifierCallback){
callback = androidDeviceVerifierCallback;
AndroidDeviceVerifierTask task = new AndroidDeviceVerifierTask();
task.execute();
}
/**
* Provide the trust managers for the URL connection. By Default this uses the system defaults plus the GoogleApisTrustManager (SSL pinning)
* @return array of TrustManager including system defaults plus the GoogleApisTrustManager (SSL pinning)
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
*/
protected TrustManager[] getTrustManagers() throws KeyStoreException, NoSuchAlgorithmException {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
//init with the default system trustmanagers
trustManagerFactory.init((KeyStore)null);
TrustManager[] defaultTrustManagers = trustManagerFactory.getTrustManagers();
TrustManager[] trustManagers = Arrays.copyOf(defaultTrustManagers, defaultTrustManagers.length + 1);
//add our Google APIs pinning TrustManager for extra security
trustManagers[defaultTrustManagers.length] = new GoogleApisTrustManager();
return trustManagers;
}
private class AndroidDeviceVerifierTask extends AsyncTask<Void, Void, Boolean>{
private Exception error;
@Override
protected Boolean doInBackground(Void... params) {
//Log.d(TAG, "signatureToVerify:" + signatureToVerify);
try {
URL verifyApiUrl = new URL(GOOGLE_VERIFICATION_URL + apiKey);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, getTrustManagers(), null);
HttpsURLConnection urlConnection = (HttpsURLConnection) verifyApiUrl.openConnection();
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/json");
//build post body { "signedAttestation": "<output of getJwsResult()>" }
String requestJsonBody = "{ \"signedAttestation\": \""+signatureToVerify+"\"}";
byte[] outputInBytes = requestJsonBody.getBytes("UTF-8");
OutputStream os = urlConnection.getOutputStream();
os.write(outputInBytes);
os.close();
urlConnection.connect();
//resp ={ “isValidSignature”: true }
InputStream is = urlConnection.getInputStream();
StringBuilder sb = new StringBuilder();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = rd.readLine()) != null) {
sb.append(line);
}
String response = sb.toString();
JSONObject responseRoot = new JSONObject(response);
if(responseRoot.has("isValidSignature")){
return responseRoot.getBoolean("isValidSignature");
}
}catch (Exception e){
//something went wrong requesting validation of the JWS Message
error = e;
Log.e(TAG, "problem validating JWS Message :" + e.getMessage(), e);
return false;
}
return false;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
if(error!=null){
callback.error(error.getMessage());
}else {
callback.success(aBoolean);
}
}
}
}
答案 1 :(得分:0)
使用测试认证验证服务进行生产 为了简化SafetyNet Attestation API的开发和测试,Google提供了一个在线验证服务,该服务使用简单的HTTPS请求检查SafetyNet证明结果的数字签名。
尽管看起来很有用,但它仅用于测试目的,并且具有非常严格的使用配额,根据要求不会增加。相反,您应该以不依赖于Google服务器的方式在服务器上实施数字签名验证逻辑。大多数JWT库提供签名验证功能,我们有代码示例,展示如何在Java和C#中执行此验证。我们计划在未来为更多语言提供样本。
答案 2 :(得分:-1)
有一个开源的android库,能够帮助执行验证:jwtk/jjwt