加密算法 - DES,AES, RSA, MD5, SHA, HMAC, base64

在介绍加密算法之前, 先介绍一下 base64:

0. base64

  • Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3
  • base64 编码构成 : a-z, A-Z, 0-9, +/

Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。可查看RFC2045~RFC2049,上面有MIME的详细规范。
Base64编码是从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息。例如,在Java Persistence系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bit的UUID)编码为一个字符串,用作HTTP表单和HTTP GET URL中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码具有不可读性,需要解码后才能阅读。


转换实例:
转换前 11111111, 11111111, 11111111 (二进制)
转换后 00111111, 00111111, 00111111, 00111111 (二进制)
上面的三个字节是原文,下面的四个字节是转换后的Base64编码,其前两位均为0。
转换后,我们用一个码表来得到我们想要的字符串(也就是最终的Base64编码),这个表是这样的:(摘自RFC2045)

  • Objective-C 中使用 base64编码解码:
    - (NSString *)base64:(NSData *)data { return [data base64EncodedStringWithOptions:0]; }
    - (NSData *)base64:(NSString *)string { return [[NSData alloc] initWithBase64EncodedString:string options:0]; }

1. 哈希(散列)算法

  • 算法公开;
  • 对不同数据加密结构是定长的! 32位字符!!
  • 加密后的数据是不可逆的,
  • MD5, SHA, HMAC

1.1 MD5

MD5Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3MD4

  • MD5算法具有以下特点:

    • 1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。
    • 2、容易计算:从原数据计算出MD5值很容易。
    • 3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
    • 4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。除了MD5以外,其中比较有名的还有sha-1RIPEMD以及Haval等。

  • 原理:
    对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

  • 使用: 通常我们习惯对 MD5 加密的数据进行 base64编码后输出.

    + (NSData *)md5WithData:(NSData *)hashData {
        unsigned char *digest;
        digest = malloc(CC_MD5_DIGEST_LENGTH);
    
        CC_MD5([hashData bytes], (CC_LONG)[hashData length], digest);
    
        NSData *md5Data = [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH];
        free(digest); 
        return md5Data;
    } 
    

1.2 SHA

安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。

  • SHA家族的五个算法,分别是SHA-1SHA-224SHA-256SHA-384,和SHA-512,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)发布;是美国的政府标准。后四者有时并称为SHA-2SHA-1在许多安全协定中广为使用,包括TLSSSLPGPSSHS/MIMEIPsec,曾被视为是MD5(更早之前被广为使用的杂凑函数)的后继者。但SHA-1的安全性如今被密码学家严重质疑;虽然至今尚未出现对SHA-2有效的攻击,它的算法跟SHA-1基本上仍然相似;因此有些人开始发展其他替代的杂凑算法。

  • 应用:
    SHA-1, SHA-224, SHA-256, SHA-384 和 SHA-512都被需要安全杂凑算法的美国联邦政府所应用,他们也使用其他的密码算法和协定来保护敏感的未保密资料。FIPS PUB 180-1也鼓励私人或商业组织使用SHA-1 加密。Fritz-chip将很可能使用SHA-1 杂凑函数来实现个人电脑上的数位版权管理。
    首先推动安全杂凑算法出版的是已合并的数位签章标准。
    SHA杂凑函数已被做为SHACAL分组密码算法的基础。

     + (NSData *)sha1WithData:(NSData *)hashData
     {
        unsigned char *digest;
        digest = malloc(CC_SHA1_DIGEST_LENGTH);
    
        CC_SHA1([hashData bytes], (CC_LONG)[hashData length], digest);
        NSData *sha1Data = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
        free(digest);
    
        return sha1Data;
    }
    

1.3 HMAC

HMAC Hash-based Message Authentication Code, 哈希消息认证码.

  • HMAC是密钥相关的哈希运算消息认证码,HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。就是所谓的加盐 !

  • 运算作用

    • 验证TPM接受的授权数据和认证数据;
    • 确认TPM接受到的命令请求是已授权的请求,并且,命令在传送的过程中没有被改动过。

    定义HMAC需要一个加密用散列函数(表示为H,可以是MD5或者SHA-1)和一个密钥K。我们用B来表示数据块的字节数。(以上所提到的散列函数的分割数据块字长B=64),用L来表示散列函数的输出数据字节数(MD5中L=16,SHA-1中L=20)。鉴别密钥的长度可以是小于等于数据块字长的任何正整数值。应用程序中使用的密钥长度若是比B大,则首先用使用散列函数H作用于它,然后用H输出的L长度字符串作为在HMAC中实际使用的密钥。一般情况下,推荐的最小密钥K长度是L个字节

  • 算法表示

