浅谈加密

什么是加密?

数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码,通常称为“密文”,使其只能在输入相应的密钥之后才能显示出本来内容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的。该过程的逆过程为解密,即将该编码信息转化为其原来数据的过程。

分类

计算机安全中使用的加密主要有:

  • 对称加密(DES、3DES、DESX、Blowfish、IDEA、RC4、RC5、RC6和AES)
  • 非对称式加密(RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、椭圆曲线、DSA(数字签名用))
  • “哈希函数”(本质上说不是加密算法,因为无法解密,准确来说是一种签名算法)

1. 密钥

在密码学中,密钥(key,又常称金钥)是指某个用来完成加密、解密、完整性验证等密码学应用的秘密信息。

2. 对称加密

即信息的发送方和接收方使用同一个密钥去加密和解密数据。它的最大优势是加/解密速度快,适合于对大数据量进行加密,但密钥管理困难。常见的对称加密算法有:DES算法,3DES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法以及正在普及的AES算法

2.1 加密过程
浅谈加密_第1张图片
对称加密.png
2.2 存在的问题
  • 密钥难于管理。假设两个用户需要使用对称加密方法加密然后交换数据,则用户最少需要2个密钥并交换使用,如果企业内用户有n个,则整个企业共需要n×(n-1) 个密钥,密钥的生成和分发将成为企业信息部门的恶梦。对称加密算法的安全性取决于加密密钥的保存情况,但要求企业中每一个持有密钥的人都保守秘密是不可能的,他们通常会有意无意的把密钥泄漏出去——如果一个用户使用的密钥被入侵者所获得,入侵者便可以读取该用户密钥加密的所有文档,如果整个企业共用一个加密密钥,那整个企业文档的保密性便无从谈起。
  • 无法解决消息确认的问题。不能实现数字签名。
  • 通信双方密钥交换问题。
2.3 具体实现

在以上对称算法中使用较多的DES正在被安全性更高的AES(Advanced Encryption Standard)算法所替代,一般的开发平台会集成AES加密(AES分组加密特性)。
AES支持三种长度的密钥:128位,192位,256位。平时我们所说的AES128,AES192,AES256就是指AES算法对不同长度密钥的使用。从安全性来看,AES256安全性最高,从性能来看AES128性能最高。本质就是它们的加密处理轮数不同。如果大家想更深层次的了解AES可以看这两篇篇文章:什么是AES算法?、AES算法底层原理。
在iOS中可以使用CommonCrypto. framework进行AES加密、解密。具体实例可以看文章末尾提供的Demo。

CCCryptorStatus CCCrypt(
    CCOperation op,         /* kCCEncrypt, etc. 加密或者解密*/ 
    CCAlgorithm alg,        /* kCCAlgorithmAES128, etc. 具体算法AES、DES...*/
    CCOptions options,      /* kCCOptionPKCS7Padding, etc. 填充方式ECB、CBC(默认) */
    const void *key,
    size_t keyLength,
    const void *iv,         /* optional initialization vector 初始向量*/
    const void *dataIn,     /* optional per op and alg */
    size_t dataInLength,
    void *dataOut,          /* data RETURNED here */
    size_t dataOutAvailable,
    size_t *dataOutMoved)
    __OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
  • 填充方式:
    AES算法在对明文加密的时候,并不是把整个明文一股脑加密成一整段密文,而是把明文拆分成一个个独立的明文块,每一个铭文块长度128bit。
    这些明文块经过AES加密器的复杂处理,生成一个个独立的密文块,这些密文块拼接在一起,就是最终的AES加密结果。
    但是这里涉及到一个问题:
    假如一段明文长度是196bit,如果按每128bit一个明文块来拆分的话,第二个明文块只有64bit,不足128bit。这时候怎么办呢?就需要对明文块进行填充(Padding)。
    kCCOptionPKCS7Padding:的填充模式是不足补0,CCOptions只提供了这一种方式。据说是兼容PKCS5Padding模式的。
    PKCS5Padding:如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。其他平台会有的填充方式。
    比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}
    ISO10126Padding如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字节,最后一个字符值等于缺少的字符数,其他字符填充随机数。

  • AES工作模式(只说iOS中有的两种):
    ECB(Electronic Codebook Book)是最简单的工作模式,在该模式下,每一个明文块的加密都是完全独立,互不干涉的。
    优点: 1.简单、 2.有利于并行计算。
    缺点同样也很明显:相同的明文块经过加密会变成相同的密文块,因此安全性较差。
    CBC模式(Cipher Block Chaining)引入了一个新的概念:初始向量IV(Initialization Vector)
    IV是做什么用的呢?它的作用和MD5的“加盐”有些类似,目的是防止同样的明文块始终加密成同样的密文块。
    CBC模式在每一个明文块加密前会让明文块和一个值先做异或操作。IV作为初始化变量,参与第一个明文块的异或,后续的每一个明文块和它前一个明文块所加密出的密文块相异或。
    优点:安全性更高。
    缺点:1.无法并行计算,性能上不如ECB、2.引入初始化向量IV,增加复杂度。

