IRCrypto 0.9.7

IRCrypto 0.9.7

测试已测试
编程语言语言 Obj-CObjective C
许可证 MIT
发布上次发布2016年10月

Ivan Rodriguez维护。



IRCrypto 0.9.7

IRCrypto - iOS 加密库

警告:我不是密码学家,这个库没有被加密专家评审(可能永远不会),自行承担风险使用

概述

IRCrypto旨在提供以下功能

  • 基于字符串的加密密钥推导(使用PBKDF2,迭代10,000次)
  • 对称AES密钥生成(128位,256位,512位)
  • 非对称RSA密钥生成(1024位,2048位和4096位)
  • 非对称EC密钥生成(256位,384位和521位)(阅读更多)
  • 数据哈希(使用SHA256)
  • 数据签名(使用HMAC - SHA256 + 256位密钥)
  • 对称加密(AES CBC模式)
  • 非对称加密(RSA)
  • 使用AES CBC模式和HMAC的加密签名方案进行认证加密[RNCryptor数据格式v3]
  • 使用认证加密的公钥加密(RSA + AES CBC模式和HMAC)[RNCryptor数据格式v3]

您还可以

  • 将生成的密钥(AES、RSA、EC)保存在密钥链中,并使用TouchID和/或用户生成的密码进行保护
  • 生成用于签名数据的非对称密钥对(RSA),其中私钥将不会被返回,仅用于直接从Secure Enclave进行签名

版本

0.9.7

导入

框架

  • 克隆仓库
$ git clone https://github.com/ivRodriguezCA/IRCrypto
  • 将IRCrypto项目拖放到您的项目中,或
  • 选择构建框架方案并构建项目(⌘ + b),这将生成一个IRCrypto.framework框架,位于您的桌面目录中。

使用方法

