public ConfigAuthorizedAppRepository(IConfiguration config) { Android = new AuthorizedAppInformation(); Android.Platform = "android"; Android.AllowedRegions = config.SupportRegions(); Android.PackageName = config.AndroidPackageName(); iOS = new AuthorizedAppInformation(); iOS.Platform = "ios"; iOS.AllowedRegions = config.SupportRegions(); iOS.PackageName = config.iOSBundleId(); iOS.DeviceCheckKeyId = config.iOSDeviceCheckKeyId(); iOS.DeviceCheckTeamId = config.iOSDeviceCheckTeamId(); iOS.DeviceCheckPrivateKey = config.iOSDeviceCheckPrivateKey(); }
/// <summary> /// Validation iOS /// </summary> /// <param name="param">subumission parameter</param> /// <returns>True when successful.</returns> /// <remarks> /// https://developer.apple.com/documentation/devicecheck/accessing_and_modifying_per-device_data /// </remarks> public async Task <bool> Validation(IAppleDeviceVerification deviceVerification, DateTimeOffset requestTime, AuthorizedAppInformation app) { var payload = new ApplePayload() { DeviceToken = deviceVerification.DeviceToken, Timestamp = requestTime.ToUnixTimeMilliseconds() }; using (var sha = SHA256.Create()) { payload.TransactionId = Convert.ToBase64String( sha.ComputeHash(Encoding.UTF8.GetBytes(deviceVerification.TransactionIdSeed)) ); } Logger.LogInformation($"{nameof(Validation)} DeviceCheckKeyId:{app.DeviceCheckKeyId} DeviceCheckTeamId:{app.DeviceCheckTeamId} DeviceCheckPrivateKey:{app.DeviceCheckPrivateKey}"); var jwt = GenerateClientSecretJWT(requestTime, app.DeviceCheckKeyId, app.DeviceCheckTeamId, app.DeviceCheckPrivateKey); var payloadJson = JsonConvert.SerializeObject(payload); Logger.LogInformation($"{nameof(Validation)} payload:{payloadJson} "); var request = new HttpRequestMessage(HttpMethod.Post, UrlApple); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt); request.Content = new StringContent(payloadJson, Encoding.UTF8, "application/json"); try { var response = await ClientApple.SendAsync(request); if (!response.IsSuccessStatusCode) { Logger.LogWarning($"iOS device check failed.\r\n{nameof(HttpRequestMessage)} : {request}\r\n{nameof(HttpResponseMessage)} : {response}"); } return(response.StatusCode == System.Net.HttpStatusCode.OK); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(Validation)}"); throw; } }
/// <summary> /// Validation iOS /// </summary> /// <param name="param">subumission parameter</param> /// <returns>True when successful.</returns> /// <remarks> /// https://developer.apple.com/documentation/devicecheck/accessing_and_modifying_per-device_data /// </remarks> public async Task <bool> Validation(DiagnosisSubmissionParameter param, DateTimeOffset requestTime, AuthorizedAppInformation app) { var payload = new ApplePayload() { DeviceToken = param.DeviceVerificationPayload, Timestamp = requestTime.ToUnixTimeMilliseconds() }; var keysText = string.Empty; if (param is V1DiagnosisSubmissionParameter) { keysText = (param as V1DiagnosisSubmissionParameter).Keys .OrderBy(_ => _.KeyData) .Select(_ => _.KeyData) .Aggregate((a, b) => a + b); } else { keysText = param.Keys .OrderBy(_ => _.KeyData) .Select(_ => _.KeyData) .Aggregate((a, b) => a + b); } using (var sha = System.Security.Cryptography.SHA256.Create()) { var value = System.Text.Encoding.UTF8.GetBytes(param.AppPackageName + keysText + string.Join(',', param.Regions)); payload.TransactionId = Convert.ToBase64String(sha.ComputeHash(value)); } Logger.LogInformation($"{nameof(Validation)} DeviceCheckKeyId:{app.DeviceCheckKeyId} DeviceCheckTeamId:{app.DeviceCheckTeamId} DeviceCheckPrivateKey:{app.DeviceCheckPrivateKey}"); var jwt = GenerateClientSecretJWT(requestTime, app.DeviceCheckKeyId, app.DeviceCheckTeamId, app.DeviceCheckPrivateKey); var payloadJson = JsonConvert.SerializeObject(payload); Logger.LogInformation($"{nameof(Validation)} payload:{payloadJson} "); var request = new HttpRequestMessage(HttpMethod.Post, UrlApple); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt); request.Content = new StringContent(payloadJson, Encoding.UTF8, "application/json"); try { var response = await ClientApple.SendAsync(request); if (!response.IsSuccessStatusCode) { var responseBody = await response.Content.ReadAsStringAsync(); Logger.LogWarning($"POST {UrlApple} {response.StatusCode} {response.ReasonPhrase} {responseBody}"); } //switch (response.StatusCode) //{ // // 200 OK: The transaction was successful // // 200 Bit State Not Found: The bit state wasn't found // case System.Net.HttpStatusCode.OK: // if (response.ReasonPhrase == "OK") return true; // break; // // // default: // break; //} if (response.StatusCode != System.Net.HttpStatusCode.OK) { return(false); } return(true); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(Validation)}"); throw; } }
/// <summary> /// Validation Android /// </summary> /// <returns>True when successful.</returns> public bool Validation(IAndroidDeviceVerification deviceVerification, DateTimeOffset requestTime, AuthorizedAppInformation app) { byte[] expectedNonce = GetSha256(deviceVerification.ClearText); var claims = ParsePayload(deviceVerification.JwsPayload); // Validate the nonce if (Convert.ToBase64String(claims.Nonce) != Convert.ToBase64String(expectedNonce)) { return(false); } // Validate time interval var now = requestTime.ToUnixTimeMilliseconds(); if (app.SafetyNetPastTimeSeconds > 0) { var minTime = now - (app.SafetyNetPastTimeSeconds * 1000); if (claims.TimestampMilliseconds < minTime) { return(false); } } if (app.SafetyNetFutureTimeSeconds > 0) { var minTime = now + (app.SafetyNetFutureTimeSeconds * 1000); if (claims.TimestampMilliseconds > minTime) { return(false); } } // Validate certificate if (app.SafetyNetApkDigestSha256?.Length > 0) { var apkSha = Convert.ToBase64String(claims.ApkCertificateDigestSha256); if (!app.SafetyNetApkDigestSha256.Contains(apkSha)) { return(false); } } // Validate integrity if (app.SafetyNetCtsProfileMatch) { if (!claims.CtsProfileMatch) { return(false); } } if (app.SafetyNetBasicIntegrity) { if (!claims.BasicIntegrity) { return(false); } } return(true); }
/// <summary> /// Validation iOS /// </summary> /// <param name="param">subumission parameter</param> /// <returns>True when successful.</returns> /// <remarks> /// https://developer.apple.com/documentation/devicecheck/accessing_and_modifying_per-device_data /// </remarks> public async Task <bool> Validation(IAppleDeviceVerification deviceVerification, DateTimeOffset requestTime, AuthorizedAppInformation app) { var payload = new ApplePayload() { DeviceToken = deviceVerification.DeviceToken, Timestamp = requestTime.ToUnixTimeMilliseconds() }; using (var sha = SHA256.Create()) { payload.TransactionId = Convert.ToBase64String( sha.ComputeHash(Encoding.UTF8.GetBytes(deviceVerification.TransactionIdSeed)) ); } Logger.LogInformation($"{nameof(Validation)} DeviceCheckKeyId:{app.DeviceCheckKeyId} DeviceCheckTeamId:{app.DeviceCheckTeamId} DeviceCheckPrivateKey:{app.DeviceCheckPrivateKey}"); var jwt = GenerateClientSecretJWT(requestTime, app.DeviceCheckKeyId, app.DeviceCheckTeamId, app.DeviceCheckPrivateKey); var payloadJson = JsonConvert.SerializeObject(payload); Logger.LogInformation($"{nameof(Validation)} payload:{payloadJson} "); var request = new HttpRequestMessage(HttpMethod.Post, UrlApple); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt); request.Content = new StringContent(payloadJson, Encoding.UTF8, "application/json"); try { var response = await ClientApple.SendAsync(request); if (!response.IsSuccessStatusCode) { var responseBody = await response.Content.ReadAsStringAsync(); Logger.LogWarning($"iOS device check failed.\r\n{nameof(HttpRequestMessage)} : {request}\r\n{nameof(HttpResponseMessage)} : {response}"); } //switch (response.StatusCode) //{ // // 200 OK: The transaction was successful // // 200 Bit State Not Found: The bit state wasn't found // case System.Net.HttpStatusCode.OK: // if (response.ReasonPhrase == "OK") return true; // break; // // // default: // break; //} if (response.StatusCode != System.Net.HttpStatusCode.OK) { // FIXME: When call iOS Device check, return error sometimes, Until the cause is known, ignored device check return(true); } return(true); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(Validation)}"); throw; } }
/// <summary> /// Validation Android /// </summary> /// <param name="param">subumission parameter</param> /// <returns>True when successful.</returns> public async Task <bool> Validation(DiagnosisSubmissionParameter param, byte[] expectedNonce, DateTimeOffset requestTime, AuthorizedAppInformation app) { var claims = await ParsePayloadAsync(param.DeviceVerificationPayload); // Validate the nonce if (Convert.ToBase64String(claims.Nonce) != Convert.ToBase64String(expectedNonce)) { return(false); } // Validate time interval var now = requestTime.ToUnixTimeMilliseconds(); if (app.SafetyNetPastTimeSeconds > 0) { var minTime = now - (app.SafetyNetPastTimeSeconds * 1000); if (claims.TimestampMilliseconds < minTime) { return(false); } } if (app.SafetyNetFutureTimeSeconds > 0) { var minTime = now + (app.SafetyNetFutureTimeSeconds * 1000); if (claims.TimestampMilliseconds > minTime) { return(false); } } // Validate certificate if (app.SafetyNetApkDigestSha256?.Length > 0) { var apkSha = Convert.ToBase64String(claims.ApkCertificateDigestSha256); if (!app.SafetyNetApkDigestSha256.Contains(apkSha)) { return(false); } } // Validate integrity if (app.SafetyNetCtsProfileMatch) { if (!claims.CtsProfileMatch) { return(false); } } if (app.SafetyNetBasicIntegrity) { if (!claims.BasicIntegrity) { return(false); } } return(true); }
/// <summary> /// Validation iOS /// </summary> /// <param name="param">subumission parameter</param> /// <returns>True when successful.</returns> /// <remarks> /// https://developer.apple.com/documentation/devicecheck/accessing_and_modifying_per-device_data /// </remarks> public async Task <bool> Validation(DiagnosisSubmissionParameter param, DateTimeOffset requestTime, AuthorizedAppInformation app) { var payload = new ApplePayload() { DeviceToken = param.DeviceVerificationPayload, Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }; var keysText = param.Keys .OrderBy(_ => _.KeyData) .Select(_ => _.KeyData) .Aggregate((a, b) => a + b); using (var sha = System.Security.Cryptography.SHA256.Create()) { var value = System.Text.Encoding.UTF8.GetBytes(param.AppPackageName + keysText + string.Join(',', param.Regions)); payload.TransactionId = Convert.ToBase64String(sha.ComputeHash(value)); } var jwt = GenerateClientSecretJWT(requestTime, app.DeviceCheckKeyId, app.DeviceCheckTeamId, app.DeviceCheckPrivateKey); var content = new StringContent(JsonConvert.SerializeObject(payload)); var request = new HttpRequestMessage(HttpMethod.Post, UrlApple); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt); var response = await ClientApple.SendAsync(request); //switch (response.StatusCode) //{ // // 200 OK: The transaction was successful // // 200 Bit State Not Found: The bit state wasn't found // case System.Net.HttpStatusCode.OK: // if (response.ReasonPhrase == "OK") return true; // break; // // // default: // break; //} if (response.StatusCode != System.Net.HttpStatusCode.OK) { return(false); } return(true); }