3. 非对称加密

加密和解密用的钥匙不同,通常一个是公开的,称为公钥,另一个保密,称为私钥。信息发送者用公开密钥去加密,而信息接收者则用私用密钥去解密。公钥机制灵活,但加密和解密速度却比对称密钥加密慢得多。基于公开密钥加密的特性,它还提供数字签名的功能,使电子文件可以得到如同在纸本文件上亲笔签署的效果。公开密钥基础建设通过信任数字证书认证机构的根证书、及其使用公开密钥加密作数字签名核发的公开密钥认证,形成信任链架构,已在TLS实现并在万维网的HTTP以HTTPS、在电子邮件的SMTP以STARTTLS引入。非对称加密通常用于建立共享通信信道。由于非对称加密在计算上是昂贵的,所以两个端点通常使用非对称加密来交换对称密钥, 然后使用更快的对称加密算法来加密和解密实际数据。
常见的非对称加密方式有:RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、椭圆曲线、DSA(数字签名用)

3.1 加密过程

在数学上,让我们使用典型的爱丽丝与鲍伯假设来解释:

浅谈加密_第2张图片
非对称加密过程.png
  • Alice使用其中一种公钥算法来生成一对加密密钥:一个她保密的私钥和一个公钥。她还准备发送给Bob的消息。
  • Alice将公钥发送给Bob,未加密。因为她的私钥不能从公钥中推导出来,所以这样做不会以任何方式危害她的私钥。
  • Alice就可以使用私钥对她的消息(或消息的任何部分)进行加密,并将其发送给Bob。
  • Bob用Alice的公钥解密消息。这就证明这个消息一定是来自Alice,因为只有她拥有用于加密的私钥。
  • Bob使用Alice的公钥将他的消息加密并发送给Alice。该信息是安全的,因为即使被截获,除Alice之外,没有人拥有解密它所需的私钥。
  • Alice用她的私钥解密消息。

复杂的情况:Eve想欺骗Bob,他偷偷使用了Bob的电脑,用自己的公钥换走了Alice的公钥。此时,Bob实际拥有的是Eve的公钥,但是还以为这是Alice的公钥。因此,Eve就可以冒充Alice,写信给Bob,让Bob用假的Alice公钥进行解密。

3.1 存在的问题
  • 因为公钥是对外开放的,所以如果一个人是我的公钥加密数据发给我,我无法确认是谁发送的。(数字签名)
  • 我用私钥加密数据,任何知道我公钥的人都能解密我的数据。
  • 实现速度慢,不适合通信负荷较重的情况。
3.2 具体实现

在非对称加密中比较常用的是RAS加密算法, RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。1987年7月首次在美国公布,当时他们三人都在麻省理工学院工作实习。RSA就是他们三人姓氏开头字母拼在一起组成的。RSA是目前最有影响力和最常用的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
在iOS中可以使用可实现些RSA的加密解密。
对于想了解RSA实现原理的同学可以看着两篇文章:RSA算法原理(一)、RSA算法原理(二)。

// 生成公钥和私钥的方法
OSStatus SecKeyGeneratePair(CFDictionaryRef parameters,
    SecKeyRef * _Nullable CF_RETURNS_RETAINED publicKey, SecKeyRef * _Nullable CF_RETURNS_RETAINED privateKey)
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);

// 加密
OSStatus SecKeyEncrypt(
                       SecKeyRef           key,
                       SecPadding          padding,
                       const uint8_t        *plainText,
                       size_t              plainTextLen,
                       uint8_t             *cipherText,
                       size_t              *cipherTextLen)
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);

