一文搞懂Dfinity的身份认证

title: 一文搞懂 Dfinity 的身份认证

date: 2021-08-16 14:02:50

作者：ddd009
本文从基本的密码学概念讲起，详细解释了消息签名认证的过程，最后讲解了 Dfinity 是如何通过 Internet Identity 服务帮助用户管理自己的身份（密钥）的。

基本概念

可以看到除了 canister ID，函数名称等常规请求信息，请求内容还包括用户的公钥与消息签名，Caller's Principal 是用户的公钥进行哈希得到的，下文中会具体讲解这一过程。被调用方收到消息后会使用用户的公钥验证签名的正确性，并检查公钥与 Caller‘s Principal 是否对应。

而 canister 的开发者是不需要理会底层的密码学细节的，只需要编写代码，Dfinity 会自动完成身份验证。

这张图片展示了如何通过公钥得到用户的 Principal

首先从 DER 格式的公钥开始，对其进行 SHA-224 哈希运算，得到一个 28 字节的字符串，之后添加一字节用于区分用户 principal 和其他 principal，例如 canister 的 principal。

这 29 个字节是用户 principal 的二进制表示，可以看到 Motoko 和 JS 中 principal 变量是一个 Uint8 的 Blob 数组，里面保存的就是这 29 个字节

之后就是把这 29 个字节转化为文本表示的过程，首先添加 CRC32 错误检查码，再进行 Base32 编码，把最终得到的字符串以五个字符为一组分组，中间以 “-” 分隔，就得到用户的 prinicpal 文本表示。

顺便提一下，有base32 的社区实现 by flyq, 已经合并到 vessel-package-set，可以通过 vessel 进行使用。

如果说用户的 Principal 是与单一密钥绑定的将会非常不方便。如果说你有多个设备，那你需要在这些设备中使用同一个密钥，这即不方便又不安全。

Dfinity 使用了委托密钥的机制，如图你可使用黄色的密钥对橙色的密钥签名生成一个委托，包含作用域与过期时间。而橙色的密钥又可以对其他密钥进行委托，所以说这一方式是非常灵活的。

看到这里大家应该对于密钥对，用户 Principal，委托密钥这些基本概念有了大概的认识，那么 Dfinity 是如何使用这些密码学技术来完成用户身份验证的呢？

委托签名认证

委托密钥的一种应用与 Web Auth 有关，Web Auth 是 W3C 推出的最新标准，主要针对与 web 应用的双重认证。也就是除了传统的用户名密码外，用户还需要对服务器发出的质询使用额外的安全设备进行签名，密钥是储存在安全芯片中的，不会发生泄漏，即时你的系统感染了木马，它们也不能对安全芯片中的密钥怎么样。通过这种方式可以确保是用户登录了服务器。

上面说的是传统互联网的做法，在 Dfinity 上这一过程有些许不同。使用中心化服务器时，你可以与服务器建立有状态的长连接，因此只需要对服务器发送的质询进行一次签名，之后的通信可以使用建立的连接传输。而在 Dfinity 上，并不存在一个中心化的服务器，我们不能同服务间建立有状态的连接，服务也不能主动向用户发出质询，因此用户需要对发出的每一条请求进行签名，而使用 Web Auth 时是需要对每次签名进行确认的，我们当然不能每发一次请求就触摸一下 Yubikey 或者按一次指纹，我们先生成一个短期的会话密钥，再使用 Web Auth 对会话密钥进行委托签名，最后用委托密钥自动完成对消息的签名。这样就解决了对多个消息签名的问题。

Internet Identity

尽管 Web Auth 非常适合安全的存储密钥，但是由于浏览器的安全限制，密钥是与特定 canister 绑定的。在 IC 上只要 canister 不同就视为不同源，这种状态分离对于安全性至关重要。而这也给用户带来了麻烦，例如你很难跨设备使用同一个服务。针对这一缺点官方推出了 Internet Identity，简称 II，类似于 Sign With Google 的 SSO 服务，方便用户管理密钥与身份

如图，应用前端生成会话密钥并将公钥发送至 II 服务，如果用户同意，II 服务将对会话密钥进行委托签名，所有的认证过程都是在用户端完成的。