算法公式 : HMAC(K,M)=H(K⊕opad∣H(K⊕ipad∣M)) [1]
H 代表所采用的HASH算法(如SHA-256)
K 代表认证密码
Ko 代表HASH算法的密文
M 代表一个消息输入
B 代表H中所处理的块大小,这个大小是处理块大小,而不是输出hash的大小
如,SHA-1和SHA-256 B = 64
SHA-384和SHA-512 B = 128
L 表示hash的大小
Opad 用0x5c重复B次
Ipad 用0x36重复B次
Apad 用0x878FE1F3重复(L/4)次

  • HMAC的应用
    hmac主要应用在身份验证中,它的使用方法是这样的:

    • (1) 客户端发出登录请求(假设是浏览器的GET请求)
    • (2) 服务器返回一个随机值,并在会话中记录这个随机值
    • (3) 客户端将该随机值作为密钥,用户密码进行hmac运算,然后提交给服务器
    • (4) 服务器读取用户数据库中的用户密码和步骤2中发送的随机值做与客户端一样的hmac运算,然后与用户发送的结果比较,如果结果一致则验证用户合法.

    在这个过程中,可能遭到安全攻击的是服务器发送的随机值和用户发送的hmac结果,而对于截获了这两个值的黑客而言这两个值是没有意义的,绝无获取用户密码的可能性,随机值的引入使hmac只在当前会话中有效,大大增强了安全性和实用性。大多数的语言都实现了hmac算法,比如php的mhash、python的hmac.py、java的MessageDigest类,在web验证中使用hmac也是可行的,用js进行md5运算的速度也是比较快的。

认证流程
(1) 先由客户端向服务器发出一个验证请求。
(2) 服务器接到此请求后生成一个随机数并通过网络传输给客户端。
(3) 客户端将收到的随机数提供给ePass,由ePass使用该随机数与存储在ePass中的密钥进行HMAC-MD5运算并得到一个结果作为认证证据传给服务器(此为响应)。
(4) 与此同时,服务器也使用该随机数与存储在服务器数据库中的该客户密钥进行HMAC-MD5运算,如果服务器的运算结果与客户端传回的响应结果相同,则认为客户端是一个合法用户

+ (NSData *)hmacSha1WithData:(NSData *)hashData hmacKey:(NSString *)key
{
    unsigned char *digest;
    digest = malloc(CC_SHA1_DIGEST_LENGTH);
    const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];

    CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), [hashData bytes], [hashData length], digest);
    NSData *hmacData = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
    free(digest);
    cKey = nil;

    return hmacData;
}

2. 对称加密算法

  • 类型: DES(数据加密标准), 3DES, AES(高级密码标准)

  • ECB(Electronic Codebook,电码本)模式是分组密码的一种最基本的工作模式。在该模式下,待处理信息被分为大小合适的分组,然后分别对每一分组独立进行加密或解密处理。
    ECB模式作为一种基本工作模式,具有操作简单,易于实现的特点。同时由于其分组的独立性,利于实现并行处理,并且能很好地防止误差传播。 另一方面由于所有分组的加密方式一致,明文中的重复内容会在密文中有所体现,因此难以抵抗统计分析攻击。 因此,ECB模式一般只适用于小数据量的字符信息的安全性保护,例如密钥保护。

  • CBC(Cipher Block Chaining)/密文分组链接方式.
    它的实现机制使加密的各段数据之间有了联系。其实现的机理如下:

加密步骤如下:
1)首先将数据按照8个字节一组进行分组得到D1D2......Dn(若数据不是8的整数倍,用指定的PADDING数据补位)
2)第一组数据D1与初始化向量I异或后的结果进行DES加密得到第一组密文C1(初始化向量I为全零)
3)第二组数据D2与第一组的加密结果C1异或以后的结果进行DES加密,得到第二组密文C2
4)之后的数据以此类推,得到Cn
5)按顺序连为C1C2C3......Cn即为加密结果。

