非对称加密在移动端开发中的常用场景

概述

非对称加密简介

公开密钥密码学(英语:Public-key cryptography)也称非对称式密码学(英语:Asymmetric cryptography)是密码学的一种算法,它需要两个密钥,一个是公开密钥,另一个是私有密钥;公钥用作加密,私钥则用作解密。使用公钥把明文加密后所得的密文,只能用相对应的私钥才能解密并得到原本的明文,最初用来加密的公钥不能用作解密。由于加密和解密需要两个不同的密钥,故被称为非对称加密;不同于加密和解密都使用同一个密钥的对称加密。公钥可以公开,可任意向外发布;私钥不可以公开,必须由用户自行严格秘密保管,绝不透过任何途径向任何人提供,也不会透露给被信任的要通信的另一方。

以上这段话是引用自公开密钥加密维基百科

非对称加密和对称加密的区别

  • 用于消息解密的密钥值与用于消息加密的密钥值不同,非对称加密是私钥加密,公钥解密,反之公钥加密,私钥解密。对称加密加密好解密都是用同一个密钥值。

  • 非对称加密算法比对称加密算法慢数千倍。

数字签名

数字签名(Digital Signature) 是一种相当于现实世界中的盖章签字的功能在数字信息领域中的实现。数字签名可以识别篡改和伪装。

在数字签名技术中,有两种行为:

  • 签名生成
  • 签名验证

签名生成由通信中的发起方进行,其过程如下所示。首先对通信内容进行哈希,然后使用发送放的私钥进行加密,最终得到签名。

image.png

签名验证由通信中的接收方进行,其过程如下所示。一般而言,发送方会把消息、签名一起发送给接收方。接收方首先使用发送方的公钥对签名进行解密,计算得出一个摘要。然后对消息进行哈希,计算得出另一个摘要。最后判断两个摘要是否相等,如果相等则说明接收到的消息没有被第三方进行篡改。 image.png

那么接收方是如何获取到发送方的公钥的呢?接收方又是如何确定该公钥就是属于发送方的呢?这就是数字证书要做到事。

数字证书

数字证书(Digital Certificate) 是一种相当于现实世界中身份证的功能在数字信息领域中的实现。数字证书包含了个人或机构的 身份信息 及其 公钥,因此也称为 公钥证书(Public-Key Certificate,PKC)。 image.png 类似于身份证是由权威的公安局颁发,公钥证书也是由权威的 认证机构(Certificate Authority,CA) 颁发。认证机构向接收方提供发送方的证书,证书中包含了发送方的身份信息和公钥。为了防止证书在颁发过程中被篡改,认证机构会将身份信息和公钥作为消息,用 CA 私钥 进行签名,进而将身份信息、公钥、签名 一起放入证书,如下图所示。 image.png

  • 根证书

接收方得到发送方证书时,通过 CA 公钥对证书进行签名验证。很多情况下,CA 公钥则又是由另一个更加权威的机构颁发。类似于地方公安局的证书是由市级公安局颁发,市级公安局的证书又是由省级公安局颁发。证书是具有信任链(Chain of Trust)的,根证书(Root Certificate) 是信任源,即信任链的起源。

根证书的颁发者被称为 Root Certificate Authority(Root CA)。某一认证领域内的根证书是 Root CA 自行颁发给自己的证书(Self-signed Certificate),安装证书意味着对这个 CA 认证中心的信任。

根据证书在信任链中所处的位置,可以将证书分为三种:

  • 根证书(Root Certificate)
  • 中间证书(Intermediate Certificate)
  • 叶子证书(Leaf Certificate)

这里就有一个根本性的问题:如何保证根证书是可信的?

事实上,根证书都是随软件一起安装的,如:操作系统安装时会内置一份可信的根证书列表。

apple开发证书

首先明确证书的目的是证明你确实是apple开发者(毕竟开发者是要给apple爸爸钱的) 我们来看一下 MacOS 系统中关于 iOS 开发证书的信任链示例(通过“钥匙串”查看):

-- Apple Root Certificate Authority:根证书
   -- Apple Worldwide Developer Relations Certification Authority:中间证书
      -- iPhone Developer:(XXXXXXX):叶子证书
复制代码

image.png

使用流程

  • 开发者在本地生成密钥对,并提供开发者的身份信息。
  • 将密钥对中的公钥、身份信息发送给 CA。
  • CA 使用 CA 私钥对开发者的公钥、身份信息进行签名。
  • CA 将开发者的公钥、身份信息、签名组装成证书以供下载。

image.png 注意:从 Apple Member Center 网站下载证书到 Mac 上双击即可安装。

手动点击 *.cer 安装证书到 macOS 时,Keychain Access 追溯其签发 CA 为 AppleWWDRCA(Apple Worldwide Developer Relations Certification Authority),利用 AppleWWDRCA 证书公钥对该开发证书数字签名进行解密和校验。校验成功会标记此证书有效(This certificate is valid)

git

本地git仓库和远程仓库之间的传输是通过SSH加密的,SSH 是 Secure Shell 的缩写即安全外壳协议,SSH 为建立在应用层基础上的安全协议。SSH 为远程登录会话和其他网络服务提供安全性的协议。

SSH之所以能够保证安全,原因在于它采用了公钥加密。

SSH的使用过程

  • 远程主机收到用户的登录请求,把自己的公钥发给用户。
  • 用户使用这个公钥,将登录密码加密后,发送回来。
  • 远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。

这个过程本身是安全的,但是实施的时候存在一个风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。

