浅谈SFTP+OpenPGP的系统对接

毕业工作以来,接触的好几个项目都涉及到数据加密和传输加密的处理,这些项目的共同点是需要与异构系统对接,并且要与对方做敏感度高的数据交互。在读研期间恰好稍微接触过一些信息安全的知识普及,没想到一来公司就需要用上这部分的知识。在这里写一写经验总结,也分享一下相关的知识。

我们通常会在传输和数据两个层面做安全处理。

  • 传输,通常我们最熟悉的就是SSH/SFTP协议和基于SSL的Https协议。

  • 数据,数据是指数据在结束传输后落地在[文件系统/数据库]时的展现形式。数据的安全一般需要使用一些加密算法对数据做[签名验证]和[加密解密]两个措施来保证。OpenPGP是一套用于数据签名,加密和解密的开源规范,用的会比较多。在之前的项目实施中,大部分境外的对接用的就是SFTP+OpenPGP。

接下来首先会用一小章节介绍一下密码学的基本概念,后面的实施部分会频繁地提起到这些词汇,如果对这部分熟悉的同学直接跳过就可以啦,之后会具体介绍SFTP以及PGP的相关概念原理以及操作,最后介绍项目的实施。

密码学基本概念

在聊SFTP和OpenPGP之前,我们先用通俗的语言解释一下密码学里面一些常用的专业名词都是什么意思。

  • 加密:把人看得懂的明文转换成人看不懂的密文,比如把“i love u”转换成“iP0mlsGW5Kqr1Axy0/PVL/r5lHSBookm”。

  • 解密:把密文翻译回明文。

  • 签名:把明文数据加密成密文,这段密文就被称为原数据的签名。传输的时候把明文密文一并传输出去,签名用于发现数据被篡改。

  • 验签:用接收到的明文内容再次生成签名,对比传输过来的签名看是否一致。

  • 密钥:加解密过程中所需要的钥匙(一串字节码)。密钥是整个加密解密过程的关键。密码学中的算法和密文都不需要保护,攻击者即使知道密文和加密过程使用的是什么算法,只要攻击者没有密钥,那么明文就可以认为是安全的。

  • 算法:用于加密解密的密码学算法,一般来说分为三种 1. 对称加密 2. 非对称加密 3. 哈希(散列算法)
    算法重点元素对比

哈希主要用于签名以及验签,该算法不可逆,也就是只能加密不能解密。接下来主要说一下个人对对称加密算法非对称加密算法的理解。

从图中可以看到,对称加密算法要比非对称加密算法快,那为什么还要有非对称加密算法?这里的主要原因是密钥的保护问题。 对称加密算法加密和解密用的是同一个密钥,非对称加密算法加密用的是私钥,解密用的是公钥。也就是,使用对称加密算法时,密钥一定要为发送方和接收方双方可知,这里就一定涉及传输密钥的问题,并且当密钥被截获时,攻击者可以随意加解密。那传输过程中怎么保护好密钥?无解。因此非对称加密算法就诞生了。

使用非对称加密算法,首先要生成一对密钥,其中一个只能自己知道,因此命名为“私钥”。另一个告诉接收方,叫“公钥”。加密时,用公钥加密,私钥解密。这样攻击者只能知道公钥不能知道私钥,无法完成数据的破解。因为私钥不需要做任何的传输动作,因此可靠性高。

但是由于非对称加密算法速度慢,因此一般只用来加密少量数据,实际操作一般会结合两种加密算法来使用
1. 由对称加密算法加密文本数据;
2. 使用非对称加密算法和公钥加密对称加密的密钥,把加密后的密钥和密文一并发送给接收方;
3. 接收方使用私钥解密对称加密的密钥;
4. 最后用对称加密算法和3解密后的密钥解密文本数据。

SFTP安全文件传输协议

SFTP协议使用SSH连接通道的基础上完成文件的传输。SSH协议基于双方约定连接,使用一系列的加密手段保证了连接通道的私密安全。 SFTP 连接有两种方式,登陆密码认证和密钥认证。项目开始前,首先需要找对方确认使用的是哪种连接方式。

  1. 密码认证 sftp -P [port] [username]@host

  2. 密钥认证(以对方为服务端为例): 首先需要生成一对连接密钥

    生成密钥过程.png
    生成的密钥对(id_rsa.pub/id_rsa)会保存在~/.ssh/文件夹下面,把公钥(id_rsa.pub)提供给对方。对方拿到公钥后会保存在authorized_keys文件夹下面。最后就可以使用命令登陆 sftp -P [port] -i [private key] [username@host] (-P 参数指目标服务器端口,一般是22; -i 是私钥的绝对路径,也就是上述的id_rsa)

登陆完成后就可以直接用put [local filePath]get [remote filePath]来上传/下载文件了。

使用Java连接可以com.jcraft.jsch包来做连接,公钥认证需要填加属性

Jsch jsch = new Jsch();
jsch.addIdentity(privatekey,passPhase);

其余的可参考https://www.cnblogs.com/zjwwljty/p/8548468.html

SFTP公钥登陆流程