解密是加密的逆过程,步骤如下:
1)首先将数据按照8个字节一组进行分组得到C1C2C3......Cn
2)将第一组数据进行解密后与初始化向量I进行异或得到第一组明文D1(注意:一定是先解密再异或)
3)将第二组数据C2进行解密后与第一组密文数据进行异或得到第二组数据D2
4)之后依此类推,得到Dn
5)按顺序连为D1D2D3......Dn即为解密结果。

这里注意一点,解密的结果并不一定是我们原来的加密数据,可能还含有你补得位,一定要把补位去掉才是你的原来的数据。

  • CFB
  • OFB

2.1 DES加密

DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。需要注意的是,在某些文献中,作为算法的DES称为数据加密算法(Data Encryption Algorithm,DEA),已与作为标准的DES区分开来。

  • 算法入口参数

    DES算法的入口参数有三个:Key、Data、Mode。其中Key为7个字节共56位,是DES算法的工作密钥;Data为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密.

  • 基本原则

DES设计中使用了分组密码设计的两个原则:混淆( confusion)和扩散(diffusion),其目的是抗击敌手对密码系统的统计分析。混淆是使密文的统计特性与密钥的取值之间的关系尽可能复杂化,以使密钥和明文以及密文之间的依赖性对密码分析者来说是无法利用的。扩散的作用就是将每一位明文的影响尽可能迅速地作用到较多的输出密文位中,以便在大量的密文中消除明文的统计结构,并且使每一位密钥的影响尽可能迅速地扩展到较多的密文位中,以防对密钥进行逐段破译。

  • 算法步骤
    • 1)初始置换;
    • 2)逆置换.
  • 终端指令:
    • DES(ECB)加密
      $ echo -n hello | openssl enc -des-ecb -K 616263 -nosalt | base64

    • DES(CBC)加密
      $ echo -n hello | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt | base64

    • DES(ECB)解密
      $ echo -n HQr0Oij2kbo= | base64 -D | openssl enc -des-ecb -K 616263 -nosalt -d

    • DES(CBC)解密
      $ echo -n alvrvb3Gz88= | base64 -D | openssl enc -des-cbc -iv 0102030405060708 -K 616263 -nosalt -d

2.2 3DES

  • 3DES(即Triple DES)是DES向AES过渡的加密算法,它使用3条56位的密钥对数据进行三次加密。是DES的一个更安全的变形。它以DES为基本模块,通过组合分组方法设计出分组加密算法。比起最初的DES,3DES更为安全。

  • 该方法使用两个密钥,执行三次DES算法,加密的过程是加密-解密-加密,解密的过程是解密-加密-解密。
    3DES加密过程为:C=Ek3(Dk2(Ek1(P)))
    3DES解密过程为:P=Dk1(EK2(Dk3(C)))

  • 采用两个密钥进行三重加密的好处有:

    • 两个密钥合起来有效密钥长度有112bit,可以满足商业应用的需要,若采用总长为168bit的三个密钥,会产生不必要的开销。
    • 加密时采用加密-解密-加密,而不是加密-加密-加密的形式,这样有效的实现了与现有DES系统的向后兼容问题。因为当K1=K2时,三重DES的效果就和原来的DES一样,有助于逐渐推广三重DES。
    • 三重DES具有足够的安全性,目前还没有关于攻破3DES的报道。

2.3 AES加密