创建一个IRCrypto实例并使用选项进行配置(有关选项的更多信息,请参阅IRPublicConstants头文件)。当你使用默认选项时,IRCrypto将生成一对RSA密钥(2048位),一个AES密钥(256位),一个用于签名的RSA密钥对(私钥将永远不会离开安全区域)和一个HMAC密钥(256位)。所有这些密钥都将使用TouchID保护保存在密钥chain中。(注意:如果设备不支持TouchID,则需要应用密码来保护这些密钥。在创建您的IRCrypto实例时使用kIRAppPasswordKey选项密钥。

  • 对于默认选项
- (void)someMethod {
    IRCrypto *crypto = [IRCrypto new];
}
  • 当设备不支持TouchID时
- (BOOL)supportsTouchID {
    LAContext *context = [LAContext new];
    NSError *error = nil;

    return [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
}
- (void)someMethod {
  kIRKeyProtection protection = [self supportsTouchID] ? kIRKeyProtectionTouchID : kIRKeyProtectionPassword;
  NSDictionary *options = @{
                              kIRAsymmetricEncryptionProtectionKey:@(protection),
                              kIRSymmetricEncryptionProtectionKey:@(protection),
                              kIRHMACProtectionKey:@(protection),
                              kIRAppPasswordKey: @"my-secret-password"
                            };
  IRCrypto *crypto = [[IRCrypto alloc] initWithOptions:options];
}
  • 仅使用自己的密钥(或密码)通过IRCrypto进行加密和解密
- (void)someMethod {
    IRCrypto *crypto = [[IRCrypto alloc] initWithOptions:@{kIREncryptionOptionsKey:@(kEncryptionOptionsNone)}];
}

生成加密密钥

IRCrypto可以帮助你生成安全的AES和HMAC密钥,还可以从密码中导出安全的AES密钥。

生成256位AES密钥

- (void)generateAESKey {
  IRCrypto *crypto = ...
  NSData *aesKey = [crypto randomAESEncryptionKeyOfLength:32];
  //Encrypt using `aesKey`
}

生成256位HMAC密钥

- (void)generateHMACKey {
  IRCrypto *crypto = ...
  NSData *hmacKey = [crypto randomHMACKeyOfLength:32];
  //Use `hmacKey`
}

从密码中导出256位AES密钥

- (void)generateAESKeyFromPassword {
  NSString *password = ...
  IRCrypto *crypto = [IRCrypto new];
  [crypto keyFromPassword:password
                 ofLength:32
               completion:^(NSData * _Nonnull aesKey, NSData * _Nonnull salt) {
                     // Use the aesKey
               }];
}

加密和解密

加密应提供机密性和完整性,这就是我们为什么需要使用类似于认证加密与关联数据(AEAD)的方案。IRCrypto使用高级加密标准 (AES)在加密块链(CBC)模式下进行机密性加密,使用基于散列的消息认证码(HMAC)进行完整性认证。IRCrypto使用RNCryptor文件格式v3来封装标题、密文和MAC。

AEAD加密

如果使用默认选项,您可以简单地使用带有明文数据的aeEncryptData:completion:failure:方法。您不需要保存IV,因为它是RNCryptor数据格式的一部分。

- (void)someAuthenticatedEncryptionMethod {
    IRCrypto *crypto = ...
    NSData *plaintext = ...
    [crypto aeEncryptData:plaintext
               completion:^(NSData *cipherData, NSData *iv, NSData *encryptionSalt, NSData *hmacSalt) {
                  // encryptionSalt and hmacSalt will be nil
                  // iv is returned but could be ignored
                  // Do something cipherData
               } 
               failure:^(NSError *error) {
                  // Handle error
               }
    ];
}

您还可以使用aeEncryptData:symmetricKey:hmacKey:completion:failure:方法使用自己的密钥进行加密(建议使用2个不同的密钥,一个用于AES,一个用于HMAC)。您不需要保存IV,因为它是RNCryptor数据格式的一部分。

- (void)someAuthenticatedEncryptionMethod {
    IRCrypto *crypto = ...
    NSData *plaintext = ...
    NSData *aesKey = ... // This should be a 256 bit random key
    NSData *hmacKey = ... // This should be a 256 bit random key
    [crypto aeEncryptData:plaintext
             symmetricKey:aesKey
                  hmacKey:hmacKey
               completion:^(NSData *cipherData, NSData *iv, NSData *encryptionSalt, NSData *hmacSalt) {
                  // encryptionSalt and hmacSalt will be nil
                  // iv is returned but could be ignored
                  // Do something cipherData
               } 
               failure:^(NSError *error) {
                  // Handle error
               }
    ];
}

如果您不想生成随机密钥,则可以使用aeEncryptData:password:completion:failure:方法使用简单的文本密码进行加密。您不需要保存encryptionSalthmacSalt,因为它们是RNCryptor数据格式的一部分。

- (void)someAuthenticatedEncryptionMethod {
    IRCrypto *crypto = ...
    NSData *plaintext = ...
    NSString *password = ... // This can be a string of any length
    [crypto aeEncryptData:plaintext
                 password:password
               completion:^(NSData *cipherData, NSData *iv, NSData *encryptionSalt, NSData *hmacSalt) {
                  // iv, encryptionSalt and hmacSalt are returned but could be ignored
                  // Do something with cipherData *and* encryptionSalt *and* hmacSalt
               } 
               failure:^(NSError *error) {
                  // Handle error
               }
    ];
}

AEAD解密

由于IRCryptor使用了RNCryptor文件格式,大部分解密所需的信息都已经包含在其中,IRCryptor只需要相应的解密密钥,这样就简化了保存IV和Salt的烦恼。

如果您使用默认选项,您只需使用您的密文调用aeDecryptData:completion:failure:方法即可。

- (void)someAuthenticatedDecryptionMethod {
    IRCrypto *crypto = ...
    NSData *ciphertext = ...
    [crypto aeDecryptData:ciphertext
               completion:^(NSData *decryptedData) {
                  // Do something with decryptedData
               } 
               failure:^(NSError *error) {
                  // Handle error
               }
    ];
}

使用自己的密钥使用aeDecryptData:symmetricKey:hmacKey:completion:failure:方法解密

- (void)someAuthenticatedDecryptionMethod {
    IRCrypto *crypto = ...
    NSData *ciphertext = ...
    NSData *aesKey = ... // This should be a 256 bit random key
    NSData *hmacKey = ... // This should be a 256 bit random key
    [crypto aeDecryptData:ciphertext
             symmetricKey:aesKey
                  hmacKey:hmacKey
               completion:^(NSData *decryptedData) {
                  // Do something with decryptedData
               } 
               failure:^(NSError *error) {
                  // Handle error
               }
    ];
}

当然,您也可以使用密码通过aeDecryptData:password:completion:failure:方法解密

- (void)someAuthenticatedDecryptionMethod {
    IRCrypto *crypto = ...
    NSData *ciphertext = ...
    NSString *password = ... // This can be a string of any length
    [crypto aeDecryptData:ciphertext
                 password:password
               completion:^(NSData *decryptedData) {
                  // Do something with decryptedData
               } 
               failure:^(NSError *error) {
                  // Handle error
               }
    ];
}

IRCrypto还提供了使用CBC模式下AES进行简单加密和解密的方法。请注意,这些方法提供机密性,但无法抵抗主动攻击。此外,在采用此方法时,您需要记住保存相应的IV和Salt。

对称加密

如果您使用默认选项,可以直接使用您的明文数据调用encryptData:completion:failure:方法(记得保存返回的IV用于解密)。

- (void)someEncryptionMethod {
    IRCrypto *crypto = ...
    NSData *plaintext = ...
    [crypto encryptData:plaintext
             completion:^(NSData *cipherData, NSData *iv, NSData *salt) {
                // Salt will be nil
                // Do something with iv and cipherData
             } 
             failure:^(NSError *error) {
                // Handle error
             }
    ];
}

您还可以使用自己的密钥通过encryptData:withKey:completion:failure:方法进行加密。

- (void)someEncryptionMethod {
    IRCrypto *crypto = ...
    NSData *plaintext = ...
    NSData *key = ... // This should be a 256 bit random key
    [crypto encryptData:plaintext
                withKey:key
             completion:^(NSData *cipherData, NSData *iv, NSData *salt) {
                // Salt will be nil
                // Do something with iv and cipherData
             } 
             failure:^(NSError *error) {
                // Handle error
             }
    ];
}

与AEAD类似,如果您不想生成随机密钥,可以使用encryptData:withPassword:completion:failure:方法通过简单的文本密码进行加密(注意,在这种情况下,您还需要保存salt用于解密)。

- (void)someEncryptionMethod {
    IRCrypto *crypto = ...
    NSData *plaintext = ...
    NSString *password = ... // This can be a string of any length
    [crypto encryptData:plaintext
           withPassword:password
             completion:^(NSData *cipherData, NSData *iv, NSData *salt) {
                // Note that salt will *not* be nil
                // Do something with iv, cipherData *and* salt
             } 
             failure:^(NSError *error) {
                // Handle error
             }
    ];
}

对称解密

如果您使用默认选项,可以简单调用decryptData:iv:completion:failure:方法,传入您的密文和解密时返回的iv。

- (void)someDecryptionMethod {
    IRCrypto *crypto = ...
    NSData *ciphertext = ...
    NSData *iv = ... // The iv returned after encryption
    [crypto decryptData:ciphertext
                     iv:iv
             completion:^(NSData *decryptedData) {
                // Do something with decryptedData
             } 
             failure:^(NSError *error) {
                // Handle error
             }
    ];
}

使用您自己的密钥通过decryptData:withKey:iv:completion:failure:方法进行解密。

- (void)someDecryptionMethod {
    IRCrypto *crypto = ...
    NSData *ciphertext = ...
    NSData *key = ... // This should be a 256 bit random key
    NSData *iv = ... // The iv returned after encryption
    [crypto decryptData:ciphertext
                withKey:key
                    iv:iv
             completion:^(NSData *decryptedData) {
                // Do something with decryptedData
             } 
             failure:^(NSError *error) {
                // Handle error
             }
    ];
}

使用密码通过decryptData:withPassword:iv:salt:completion:failure:方法进行解密。

- (void)someDecryptionMethod {
    IRCrypto *crypto = ...
    NSData *ciphertext = ...
    NSString *password = ... // This can be a string of any length
    NSData *iv = ... // The iv returned after encryption
    NSData *salt = ... // The salt returned after encryption
    [crypto decryptData:ciphertext
           withPassword:password
                     iv:iv
                   salt:salt
             completion:^(NSData *decryptedData) {
                // Do something with decryptedData
             } 
             failure:^(NSError *error) {
                // Handle error
             }
    ];
}

哈希和数据完整性

IRCrypto可以帮助您使用HMAC-SHA256和SHA256哈希摘要添加数据完整性。

使用HMAC进行数据完整性校验

- (void)hmacData {
  IRCrypto *crypto = ...
  NSData *data = ...
  [crypto hmacData:data
        completion:^(NSData * _Nonnull hmacData) {
          //Use HMAC data
        }
        failure:^(NSError * _Nonnull error) {
          //Use error
        }];
}

使用自定义密钥的HMAC进行数据完整性校验

- (void)hmacDataWithCustomKey {
  IRCrypto *crypto = ...
  NSData *hmacKey = ...
  NSData *data = ...
  [crypto hmacData:data
           withKey:hmacKey
        completion:^(NSData * _Nonnull hmacData) {
          //Use HMAC data
        }
        failure:^(NSError * _Nonnull error) {
          //Use error
        }];
}

使用SHA256哈希数据

- (void)hashData {
  IRCrypto *crypto = ...
  NSData *data = ...
  NSData *digest = [crypto hashData:data]; //Use SHA256 digest
}

测试

测试是用Objective-C编写的,使用XCTest框架,选择IRCrypto方案,并按⌘ + u

开发

想要贡献?太好了!

再次强调,这是一个学习实验,如果你发现问题或想添加更多功能,我非常乐意合并你的pull requests :)。

待办事项

  • 邀请一些加密专家来看看这段代码。
  • 错误处理
  • 为密钥大小添加RSA和EC密钥对枚举(目前RSA为2048,EC为256)。
  • 增量对称加密
  • 添加文档
  • IRCrypto添加数据签名功能
  • 完成公开密钥功能导出
  • 添加CHANGES文件
  • 实现带认证加密的公钥加密

为什么?

在任意加密课程中,您首先要学习的是:绝对不要实施自己的加密,尽管我在尽可能地正确使用加密并采用所有最佳实践,这个库中可能也有一些问题。那为什么我会创建这个库呢?简短的答案是:我想学习。虽然这个库目前不打算用于实际应用中(也许在将来?),在实施它的过程中我学到了很多,我不仅理解了这些密码学概念的原理,还想通过实际代码来实验这些概念。话虽如此,如果你找到任何问题,请告诉我,我会修复它们,我们可以共同从中学习。

许可

MIT许可(MIT)版权(c)2016伊万·罗德里格斯