另外这里提一下sftp公钥登陆流程,实际上也包含了一个非对称加密解密的过程。

  1. 当客户端需要使用公钥登录时,向服务器发送一个公钥的id;

  2. 服务器接收到id后验证公钥在服务端是否存在,如果不存在登陆失败,如果存在进行下一步;

  3. 服务器根据id获取公钥的内容,验证公钥是否可用,如果不可用登陆失败,如果可用进行下一步;

  4. 服务端随机生成一段字符串,并用公钥进行加密,把加密后的结果返回给客户端;(这里便是一个非对称加密)

  5. 客户端收到加密的字符串,使用私钥进行解密,把解密得到的字符串连同session id一起使用md5做哈希,把哈希后的结果返回给服务端;

  6. 服务端使用同样的md5计算并把计算结果与客户端返回的值做对比,验证是否登陆通过。

OpenPGP数据加密协议

上面第一章说了很多密码学的基本概念,并且在最后指出了加密的常规操作是对称加密结合非对称加密共同完成的。这一章的OpenPGP讲的就是一个常用的加密标准,如何统一化一个复合的加密流程使得发收双方能够减少加密流程的制定工作从而进行快速地做数据对接。

我们选定需要的对称及非对称加密算法,一次签名+加密的OpenPGP流程大致如图所示(使用OpenPGP签名(SHA-256)及加密(RSA-4096,AES-256))。

  1. 将输入的数据压缩,这一步可以模糊简单的数据规律同时压缩数据量

  2. 使用我方私钥(结合私钥密码)签名数据并生成签名块

  3. 生成对称加密密钥

  4. 使用对称加密加密压缩数据得到密文

  5. 使用对方公钥加密对称密钥

  6. 将签名块,密文块以及加密后的对称密钥组成最终PGP块


    OpenPGP加密流程.png

项目实施

当时我接到需要跟合作伙伴做签名加解密的对接任务时,无论是技术上还是流程上还是踩了不少坑,这些踩下的坑并不是说会对项目造成类似于停滞不前的严重影响,但是维护的时候就嘿嘿了。

基本上,当和对方确认了安全手段是SFTP和OPENPGP时(其他的暂时没遇到或者不值一提或者不允许提),我梳理了以下的实施流程(自己是客户端并且语言使用JAVA)

  1. 与对方确定SFTP登陆方式(密码or公钥),假设是公钥,需要让客户端方生成一对密钥,并共享公钥(生成方式在文章的SFTP介绍中有提,这里就不再赘述了)

  2. 与对方线下确认服务器的指纹是否正确(打电话,邮件或者见面问都行)。在[home_dir]/.ssh/目录下有一个knownhosts文件,与对方服务器建立ssh/sftp连接时,该文件需要记录有对方服务器的指纹信息,若ip和指纹信息不匹配,该连接将会被拒绝,这个机制是ssh用来防止中间人攻击做出的保护措施。

  3. 借助com.jcraft.jsch编写一个SFTP客户端并部署做测试对接(温馨提示:记得需要在客户端上做些幂等,超时重连等处理)

  4. 做完连接测试接下来做加密系列操作。一般来说,我们在使用OpenPGP标准时不会从头实现一遍这一套规则。我们会使用GnuPG来做密钥管理,使用bouncycastle来实现openpgp加密规范。

  5. 首先mac终端上使用命令brew install gnupg 安装GPG工具(GnuPG简称GPG)

  6. 因为双方都需要做加密,需要跟对方交换密钥,假设双方分别叫A和B,

    • A这边用B的公钥加密,用A的私钥解密;

    • 同样,B这边用A的公钥加密,B的私钥解密

    首先生成自己的一对密钥(不要嫌麻烦,这些是必要的)

    gpg --full-generate-key

    生成密钥.png

解释一下上面一些概念,估计大伙看了会有点疑问

  • E5834DCB37EED9CD59F2D6673B7411F1C006EA29是密钥的id,这个id可以用来编辑密钥,可以给别人导入你的密钥;

  • 密钥有限期,顾名思义,到期后过期密钥需要替换或者通过gpg --edit-key [Key ID]命令延期(记得管理好自己的密钥期限)

  • 密钥类型的1,2是带有子密钥结构的,一般默认会选第一种,主密钥和子密钥都使用RSA算法,其中主密钥做签名认证,子密钥做加密。但有些企业会要求无子密钥,那就只能选3,4了。我们可以看看刚刚生产的1类型(RSA和RSA)密钥。

    gpg --edit-key [Key ID]

编辑密钥.png

SC代表签名和认证,E代表加密,ssb代表secret sub key,sec代表secret key。

好了,密钥生成完了以后,可以使用两种方式来把公钥给对方

6.1. 使用命令gpg --import [key id]来让对方导入我们生成的公钥

6.2. 使用命令gpg --armor --output [public key name] --export [KEY ID]把公钥文件导出来(--armor参数是使文件以ASCII码形式展现,如果忽略这个参数生成的是2进制文件),然后发给对方

这两种方式都要注意的是人工验证KEY ID是否是真的是对方,最好见面问,不能见面也可以打电话。(自己思考为啥要这个动作)。

最后用同样的方法把对方生成的密钥导入就结束密钥交换流程了。

  1. 最后,得到密钥了,再通过bouncy castle编写一个加密,解密,签名,认证的算法,然后跟对方测试验证就完成了。

    这里的bouncy castle使用方法不详细讲,代码也不打算公开先,网上可以搜到demo;因为这个包文档很不全,需要很大程度低靠自己理解(可参考上面OPENPGP的原理流程来辅助理解),之后有机会会出一个专门的篇幅来解读bouncy castle的openpgp实现原理。

你可能感兴趣的:(浅谈SFTP+OpenPGP的系统对接)