// 解密
OSStatus SecKeyDecrypt(
                       SecKeyRef           key,                /* Private key */
                       SecPadding          padding,         /* kSecPaddingNone,
                                                             kSecPaddingPKCS1,
                                                             kSecPaddingOAEP */
                       const uint8_t       *cipherText,
                       size_t              cipherTextLen,       /* length of cipherText */
                       uint8_t             *plainText,  
                       size_t              *plainTextLen)       /* IN/OUT */
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);

但是这个framework的api只支持从标准证书文件(cer, crt)中读取公钥。有时java后台给我们的公钥只是一个pem格式的公钥文件,其中保存的公钥信息是base64加密的字符串,这个用sercurity就不能读取了。如:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNiCH4fHkohPMNqQJPGZZKZc39
4AFNAf6ddAFOQ5nFXAjrDQYKsZV90bsnFLftOA0mFapuu8xKH5d0C60lphnYPBh0
LwwOJEByrbXk4QxjTIXIYmvHTyvcL4YhG02YyYmdJ4MIdjvRaSvyBscpAUciN0RN
YkFjT6X2H59jZ4MriQIDAQAB
-----END PUBLIC KEY-----

这种使用sercurity.framework就不行了。但是我们可以使用OpenSSL的API来实现此类的加解密。

// 从接口请求公钥,并保存到沙盒
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];           
NSString *filePath = [docDir stringByAppendingPathComponent:@"public_key.pem"];            
[key writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:NULL];

// 使用OpenSSL读取密钥
- (BOOL)importRSAKeyWithType:(KeyType)type {
    FILE *file;
    NSString *keyName = type == KeyTypePublic ? @"public_key" : @"private_key";
    NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *keyPath = [docDir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.pem", keyName]];
    
    file = fopen([keyPath UTF8String], "rb");
    
    if (NULL != file) {
        if (type == KeyTypePublic) {
            _rsa = PEM_read_RSA_PUBKEY(file, NULL, NULL, NULL);
            assert(_rsa != NULL);
        } else {
            _rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);
            assert(_rsa != NULL);
        }
        
        fclose(file);
        
        return (_rsa != NULL) ? YES : NO;
    }
    
    return NO;
}

OpenSSL是什么?OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其他的目的使用。

OS X包含一个到OpenSSL开源加密工具包的低级命令行界面; 此界面在iOS上不可用。此外,尽管OpenSSL在开源社区中经常使用,但是它并没有提供稳定的API。出于这个原因,OpenSSL的编程接口在OS X中被弃用,并且不在iOS中提供。强烈建议不要使用Apple提供的OpenSSL程序库。为确保兼容性,如果您的应用程序依赖于OpenSSL,则应该自己编译并将已知版本的OpenSSL静态链接到您的应用程序中。

在MAC OS上使用OpenSSL生成密钥对:

  • genrsa -out rsa_private_key.pem 1024 (生成私钥,服务端使用的) 这条命令让OpenSSL随机生成了一份私钥,加密长度是1024位。
  • rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout (生成公钥)
  • pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt

4. 哈希函数

哈希函数也称为散列函数,它能够对不同长度的输入信息,产生固定长度的输出。这种固定长度的输出称之为原消息的散列或者消息摘要,消息摘要长度固定且比原始信息小得多,一般情况下,消息摘要是不可逆的。常见的摘要算法有CRC系列,MD系列,SHA,HMAC系列以及以及PRIPEMD、PANAMA、TIGER等。
在以上的摘要算法中,MD5(Message Digest Algorithm 5)是比较常用的在iOS的开发中。
比较常用的两个场景,一个是将用户的密码直接MD5加密,传输和存储都使用这个MD5值,保证用户的密码不会泄露。再一种是后台和客户端通信时,通过对指定的数据进行MD5加密进行身份验证。
使用MD5做以上两种情况的加密主要是因为MD5具有以下特性:

  • 压缩性:输入任意长度的信息,经过摘要处理,输出为128位的信息。
  • 容易计算:从原数据计算出MD5值很容易。
  • 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
  • 强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

根据MD5的特点,MD5一般用于一致性验证,数字签名,安全访问限制,安全访问认证。一般的开发平台中都会集成MD5加密,在iOS的开发中,导入CommonCrypto/CommonDigest.h头文件即可进行MD5加密的操作。但是普通的MD5加密,现在已经有软件可以进行破解,所有我们想出来一种方法,使MD5这种加密更加复杂,更难被破解。这种方式就是加盐。原理:简而言之,MD5把128bit的信息摘要分成A,B,C,D四段(Words),每段32bit,在循环过程中交替运算A,B,C,D,最终组成128bit的摘要。想更深层了解MD5的可以看这两篇文章:什么是MD5算法?、如何破解MD5算法?

