0%

SSH的全称是Secure Shell,主要用途之一是远程登录。在使用VPS时我们会经常使用到SSH,这里总结一下SSH的两种登录方式。
SSH的登录方式有以下两种:

  • 口令登录
  • 公钥登录

下面对以上两种登录方式逐一介绍

口令登录

口令登录的整个过程是这样的:

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

第一次尝试登录一台VPS,你在终端尝试如下方式登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
QuinnChens-MacBook-Pro:.ssh Quinn$ ssh root@139.162.xxx.xxx
The authenticity of host '139.162.xxx.xxx (139.162.xxx.xxx)' can't be established.
ECDSA key fingerprint is SHA256:A8AiPFbsTEHDlCgKt1x/Q3vMXpTt4JdoIDf1+dEc144.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '139.162.xxx.xxx' (ECDSA) to the list of known hosts.
root@139.162.xxx.xxx's password:
Welcome to Ubuntu 14.04.4 LTS (GNU/Linux 4.8.6-x86_64-linode78 x86_64)

* Documentation: https://help.ubuntu.com/
New release '16.04.2 LTS' available.
Run 'do-release-upgrade' to upgrade to it.

Last login: Sun Feb 19 12:48:41 2017 from 14.19.154.20
root@ubuntu:~#

此时VPS返回给你一串摘要字符串,这是一段服务器的公钥的摘要,并且让你选择是否接受这个公钥。这时有两个问题,我怎么知道应不应该接受它?还有,如果接受这个公钥会怎么样?

回答第一个问题:为了防止中间人攻击,HTTPS是使用证书认证机制,而SSH是没有借助证书的,所以为了验证公钥的可靠性,远程的机器需要在自己的网站贴出自己的公钥摘要,然后人工去检查公钥的真伪。

回答第二个问题:如果接受了这个公钥,你继续输入密码即可登录VPS,而且本地的~/.ssh/known_hosts中会保存这把公钥。下次使用这种方式登录,则只需要输入密码即可。客户端就会使用服务器的公钥对密码加密,发送给远程主机,远程主机使用私钥解密后,校验密码正确与否。

另外,上面服务器发送客户端的公钥,存储在服务器的/etc/ssh/ssh_host_rsa_key.pub中(有可能是同目录下的其他.pub文件中)

此处记录一个我遇到一个问题,就是当我登录了远程主机,亲自去校验公钥的摘要时,发现如果本地机器和远程主机的ssh的版本不一样,生成公钥的摘要的算法也不同,检查起来还挺麻烦的,网上有不少关于这个问题的文章,我还没完全找到方法,

公钥登陆

如果每次都使用密码登录,显然显得繁琐,那么你可以选择公钥登录。

公钥登录的原理如下:客户端将自己的一个公钥分享给远程主机,每次用户的登录时,远程主机向用户发送一份随机数,用户用自己的私钥对这份随机数加密,然后发送回远程主机,远程主机使用来自用户的公钥解密,进行校验,即可验证登录合法性。

为了使用这种方式登录,你需要将本地的.ssh/id_rsa.pub的内容存储到服务器的.ssh/authorized_keys文件中。
你可以使用以下命令实现以上功能。

1
ssh-copy-id user@host

而如果本地还未存在.ssh/id_rsa.pub文件,则可以使用以下命令生成

1
ssh-keygen

在生成密钥时,终端会要求你生成一份密钥的口令(密码的密码),下次你登录时,虽然不用输入远程主机的密码,但是你需要输入这个密钥的口令。这样也好,否则所有人用你的这台电脑都可以登录到你的VPS了。

以上就是关于SSH两种登录方式的介绍,其实SSH除了用于远程登录,还有一个用途就是用于很多穿越GFW的领域,这涉及到SSH的端口转发功能。

这个周末阅读了一些关于HTTPS的资料,再结合一些自己的理解,这里记录下我对HTTPS最新的理解。

首先,先列出和HTTPS相关的知识点

  • SSL/TLS
  • 对称加密、非对称加密、公钥私钥
  • 数字摘要,数字签名
  • 数字证书

另外,列出HTTPS的步骤

  • 握手阶段(建立安全连接,非对称加密、数字摘要、数字签名、数字证书都是在这个阶段出现)
  • 通信阶段(这个阶段进行简单的对称加密通信)