AES加密算法即密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院 (NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。该算法为比利时密码学家[Joan Daemen](javascript:linkredwin('Joan Daemen'); "Joan Daemen")和[Vincent Rijmen](javascript:linkredwin('Vincent Rijmen'); "Vincent Rijmen")所设计,结合两位作者的名字,以Rijndael之命名之,投稿高级加密标准的甄选流程。(Rijdael的发音近于 "Rhinedoll"。)

  • AES算法基于排列和置换运算。
    排列是对数据重新进行安排,置换是将一个数据单元替换为另一个。AES 使用几种不同的方法来执行排列和置换运算。AES是一个迭代的、对称密钥分组的密码,它可以使用128、192 和 256 位密钥,并且用 128 位(16字节)分组加密和解密数据。与公共密钥密码使用密钥对不同,对称密钥密码使用相同的密钥加密和解密数据。通过分组密码返回的加密数据的位数与输入数据相同。迭代加密使用一个循环结构,在该循环中重复置换和替换输入数据。
  • 终端指令
    • AES(ECB)加密
      $ echo -n hello | openssl enc -aes-128-ecb -K 616263 -nosalt | base64

    • AES(CBC)加密
      $ echo -n hello | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt | base64

    • AES(ECB)解密
      $ echo -n d1QG4T2tivoi0Kiu3NEmZQ== | base64 -D | openssl enc -aes-128-ecb -K 616263 -nosalt -d

    • AES(CBC)解密
      $ echo -n u3W/N816uzFpcg6pZ+kbdg== | base64 -D | openssl enc -aes-128-cbc -iv 0102030405060708 -K 616263 -nosalt -d

加密过程是先加密,再base64编码; 解密过程是先base64解码,再解密

  • 应用:

加密:

    - (NSString *)encryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {

      // 设置秘钥
      NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
      uint8_t cKey[self.keySize];
      bzero(cKey, sizeof(cKey));
      [keyData getBytes:cKey length:self.keySize];

      // 设置iv
      uint8_t cIv[self.blockSize];
      bzero(cIv, self.blockSize);
      int option = 0;
      if (iv) {
          [iv getBytes:cIv length:self.blockSize];
          option = kCCOptionPKCS7Padding;
      } else {
          option = kCCOptionPKCS7Padding | kCCOptionECBMode;
      }

      // 设置输出缓冲区
      NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
      size_t bufferSize = [data length] + self.blockSize;
      void *buffer = malloc(bufferSize);

      // 开始加密
      size_t encryptedSize = 0;

      /**
       1. 加密/解密
       2. 加密算法(AES/DES)
       3. 加密方式(ECB/CBC)
       4. 加密秘钥
       5. 秘钥长度
       6. iv 初始向量, ECB 传 nil
       7. 待加密数据
       8. 数据长度
       9. 密文缓存区地址
       10. 密文长度
       11. 加密结果的大小(地址)
       */
      CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                      self.algorithm,
                                      option,
                                      cKey,
                                      self.keySize,
                                      cIv,
                                      [data bytes],
                                      [data length],
                                      buffer,
                                      bufferSize,
                                      &encryptedSize);

      NSData *result = nil;
      if (cryptStatus == kCCSuccess) {
           result = [NSData dataWithBytesNoCopy:buffer length:encryptedSize];
      } else {    
          free(buffer);
          NSLog(@"[错误] 加密失败|状态编码: %d", cryptStatus);
      }

      return [result base64EncodedStringWithOptions:0];
   }

解密:

  - (NSString *)decryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv {

// 设置秘钥
NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
uint8_t cKey[self.keySize];
bzero(cKey, sizeof(cKey));
[keyData getBytes:cKey length:self.keySize];

// 设置iv
uint8_t cIv[self.blockSize];
bzero(cIv, self.blockSize);
int option = 0;
if (iv) {
    [iv getBytes:cIv length:self.blockSize];
    option = kCCOptionPKCS7Padding;
} else {
    option = kCCOptionPKCS7Padding | kCCOptionECBMode;
}

// 设置输出缓冲区
NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
size_t bufferSize = [data length] + self.blockSize;
void *buffer = malloc(bufferSize);

// 开始解密
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                      self.algorithm,
                                      option,
                                      cKey,
                                      self.keySize,
                                      cIv,
                                      [data bytes],
                                      [data length],
                                      buffer,
                                      bufferSize,
                                      &decryptedSize);

NSData *result = nil;
if (cryptStatus == kCCSuccess) {
    result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
    free(buffer);
    NSLog(@"[错误] 解密失败|状态编码: %d", cryptStatus);
}

return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}

3 非对称加密算法(RSA)

未完待续...

推荐阅读更多精彩内容

  • 今夜,我的诗介于世俗与理想之间在星辰西移的时刻众人犹在梦乡而我的诗已坐在了你的渡船之上
    Jack老钱阅读 86评论 3 6
  • 你让我学会闭嘴 让我分清是非错对 何为丑恶,何为善美 我学不会 文明与否无所谓 去返古尊崇 何为高贵何为卑贱 我学...
    水手张叁阅读 81评论 0 2
  • 有如下三个例子,证明我是《数码宝贝》的脑残粉: 1.初中在QQ空间写过一篇数码宝贝19定律(已删),差不多1000...
    青猫朱阅读 298评论 0 0
  • 现在很多人都在提倡积极乐观的生活,就连交朋友也直呼“远离消极的人”,可是,谁能做到永远积极乐观?面上挂着笑,也许心...
    妍兮阅读 382评论 6 4