可靠地验证 JWS 证书链和域

2024-01-19

我正在 Node.JS 中编写后端代码来验证来自 Google SafetyNet API 的 JWS。 我很惊讶没有为此找到现成的模块,因此我开始使用可用的库来研究 JWS 的一些简单验证:

首先,谷歌表示需要执行以下步骤:

  1. 从 JWS 消息中提取 SSL 证书链。
  2. 验证 SSL 证书链并使用 SSL 主机名匹配来验证叶证书是否已颁发给该主机名 attest.android.com。
  3. 使用证书来验证 JWS 消息的签名。
  4. 检查 JWS 消息的数据,确保其与原始请求中的数据匹配。特别是,请确保 时间戳已经过验证,并且随机数、包名称和 应用程序签名证书的哈希值与预期值匹配。

(from https://developer.android.com/training/safetynet/attestation#verify-attestation-response https://developer.android.com/training/safetynet/attestation#verify-attestation-response)

I found 节点何塞 https://github.com/cisco/node-jose它提供了一个简单的接口来验证 JWS,并且它有一个允许嵌入密钥的选项。 我试图准确了解此过程的作用以及它是否足以验证 JWS 的真实性?

const {JWS} = require('node-jose');
const result = await JWS.createVerify({allowEmbeddedKey: true}).verify(jws);

if (result.key.kid === 'attest.android.com') {
  // Are we good to go or do we manually need to verify the certificate chain further?
}

使用嵌入式密钥是否确实验证了嵌入式证书链x5c使用根 CA 以及针对证书的签名?或者我是否需要明确从 Google 获取公钥来单独验证证书?

然后,一个有点相关的问题涉及 Google 用于执行此验证的 API:有一个 APIhttps://www.googleapis.com/androidcheck/v1/attestations/verify?key=...执行这个确切的操作,但它似乎已从 Google 文档中删除,并且只能在过时的文章和有关 SafetyNet 的 SO 答案中找到引用,例如this one https://stackoverflow.com/questions/43095289/is-api-key-needed-in-safetynet-attestation这似乎表明该 API 仅用于测试,在生产中您should自行进行证书验证。有谁知道这个 API 是否适合生产使用?如果每个人都打算手动验证 JWS,我觉得 Google 不会提供更多文档和代码示例有点令人惊讶,因为这个过程很容易出错,而且错误可能会产生严重影响?到目前为止,我只找到了一些 Java 中的第三方示例,但没有来自 Google 的服务器端代码示例。


这里有步骤 https://developer.android.com/training/safetynet/attestation.html#verify-attestation-response您需要按照 Google 的建议执行操作。

绝对可以随意浏览所有参考链接,以更好地了解该过程。请查看此处使用的每个库函数,了解它们在做什么以及这是否正是您希望它们执行的操作。我编写了伪代码来解释这些步骤。您可能需要在示例证明令牌上运行它们来测试它们并相应地更改一些内容。

如果能在一个地方查看 SafetyNet 的整个节点实现也是很好的。

// following steps should be performed
// 1. decode the JWS
// 2. the source of the first certificate in x5c array of jws header 
//    should be attest.google.com
// 3. to make sure if the JWS was not tampered with, validate the signature of JWS (how signature verification is done is explained in the reference links)
//    with the certificate whose source we validated
// 4. if the signature was valid, we need to know if the certificate was valid by 
//    explicitly checking the certificate chain
// 5. Validate the payload by matching the package name, apkCertificateDigest
//    and nonce value (apkCertificateDigest is base64 encoding of the hash of signing app's certificate)
// 6. and now you can trust the ctsProfileMatch and BasicIntegrity flags
// let's see some code in node, though this will not run as-is, 
// it provides an outline on how to do it and which functions to consider when implementing

const pki = require('node-forge').pki;
const jws = require('jws');
const pem = require("pem");
const forge = require('node-forge');

const signedAttestation = "Your signed attestation here";

function deviceAttestationCheck(signedAttestation) {
  // 1. decode the jws
  const decodedJws = jws.decode(signedAttestation);
  const payload = JSON.parse(decodedJws.payload);

  // convert the certificate received in the x5c array into valid certificates by adding 
  // '-----BEGIN CERTIFICATE-----\n' and '-----END CERTIFICATE-----'
  // at the start and end respectively for each certificate in the array
  // and by adding '\n' at every 64 char
  // you'll have to write your own function to do the simple string reformatting
  // get the x5c certificate array
  const x5cArray = decodedJws.header.x5c;
  updatedX5cArray = doTheReformatting(x5cArray);

  // 2. verify the source to be attest.google.com
  certToVerify = updatedX5cArray[0];
  const details = pem.readCertificateInfo(certToVerify);
  // check if details.commanName === "attest.google.com"

  const certs = updatedX5cArray.map((cert) => pki.certificateFromPem(cert));

  // 3. Verify the signature with the certificate that we received
  // the first element of the certificate(certs array) is the one that was issued to us, so we should use that to verify the signature
  const isSignatureValid = jws.verify(signedAttestation, 'RS256', certs[0]);

  // 4. to be sure if the certificate we used to verify the signature is the valid one, we should validate the certificate chain
  const gsr2Reformatted = doTheReformatting(gsr2);
  const rootCert = pki.certificateFromPem(gsr2Reformatted);
  const caStore = pki.createCaStore([rootCert]);

  // NOTE: this pki implementation does not check for certificate revocation list, which is something that you'll need to do separately
  const isChainValid = pki.verifyCertificateChain(caStore, certs);

  // 5. now we can validate the payload
  // check the timestamps, to be within certain time say 1 hour
  // check nonce value, to contain the data that you expect, refer links below
  // check apkPackageName to be your app's package name
  // check apkCertificateDigestSha256 to be from your app - quick tip -look at the function below on how to generate this
  // finally you can trust the ctsProfileMatch - true/false depending on strict security need and basicIntegrity - true, minimum to check

}

// this function takes your signing certificate(should be of the form '----BEGIN CERT....data...---END CERT...') and converts into the SHA256 digest in hex, which looks like - 92:8H:N9:84:YT:94:8N.....
// we need to convert this hex digest to base64 
// 1. 92:8H:N9:84:YT:94:8N.....
// 2. 928hn984yt948n - remove the colon and toLowerCase
// 3. encode it in base64
function certificateToSha256DigestHex(certPem) {
  const cert = pki.certificateFromPem(certPem);
  const der = forge.asn1.toDer(pki.certificateToAsn1(cert)).getBytes();
  const m = forge.md.sha256.create();
  m.start();
  m.update(der);
  const fingerprint = m.digest()
      .toHex()
      .match(/.{2}/g)
      .join(':')
      .toUpperCase();

  return fingerprint
}

// 92:8H:N9:84:YT:94:8N => 928hn984yt948n
function stringToHex(sha256string) {
  return sha256string.split(":").join('').toLowerCase();
}

// this is what google sends you in apkCertificateDigestSha256 array
// 928hn984yt948n => "OIHf9wjfjkjf9fj0a="
function hexToBase64(hexString) {
  return Buffer.from(hexString, 'hex').toString('base64')
}

所有对我有帮助的文章:

  1. 步骤摘要 -Here https://stackoverflow.com/questions/44262107/how-does-ssl-hostname-matching-work-on-jws
  2. 深入解释与实施 -Here https://medium.com/@herrjemand/verifying-fido2-safetynet-attestation-bd261ce1978d
  3. 你应该记住的事情——Here https://android-developers.googleblog.com/2017/11/10-things-you-might-be-doing-wrong-when.html
  4. 谷歌的清单以正确执行 -Here https://developer.android.com/training/safetynet/attestation-checklist
  5. 深入研究这个过程 -Here https://www.blackhat.com/docs/eu-17/materials/eu-17-Mulliner-Inside-Androids-SafetyNet-Attestation.pdf
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

可靠地验证 JWS 证书链和域 的相关文章

  • if 不是 localhost 语句 htaccess

    我目前强迫访问者通过 https 访问我的所有网站 主要是 Wordpress 我使用以下代码 RewriteEngine On RewriteCond HTTPS on RewriteRule https SERVER NAME REQU
  • 如何在docker的keycloak中添加SSL

    我在将 SSL 证书添加到在 docker 上运行的 Keycloak 时遇到问题 我通过负载均衡器从 AWS EC2 获得了 SSL 证书 但不知道如何将其添加到 docker 上的 Keycloak 中 我正在通过谷歌搜索 但尚未找到任
  • 如何使用 lodash、underscore 或 bluebird 同步迭代数组 [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我有一个数组 其中每个索引处包含文件名 我想下载这些文件一次一个 同步 我知道关于 Async 模块 但我想知道是否有任何功能Lodash
  • 如何使用Python中字符串变量中存储的证书打开ssl套接字

    在Python中 ssl wrap socket可以从文件中读取证书 ssl wrap socket需要证书作为文件路径 如何使用从字符串变量读取的证书启动 SSL 连接 我的主机环境不允许写入文件 并且临时文件模块不起作用我正在使用Pyt
  • sails js 数据关系

    sails js 是否支持模型 集合之间的关系 如果是这样 有人可以指出文档吗 例如 我有 2 个模型 1 生产 production name string description text 2 Album album name stri
  • fsockopen() 和 SSL 出错,“无法启用加密”

    我正在尝试连接到 Nominet EPP 测试台 但收到 无法启用加密 的消息 这似乎是一个罕见的错误 没有记录的解决方案或原因 用行 socket fsockopen ssl testbed epp nominet org uk 700
  • 如何在 MongoDB v3.0.5 中创建用户

    我需要在 mongodb 中为我的数据库创建一个用户 但似乎我无法让它工作 我已经在我的 Windows 7 机器上安装了 mongoDb v3 0 5 根据本文 https docs mongodb org v3 0 tutorial a
  • 带回调或异步/等待的节点 postgres 事务?

    我正在运行 Node 7 6 0 它支持 async await node postgres 客户端池支持 async await 并且有一个很好的示例here https github com brianc node pg pool pl
  • 使用 Puppeteer 和 Headless Chrome 获取 DOM 节点文本

    我正在尝试使用无头 Chrome 和 Puppeteer 来运行我们的 Javascript 测试 但我无法从页面中提取结果 基于这个答案 https stackoverflow com a 45799744 4794 看起来我应该使用pa
  • nvm代理设置配置文件

    在Linux下 我使用 http proxy and https proxy这适用于node and npm but nvm不使用它们 我应该在哪个配置文件中写入代理设置 uname a Linux Breizh Atao 3 19 0 3
  • 阻止 PM2 上不同时运行的请求

    在我的 Express 应用程序中 我在应用程序中定义了 2 个端点 一种用于 is sever up 检查 另一种用于模拟阻塞操作 app use status req res gt res sendStatus 200 app use
  • 获取 Promise 的值并分配给变量

    utility fetchInfo 返回一个 Promise 对象 我需要能够获取此 Promise 对象的值 并将其值分配给一个变量 以便稍后在我的代码中使用 此刻 我可以愉快地打印出result到控制台 但我需要能够将此值分配给myVa
  • 弹出表单可见,但 Puppeteer 中缺少 html 代码

    我目前正在尝试从网站获取一些信息 https www bauhaus info https www bauhaus info 并在 cookie 弹出表单中失败 到目前为止 这是我的代码 async gt const browser awa
  • NodeJS 找不到模块“grpc”

    我尝试在我的树莓派 3 上运行 JS 脚本 但我不断返回一个似乎是 grpc 模块的问题 我已经尝试重新安装和重建 npm 以下https github com firebase firebase tools issues 442 http
  • 将jquery与nodejs一起使用时的基本错误

    我正在尝试在我的项目中使用一些jquery 当我尝试使用它时 我在复制的代码中遇到了错误 并且无法获得任何有关它的谷歌帮助 var jquery require jquery var jquery create TypeError Obje
  • 如何使用expressjs发送多个文件?

    我希望能够发送许多文件 如果可能的话 发送整个目录 以便我可以在从 html 文件调用的其他 js 文件中访问它 const app require express const http require http Server app co
  • 谷歌应用程序引擎nodejs本地开发

    有没有办法在本地运行我的nodejs应用程序 以便我可以模拟在生产App Engine中运行的应用程序而无需部署它 由于某种原因 我的应用程序在 Google App Engine 中的行为与我的本地主机不同 我厌倦了每次为了查看是否存在错
  • 已安装全局 NPM 包但未找到命令

    我已经全局安装了两个 npm 包 下载 https www npmjs com package download and 谜虚拟盒 https www npmjs com package enigmavirtualbox通过命令行 npm
  • 节点项目的 Azure git 部署失败

    我正在尝试将我的项目部署到azure 它正在失败 这些是我采取的步骤 git init git config core longpaths true git add git commit m initial commit 所有这些都有效 我
  • 使用 node.js 获取正在运行的进程的 stdin/stdout

    我正在从节点启动一个进程child process spawn http nodejs org docs v0 6 1 api child processes html child process spawn处理 process stdou

随机推荐

  • 使用 Mockito 2 模拟期末课程

    我正在从我当前正在进行的项目中删除 Powermock 因此我尝试仅使用 Mockito mockito core 2 2 28 重写一些现有的单一测试 当我运行测试时 出现以下错误 org mockito exceptions base
  • 如何声明其类没有默认构造函数的对象数组?

    如果一个类只有一个构造函数和一个参数 如何声明数组 我知道在这种情况下推荐使用向量 例如 如果我有一堂课 class Foo public Foo int i 如何声明一个包含 10000 个 Foo 对象的数组或向量 对于数组 您必须在定
  • 如何将 .env 文件中的环境变量读取到 terraform 脚本中?

    我正在使用 terraform 在 aws 上构建 lambda 函数 terraform 脚本中用于上传环境变量的语法是 resource aws lambda function name of function environment
  • CSS Transition 相当于 jQuery fadeIn()、fadeOut()、fadeTo()

    我有这个 button1 click function header bg fadeTo 15 0 function document getElementById header bg style fill FF0000 fadeTo sl
  • 完全通过 FIFO 连接到 MySQL 客户端

    在 Bash 脚本中 我想在多个顺序访问中保持 MySQL 会话打开 访问 MySQL 的常见方法是为每个 SQL 命令或命令集打开一个单独的会话 例如 mysql u user e show tables 此方法的限制是那些需要双重事务的
  • Django settings.AUTH_USER_MODEL 在单独的模块中定义

    表达我的事情的正确方式是什么AUTH USER MODEL 我有以下一组 文件夹结构 后端 API 楷模 用户 py user py位于内models folder 在设置 py中 AUTH USER MODEL myapp User IN
  • 有没有办法在 JavaFX LineChart 中断开串联的 2 个点?

    我在 LineChart 上有四个系列 每个系列都包含一定数量的按时间划分的图表 默认情况下 LineChart 连接这些图表 它看起来很难看并且在上下文中没有任何意义 所以我想将它们分开 但保留颜色和图例 换句话说 我想要的是删除两个特定
  • Spring框架中的依赖注入和控制反转是什么?

    依赖注入 和 控制反转 经常被认为是使用 Spring 框架开发 Web 框架的主要优点 如果可能的话 有人可以用一个非常简单的术语解释它是什么吗 Spring 有助于创建松散耦合的应用程序 因为依赖注入 在 Spring 中 对象定义它们
  • C++ 自省技术,类似于 python

    C 中是否有像 Python 中那样的自省技术 例如 我想获取有关特定对象的更多信息 而不需要通过头文件或引用 cpp 引用 我是问了一个正确的问题 还是走错了方向 Update 根据以下答案 这个答案与我的问题相关 如何向 C 应用程序添
  • Blaze:{{#if}} 语句中的逻辑(Not、Or、And...)

    有没有办法在 if 语句中进行逻辑运算 我希望有这样的事情 if A B some html if 我在 blaze 中找不到有关逻辑的文档 所以我猜它不受支持 我只是想确定一下 抱歉问了一个相当愚蠢的问题 正如 Billy Bob 所建议
  • Express.js:如何获取 ip 地址并渲染视图?

    我真的认为这应该很容易 但是当我渲染一个jade模板时 我也想抓取ip地址 我的代码看起来像这样 app js app get index home index js exports home function req res res re
  • C# 中的外部 IP 地址

    在 C 中获取外部 IP 地址的最简单方法是什么 框架内没有内置的方法来执行此操作 因为很难确定外部 公共 IP 地址是什么 当然 这是假设您的 IP 在某个网关后面经过 NAT 一种方法是抓取类似的网站http www whatismyi
  • 解释错误:ISO C++ 禁止声明没有类型的“Personlist”

    我有一个类将处理我之前创建的另一个类的对象数组 效果很好 当我尝试创建列表类的对象时出现问题 这是列表类的标题 ifndef personlistH define personlistH include Person h include
  • 如何从当前月份中选择当前日期

    我想检索当月 1 30 之间的数据 我正在使用 MSACCESS Dbase 来执行此操作 下面是我正在尝试的查询 SELECT count usercategory as category count usercategory FROM
  • VS Code 扩展安全如何处理?

    我已经使用 VS Code 一年左右了 我不知道 VS Code Extension 安全性是如何处理的 我对这样的事情感到震惊 Markdown 预览增强 https marketplace visualstudio com items
  • CRA + React Leaflet:编译失败

    我刚刚开始一个全新的项目create react app并设置react leaflet正如他们的文档所建议的here https react leaflet js org docs start installation 我正在尝试使用这个
  • pyspark:自动填充隐式缺失值

    我有一个数据框 user day amount a 2 10 a 1 14 a 4 5 b 1 4 你看 最大值day是4 最小值是1 我要填0 for amount列中所有用户的所有缺失天数 因此上面的数据框将变为 user day am
  • Safari 和 iOS 上的 Html5(音频)

    我正在开发一款 Web 应用程序 但与 Apple 设备和 PC 上的 Safari 浏览器存在兼容性问题 Html5 音频标签
  • 假人的 Getters \ Setters

    我一直在尝试了解 getter 和 setter 但它没有被理解 我读过JavaScript Getter 和 Setter http ejohn org blog javascript getters and setters and 定义
  • 可靠地验证 JWS 证书链和域

    我正在 Node JS 中编写后端代码来验证来自 Google SafetyNet API 的 JWS 我很惊讶没有为此找到现成的模块 因此我开始使用可用的库来研究 JWS 的一些简单验证 首先 谷歌表示需要执行以下步骤 从 JWS 消息中