从上面的梳理不难看出,我们接下来讲到的很多概念,都是出现在握手阶段。接下来让我们从基础开始。

一、HTTP HTTPS SSL/TLS的概念

如果网络通信只基于HTTP协议,信息是明文传输的,HTTP协议结合SSL/TLS协议后,就能实现加密的网络通信,这种通信协议也就是HTTPS协议。HTTPS协议的加密工作就是由SSL/TLS协议完成。

SSL/TLS层是介于应用层(HTTP就在这一层)和TCP层的传输安全层,下图是来自《HTTP权威指南》一书的截图

SSL/TLS

SSL的全称是:Secure Sockets Layer;TLS的全称是:Transport Layer Security

那么SSL和TLS有什么关系呢?很简单,TLS是SSL的升级版本。最开始NetScape公司设计了SSL协议,后来互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS。

综上所述,我们可以知道,HTTPS相比于HTTP,就是多了SSL/TLS这一层,所以我们接下来讨论的HTTPS的特点,其实就是SSL/TLS的特点。

二、关于加密的基础

关于加密,首先得知道一个概念,密钥是加密算法的一个参数,密钥就像调用一个函数时输入的参数。而HTTPS中使用的加密算法基本公开的,所以加密的安全性取决于你对密钥的管理和保护。

HTTPS中涉及到的加密算法,包括以下三种。

  • 对称加密:DES,Triple-DES,RC2,RC4等
  • 非对称加密:RSA等
  • 不可逆加密:SHA-1,MD5等

接下来一一介绍这三种算法的特点以及应用场景

对称加密

密钥成对出现,而且两个密钥相同,两个密钥之间可以相互加密解密,对称加密的缺点在于维护密钥很麻烦,如果N个节点间相互通信,则每个节点需要给其他(N-1)个节点颁发一个唯一的密钥,所以一共需要产生N*(N-1)个密钥,管理成本很高。而接下来讲到的非对称加密对密钥的管理就容易多了。

另外,由于加密解密的密钥相同,所以,如果A需要和B、C两个人通信,则A需要给B和C颁发不同的密钥,否则B和C之间可以解密你发给他们任何一个人的信息。所以对称加密是一对一的,而下面即将讲到的非对称加密则是一对多的。

对称加密有一个优点,它的计算速度相比非对称加密更快。

非对称加密

密钥成对出现,而且两个密钥不同,一个叫公钥,一个叫私钥,公钥可以有多个,私钥则只能有一个。私钥一般存储于服务器,而公钥则是公开的,每个人都可以从服务器获取一把公钥。

非对称加密管理密钥很简单,服务器持有一把私钥,所有客户端可以从服务器请求获得一把公钥,大家获得的公钥都是相同的,公钥加密的信息只能由私钥解密,所以,虽然所有客户端的公钥相同,也不同担心他们解密其他人的信息。很明显,相比于对称加密的一对一关系,非对称加密是一对多的关系。

非对称加密有一个缺点,就是相比于对称加密,速度比较慢。

从上面的介绍,我们知道对称加密和非对称加密各有优点缺点,对称加密优点在于速度快,非对称加密优点在于管理密钥更方便,所以HTTPS扬长避短,在不同阶段使用这两种算法。HTTPS开始建立连接时,进行几次握手,这个过程使用非对称加密,这个过程完成后,客户端和服务器就会持有一个相同的密钥,接下来使用更快的对称加密进行通信。

好,回头看看文章开头讲到的HTTPS的两个阶段:握手阶段(建立通信阶段)和通信阶段。其实HTTPS的很多概念,诸如数字摘要,数字签名,数字证书,都是在握手阶段出现的,通信阶段只是简单的对称加密而已。

公钥私钥关系进一步理解

上面我们讲到对称加密的两个密钥可以相互加解密,那么非对称加密的公钥和私钥呢?它们可以相互加解密吗?

答案是可以,首先私钥由服务器使用,公钥由客户端使用,一般有以下两种场景使用公钥私钥

  • 客户端使用公钥加密数据,服务器使用私钥解密数据
  • 服务器使用私钥进行签名(涉及私钥加密),客户端使用公钥进行认证(涉及公钥解密)

这里提到的签名和认证,在接下来会讲到。我们先理解这一点,非对称加密中,公钥私钥是可以相互加密解密的