SHA系列算法,和MD5算法类似,SHA也是一种生成信息摘要的算法。SHA算法的分类:SHA-1,SHA-2(SHA-224,SHA-256,SHA-384,SHA-512)SHA-3于2015年问世。

  • SHA-1算法可以从明文生成160bit的信息摘要,比MD5的摘要长度多了32bit(不同明文的碰撞几率降低了2^32次方倍),但是性能比MD5略低。(2005年被破解)原理:就是把160bit的信息摘要分成A,B,C,D,E五段。
  • SHA-2算法,SHA-256:可以生成256bit的信息摘要,SHA-224:SHA-256的“阉割版”,可以生成224bit的信息摘要。

为什么有这么多版本?为了使用不同的应用场景,从而对安全,性能,空间等因素做出权衡。比如说我的需求仅仅是验证数据完整性,使用SHA-512显然是浪费了。

HMAC-MD5算法就是采用密钥加密+MD5信息摘要的方式形成新的密文。主要是为了能让人对对方身份正确性和消息有效性进行验证,与消息摘要的最大不同,就是有签名密钥。

5.数字签名、数字证书

数字签名(又称公钥数字签名,英语:Digital Signature)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证,但法条中的电子签章与数字签名,代表之意义并不相同,电子签章用以辨识及确认电子文件签署人身份、资格及电子文件真伪者。而数字签名则是以数学算法或其他方式运算对其加密,才形成电子签章,意即使用数字签名才创造出电子签章。数字签名可以理解为摘要算法与非对称加密的综合使用。
关于数字签名、数字证书这篇文章讲的很清楚在这里就不在陈述了。传送门:数字签名是什么?

其他

登录:
现在,大部分App的接口都采用RESTful架构,RESTFul最重要的一个设计原则就是,客户端与服务器的交互在请求之间是无状态的,也就是说,当涉及到用户状态时,每次请求都要带上身份验证信息。实现上,大部分都采用token的认证方式,
一般流程是:

  • 用户用密码登录成功后,服务器返回token给客户端;
  • 客户端将token保存在本地,发起后续的相关请求时,将token发回给服务器;
  • 服务器检查token的有效性,有效则返回数据,若无效,分两种情况:
  1. token错误,这时需要用户重新登录,获取正确的token
  2. token过期,这时客户端需要再发起一次认证请求,获取新的token

然而,此种验证方式存在一个安全性问题:当登录接口被劫持时,黑客就获取到了用户密码和token,后续则可以对该用户做任何事情了。用户只有修改密码才能夺回控制权。
如何优化呢?
第一种解决方案:采用HTTPS。HTTPS在HTTP的基础上添加了SSL安全协议,自动对数据进行了压缩加密,在一定程序可以防止监听、防止劫持、防止重发,安全性可以提高很多。不过,SSL也不是绝对安全的,也存在被劫持的可能。另外,服务器对HTTPS的配置相对有点复杂,还需要到CA申请证书,而且一般还是收费的。而且,HTTPS效率也比较低。一般,只有安全要求比较高的系统才会采用HTTPS,比如银行。而大部分对安全要求没那么高的App还是采用HTTP的方式。
第二种:我们目前的做法是给每个接口都添加签名。给客户端分配一个密钥,每次请求接口时,将密钥和所有参数组合成源串,根据签名算法生成签名值,发送请求时将签名一起发送给服务器验证。

另外,现在越来越多App取消了密码登录,而采用手机号+短信验证码的登录方式,我在当前的项目中也采用了这种登录方式。这种登录方式有几种好处:

  1. 不需要注册,不需要修改密码,也不需要因为忘记密码而重置密码的操作了;
  2. 用户不再需要记住密码了,也不怕密码泄露的问题了;
    相对于密码登录其安全性明显提高了。

Base64编码:
所谓Base64,就是说选出64个字符----小写字母a-z、大写字母A-Z、数字0-9、符号"+"、"/"(再加上作为垫字的"=",实际上是65个字符)----作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。并不是加密,只是一种编码方式。Base64很直观的目的就是让二进制文件转化为64个基本的ASCII码字符。又来传送门:Base64笔记

隐写术:


浅谈加密_第3张图片
隐写术.jpg

Demo:点点点我


结束

你可能感兴趣的:(浅谈加密)