可以设想,如果攻击者插在用户与远程主机之间(比如在公共的wifi区域),用伪造的公钥,获取用户的登录密码。再用这个密码登录远程主机,那么SSH的安全机制就荡然无存了。这种风险就是著名的"中间人攻击"(Man-in-the-middle attack)。

如果你是第一次登录对方主机,系统会出现下面的提示:

  $ ssh user@host

  The authenticity of host 'host (12.18.429.21)' can't be established.

  RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.

  Are you sure you want to continue connecting (yes/no)?
复制代码

这段话的意思是,无法确认host主机的真实性,只知道它的公钥指纹,问你还想继续连接吗?

所谓"公钥指纹",是指公钥长度较长(这里采用RSA算法,长达1024位),很难比对,所以对其进行MD5计算,将它变成一个128位的指纹。上例中是98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d,再进行比较,就容易多了。

很自然的一个问题就是,用户怎么知道远程主机的公钥指纹应该是多少?回答是没有好办法,远程主机必须在自己的网站上贴出公钥指纹,以便用户自行核对。

假定经过风险衡量以后,用户决定接受这个远程主机的公钥。

  Are you sure you want to continue connecting (yes/no)? yes

系统会出现一句提示,表示host主机已经得到认可。

  Warning: Permanently added 'host,12.18.429.21' (RSA) to the list of known hosts.

然后,会要求输入密码。

  Password: (enter password)

如果密码正确,就可以登录了。

当远程主机的公钥被接受以后,它就会被保存在文件$HOME/.ssh/known_hosts之中。下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。

每个SSH用户都有自己的known_hosts文件,此外系统也有一个这样的文件,通常是/etc/ssh/ssh_known_hosts,保存一些对所有用户都可信赖的远程主机的公钥。

公钥登录

使用密码登录,每次都必须输入密码,非常麻烦。好在SSH还提供了公钥登录,可以省去输入密码的步骤。

所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。

这种方法要求用户必须提供自己的公钥。如果没有现成的,可以直接用ssh-keygen生成一个:

  $ ssh-keygen

运行上面的命令以后,系统会出现一系列提示,可以一路回车。其中有一个问题是,要不要对私钥设置口令(passphrase),如果担心私钥的安全,这里可以设置一个。

运行结束以后,在$HOME/.ssh/目录下,会新生成两个文件:id_rsa.pub和id_rsa。前者是你的公钥,后者是你的私钥。

这时再输入下面的命令,将公钥传送到远程主机host上面:

  $ ssh-copy-id user@host

好了,从此你再登录,就不需要输入密码了。

token

第三方登录或者自建账户登录后,都会返回一个token,后续的网络请求只要带上这个token,不需要用户名和密码。这里可以把token理解为用户名和密码的一种凭证,有它就可以去服务器获取该用户的各种资源。那它的原理是什么呢?

token的使用流程

使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

这个token必须要在每次请求时传递给服务端,它应该保存在请求头里。

实现token的机制

实施 Token 验证的方法挺多的,有一些标准方法,比如 JWT,读作:jot ,表示:JSON Web Tokens 。JWT 标准的 Token 有三个部分:

  • header
  • payload
  • signature

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature).

header

jwt的头部承载两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法 通常直接使用 HMAC SHA256

{ 'typ': 'JWT', 'alg': 'HS256' }

将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

playload

载荷就是存放有效信息的地方。

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

标准中注册的声明 (建议但不强制使用) :

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明 :

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 :

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:

{ "sub": "1234567890", "name": "John Doe", "admin": true }

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

// javascript 
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); 
var signature = HMACSHA256(encodedString, 'secret'); 
// TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
复制代码

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

从上面即可看出token是由私钥签名,并由公钥验证!!!

服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:

image.png

  • JWT 如何保护数据?

重要的是要明白,使用 JWT 的目的不是为了加密数据。使用 JWT 是为了保证发送的数据是由可信的来源创建的。

在 JWT 中的数据只是被编码和签名,并没有被加密。

编码数据的目的是为了转换数据的结构,对数据签名是为了保证接收者可以校验数据的来源。当然,编码和签名不能保护数据。JWT 的目的是为了验证数据的来源可靠性,并不是为了保护数据和防止未经授权的访问。

JWT 仅仅是对数据编码、签名,并没有加密。因此, JWT 并不能保证数据的安全性。

  • 校验 JWT

使用的 JWT 是由 HS256 算法签名,并且密钥只有服务器知晓。当用户使用附带 JWT 的请求调用 API 时,应用可以对header和payload执行相同的签名算法。然后,应用可以验证自身创建的签名是否与 JWT 中附带的签名一致(签名是否与服务器创建的 JWT 签名一致)。如果签名一致,则表示 JWT 是有效的,表明 API 请求来源可信。如果签名不一致,表示 JWT 无效,那么请求可能是对应用服务器的潜在攻击。通过校验 JWT,可以在应用和用户之间建立信任。

HTTPS

网络请求方式通常分为两种,分别是HTTP请求和HTTPS请求,其中HTTP的传输属于明文传输,在传输的过程中容易被人截取并且偷窥其中的内容,而HTTPS是一种在HTTP的基础上加了SSL/TLS层(安全套接层)的安全的超文本传输协议,其传输的内容都是加密过的,所以说是一种安全的传输;http和https使用的端口不一样,前者是80,后者是443;

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer)安全套接字层超文本传输协议,HTTPS用于安全的HTTP数据传输。HTTPS的安全基础是SSL,

非对称加密算法(公钥和私钥)交换对称密钥+数字证书验证身份(验证公钥是否是伪造的)+利用对称密钥加解密后续传输的数据=安全

image.png

猜你喜欢

转载自juejin.im/post/7017751396749410311