另外,非对称加密的实现原理,其中一步是生成两个不同的密钥,这两个密钥谁都可以当密钥和私钥,不过被选择作为私钥的一方,必须只能有一份,并且进行妥善管理,而公钥则可以公开。既然最初两个不同密钥,谁都可以当公钥和私钥,那么从这一点也可以侧面证明,公钥私钥是可以相互加密解密的,

非对称加密

非对称加密指HASH,MD5之类的不可逆算法,明文执行一次HASH算法生成数字摘要,数字摘要再进一步使用服务器的私钥生成数字签名。接下来会讲到这个流程。

三、数字摘要,数字签名、数字证书

握手阶段,服务器拥有公钥和私钥,各个客户端可以从服务器申请获得公钥,握手阶段的最终目的是让服务器和客户端拥有一个相同的密钥,这个相同的密钥用于通信阶段的对称加密。

握手阶段,主要需要关注以下几个问题

  • 信息在发送过程是否被篡改
  • 通信的对象是否是第三方假冒的(中间人攻击)
  • 最终生成的对称密钥安全可靠

HTTPS的握手阶段就是围绕以上风险进行设计的。

数字摘要和数字签名

我们先来介绍数字摘要和数字签名在握手阶段是如何应用的。数字摘要是报文调用诸如HASH、MD5等不可逆算法产生的摘要信息,而数字签名是数字摘要使用私钥加密后的结果。数字摘要和数字签名主要用于检验信息是否被篡改,其工作原理如下

  • 客户端获取来自服务器的公钥
  • 服务器准备好一段报文(明文,未加密),使用HASH函数等不可逆算法从报文中生成数字摘要,使用私钥对数字摘要加密生成数字签名,带在报文末尾
  • 服务器发送带有数字签名的报文给客户端
  • 客户端收到报文,取下数字签名,并使用公钥对数字签名解密,得到数字摘要A
  • 客户端将数字签名取下后,从报文中生成数字摘要B,与上一步中的数字摘要A对比,如果不同,则报文可能被篡改过,或者服务器是第三方假冒的。

下图是来自《HTTP权威指南》一书对数字签名的解释

数字签名

前面我们提到非对称加密中,公钥和私钥可以相互加密解密,数字签名这里验证了其中一种情况:私钥加密,公钥解密

从上面列举的情况看来,似乎客户端和服务器的通信已经很安全了,其实不然,如果客户端在向服务器请求公钥时,某个第三方假冒者冒充了服务器,客户端获得了一把假冒的公钥,然后接下来的通信中,客户端以为这个假冒者就是服务器。假冒者用它自己的私钥加密生成数字签名,然后你用假冒者提供给你的公钥解密数字签名后,会验证到签名是正确的。这就是所谓的中间人攻击,所以,数字签名并不能避免中间人攻击。

为了应对中间人攻击,我们需要使用数字证书。

数字证书

从上面我们看出,客户端和服务器可能遭受中间人攻击的原因是:客户端想从服务器获取公钥时,却拿到了一个冒充的公钥。

为了解决这个问题,服务器可以将公钥放在数字证书中,然后传给客户端,客户端认证证书后,则信任这把公钥,否则停止通信。接下来开始解释数字证书的工作原理。

数字证书中一般带有一些服务器的相关信息,通信所需的相关信息,以及服务器的公钥,然后将这些信息生成数字摘要,再使用证书颁发机构的私钥加密生成数字签名。最终,由前面提到的服务器的相关信息,通信所需的相关信息,服务器的公钥再加上生成的数字签名,做成了数字证书。

下图是来自《HTTP权威指南》一书对数字证书结构的图解

证书结构

全球的证书并还未有一个同意的标准规定证书中需要包含哪些字段,但是目前大多人使用的规范是X.509 V3.没错,这个经常看到的很玄的名字仅仅代表一种证书结构。

客户单获得这个服务器的数字证书后,开始进行验证,验证过程就像前面提到的数字签名认证流程类似,不过,这回客户端使用了一把证书颁发机构的公钥,客户端一般都会预先安装很多权威的证书颁发机构的证书,其中就带有这时派上用场的公钥。

下图是来自《HTTP权威指南》一书对数字证书认证流程的图解

证书认证流程

到此为止,介绍完了数字签名和数字证书的概念,如果你还对这两个概念有所疑问,推荐一篇阮一峰的文章,可以辅助理解这两个概念

http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html

四、HTTPS握手阶段

前面讲过,HTTPS的重点在握手阶段,前面讲到的数字签名和数字证书等概念也是在握手阶段发挥作用,握手阶段一共有四步,接下来大概写一下HTTPS的四次握手

  • 客户端发送一个随机数A,以及描述自己所支持的加密算法
  • 服务器收到客户端信息后,生成随机数B,并发送数字证书(证书中带有公钥),并使用HASH生成数字签名,供客户端校验
  • 客户端收到服务器信息后,验证签名和证书,拿下公钥,生成随机数C,使用公钥加密随机数C,发送给服务器(并进行了数字签名,供服务器校验)
  • 服务器收到客户端信息后,用私钥解密获得随机数C,发送握手结束通知给客户端(并进行了数字签名,供客户端校验)

通过以上四次握手,客户端和服务器都获得了3个随机数,接下来双方都使用这三个随机数生成一个对称密钥,双方接下来的通信就使用这两个相同的对称密钥进行对称加密。

到此为止,如果你对HTTPS的握手阶段理解还不够清楚,推荐两篇文章,依然是阮一峰的。

http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html

五、参考

近来在做一个关于Android中Camera的需求,此处做点简要记录。提供给大家参考,避免以后猜到这些坑。

Android中Camera主要分5.0以前还有5.0以后,从5.0开始Android支持Camera2,现在项目中这个模块只使用了Camera(你可以理解为Camera1)。

使用Camera基本的API我就不一一列举了,此处只讲经验积累的东西。下文将打开相机预览窗口称为Preview,拍照结果称为picture。

旋转方向、图片大小

首先大多相机打开的方向并不是正常的,需要我们进行处理,你可能最终需要旋转度数为degree(90或180或270)。
Picture返回的照片也可能不是摆正的,需要旋转上面提到的度数degree.

我们可以获得Preview照片进行一些处理,比如二维码扫描或者ROI(感兴趣区域)识别,而Preview也是需要旋转degree角度才摆正.

使用相机时,你可以获得Preview和Picture可以支持的大小,但是你最好不要随便选择一个,首先,Preview的宽高比例需要接近你的UI中给相机预览窗口的宽高比,否则会觉得相机中图片被拉高了或者压扁了,你只需要做到比例接近就够了。另外无论是Preview的大小和Picture的大小,都需要考虑内存的情况。另外拍出来的照片大小取决于Picture的分辨率。

YUV格式

Preview回调的图片byte[]数组,是YUV存储格式的图片信息,我也是第一次见到这个概念,它和RGB一样是用于存储图片的编码格式,它需要通过某种公式转换成RGB,它的优点在于存储空间小,主要原因大概是几个Y 可以共享同一个UV.关于YUV的详细原理这里不拓展。然后如果你在项目中只拿Preview的图片,那么获取图片时有YuvImage相关的API可以使用。如果你是需要将图片信息传到JNI层进行图片处理,那么可能就需要进行进一步转换,转成RGB格式。而这里我遇到一个性能问题,工作中发现每一张Preview图片处理边缘识别时间特别慢,但是打出native层Opencv处理图片逻辑的耗时又并不是很长,最终发现是在Java层,使用YuvImage将YUV转RGB时相当耗时,基本就比得上native识别边缘的时间,最终发现可以在native层Opencv也有接口支持转YUV的格式,而且效率快很多,基本解决了在低端机器上的效率问题。

聚焦

聚焦,关于聚焦的问题,我们经常使用的方法就是设置一个FOCUS_MODE,然后调用camera.autofocus的方法并且获取聚焦结果的回调,但是需要注意一点的是,并非所有FOCUS_MODE都是可以支持camera.autofocus方法的,比如FOCUS_MODE_CONTINUOUS_VIDEO,所以设置几种FOCUS_MODE时最好看看相关注释。比如以下这段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

/**
* Continuous auto focus mode intended for video recording. The camera
* continuously tries to focus. This is the best choice for video
* recording because the focus changes smoothly . Applications still can
* call {@link #takePicture(Camera.ShutterCallback,
* Camera.PictureCallback, Camera.PictureCallback)} in this mode but the
* subject may not be in focus. Auto focus starts when the parameter is
* set.
*
* <p>Since API level 14, applications can call {@link
* #autoFocus(AutoFocusCallback)} in this mode. The focus callback will
* immediately return with a boolean that indicates whether the focus is
* sharp or not. The focus position is locked after autoFocus call. If
* applications want to resume the continuous focus, cancelAutoFocus
* must be called. Restarting the preview will not resume the continuous
* autofocus. To stop continuous focus, applications should change the
* focus mode to other modes.
*
* @see #FOCUS_MODE_CONTINUOUS_PICTURE
**/

public static final String FOCUS_MODE_CONTINUOUS_VIDEO = "continuous-video";

另外有一个小技巧,就是在调用拍照时,先调用camera.autoFocus然后在成功聚焦回调里拍照。当然,对于聚焦失败你应该设置最大的失败次数限制。

关于crash

camera.autofocus聚焦还有camera.takePicture这两个API都不能持续重复调用,否则会crash,执行完一次聚焦才能执行下一次聚焦,执行完一次拍照才能执行下一次拍照,所以你最好做一个保护,另外,根据官方文档解释,camera.takePicture返回拍照数据后,preview是自动被stop了的,所以你需要重新start一次,否则下次拍照会crash。然而,我发现在三星上拍完照preview并没有自动stop。

setFocusArea

另外关于setFocusArea,这个模块的概念在于理解其特殊的坐标系,见下图

FocusArea

这幅图网上到处都有,其实是来自Android开发官网,官网如是解释:左上角永远是(-1000,-1000),右下角永远是(1000,1000),这个坐标系最开始看起来有点奇怪,但是仔细一看只是X坐标Y坐标都是偏移缩小了1000而已,另外将X坐标Y坐标的最大值缩放为2000的长度。

那么问题来了,按照上面所说,你的镜头旋转过了,那么你此处的坐标系是否也要旋转呢,答案是不需要,直接拿旋转后的视图计算即可。亲测有效。

关于camera的生命周期

这里主要关于什么时候释放相机资源的问题,涉及到切后台,锁屏的情况,具体可以参考QQ空间的这片技术文章,文章最后讲到了这一点。

QQ空间开发团队-Android相机开发那些坑

主要技巧在于:SurfaceView设置INVISIBLE时会自动释放相机资源。

preview到picture的坐标转换

如果此时你的自定义相机有这样一个需求,在Preview中某个坐标比如(100,100)处添加一个红点,那么,你如何在最终拍照的picture上同一个地方还原放上红点呢?而且可能此时你的Preview分辨率是1080 1440,picture分辨率是3000 4000.

问题就在于如何在Preview和picture之间做坐标转换,对于原生的Android系统相机,是比较好处理的,你可以参考这个Stackoverflow的回答

http://stackoverflow.com/a/18159351/1290235.

上面这个回答其中讲到,所有可选的Preview和picture大小都是基于最大的 picture 而来, 而且是在Matrix.ScaleToFit.CENTER的规则上缩放而来,这种缩放的规则就是,小的分辨率往大的分辨率放大,小的分辨率至少会有宽或者高和大的分辨率贴合,只要宽或者高其中一者贴合即可,那么你就可以按照上面链接中讲到的方法进行坐标转换。

那么问题来了,在开发过程中,我遇到华为等机器上并不符合以上规则,也就是Preview放大后,和最大的picture对比之下,宽和高都不贴合。这种情况就无法换算了。那么怎么解决这个问题呢?

最终解决方法是这样:在寻找Preview和picture大小时,要求二者的宽/高比例相等,这样就可以解决坐标转换的问题了。

理解图片格式 YUV

可参考以下两篇文章

http://www.cnblogs.com/azraelly/archive/2013/01/01/2841269.html

http://blog.csdn.net/beyond_cn/article/details/12998247

以下公式你可能得看上面的文章后才看得懂。

YUV的存储格式比较特别,一般都是多个Y公用UV,常见如下

I420: YYYYYYYY UU VV => YUV420P
YV12: YYYYYYYY VV UU => YUV420P
NV12: YYYYYYYY UVUV => YUV420SP
NV21: YYYYYYYY VUVU => YUV420SP