San*_*492 2 encryption macos ios swift
我正在为一位在计算机上存储客户信息的朋友开发数据管理器。这些信息是公共记录,因此最高安全性并不是什么大问题(他目前以纯文本形式存储),但他不介意进行某种加密。在不详细介绍该程序的情况下,我将不得不过度简化我的问题......
如果我想加密文本对象或图片对象,并在写入磁盘之前用密码锁定它,我的最佳选择是什么?我计划在 macOS 和 iOS 上实现这一点,这样他就可以向任何人/任何设备发送和共享文件。因此对于 iOS 来说,速度是一件好事,而减小文件大小则有利于移动数据的使用。
我是 Apple 开发新手,所以我仍在筛选 API 和框架来学习所有内容,所以我很抱歉问了一个可能很简单的问题。这也是期末周,所以我只在业余时间做这件事,并且总是感谢一些帮助。
谢谢!——桑德斯
对称加密用于加密数据,AES(高级加密标准)是当前标准。Apple 在 Security.framework 中提供 Common Crypto,并在 iOS 上快速使用适用的 OSX 指令和硬件加密。另外,将密钥存储在钥匙串中。
CBC 模式下的 AES 加密,具有随机 IV (Swift 3+)
iv 是加密数据的前缀
aesCBC128Encrypt
将创建一个随机 IV 并添加到加密代码的前缀。
aesCBC128Decrypt
将在解密期间使用前缀 IV。
输入是数据,键是数据对象。如果需要在调用方法中进行转换和/或转换,则需要使用 Base64 等编码形式。
密钥的长度应恰好为 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。如果使用其他密钥大小,则会抛出错误。
默认情况下设置PKCS#7 填充。
此示例需要 Common Crypto ,
因此必须有一个到项目的桥接标头:
#import <CommonCrypto/CommonCrypto.h>
将 添加Security.framework
到项目中。
这是示例,而不是生产代码。
enum AESError: Error {
case KeyError((String, Int))
case IVError((String, Int))
case CryptorError((String, Int))
}
// The iv is prefixed to the encrypted data
func aesCBCEncrypt(data:Data, keyData:Data) throws -> Data {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if (validKeyLengths.contains(keyLength) == false) {
throw AESError.KeyError(("Invalid key length", keyLength))
}
let ivSize = kCCBlockSizeAES128;
let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let status = cryptData.withUnsafeMutableBytes {ivBytes in
SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
}
if (status != 0) {
throw AESError.IVError(("IV generation failed", Int(status)))
}
var numBytesEncrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
cryptBytes,
dataBytes, data.count,
cryptBytes+kCCBlockSizeAES128, cryptLength,
&numBytesEncrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.count = numBytesEncrypted + ivSize
}
else {
throw AESError.CryptorError(("Encryption failed", Int(cryptStatus)))
}
return cryptData;
}
// The iv is prefixed to the encrypted data
func aesCBCDecrypt(data:Data, keyData:Data) throws -> Data? {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if (validKeyLengths.contains(keyLength) == false) {
throw AESError.KeyError(("Invalid key length", keyLength))
}
let ivSize = kCCBlockSizeAES128;
let clearLength = size_t(data.count - ivSize)
var clearData = Data(count:clearLength)
var numBytesDecrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = clearData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES128),
options,
keyBytes, keyLength,
dataBytes,
dataBytes+kCCBlockSizeAES128, clearLength,
cryptBytes, clearLength,
&numBytesDecrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
clearData.count = numBytesDecrypted
}
else {
throw AESError.CryptorError(("Decryption failed", Int(cryptStatus)))
}
return clearData;
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
let clearData = "clearData0123456".data(using:String.Encoding.utf8)!
let keyData = "keyData890123456".data(using:String.Encoding.utf8)!
print("clearData: \(clearData as NSData)")
print("keyData: \(keyData as NSData)")
var cryptData :Data?
do {
cryptData = try aesCBCEncrypt(data:clearData, keyData:keyData)
print("cryptData: \(cryptData! as NSData)")
}
catch (let status) {
print("Error aesCBCEncrypt: \(status)")
}
let decryptData :Data?
do {
let decryptData = try aesCBCDecrypt(data:cryptData!, keyData:keyData)
print("decryptData: \(decryptData! as NSData)")
}
catch (let status) {
print("Error aesCBCDecrypt: \(status)")
}
Run Code Online (Sandbox Code Playgroud)
示例输出:
clearData: <636c6561 72446174 61303132 33343536>
keyData: <6b657944 61746138 39303132 33343536>
cryptData: <92c57393 f454d959 5a4d158f 6e1cd3e7 77986ee9 b2970f49 2bafcf1a 8ee9d51a bde49c31 d7780256 71837a61 60fa4be0>
decryptData: <636c6561 72446174 61303132 33343536>
Run Code Online (Sandbox Code Playgroud)
注意:
CBC 模式示例代码的一个典型问题是它将随机 IV 的创建和共享留给了用户。该示例包括 IV 的生成、加密数据的前缀以及在解密期间使用前缀 IV。这使临时用户无需考虑CBC 模式所需的详细信息。
为了安全起见,加密数据还应该进行身份验证,此示例代码没有提供身份验证,以便较小并允许与其他平台更好的互操作性。
还缺少从密码中导出密钥的密钥,建议使用PBKDF2,因为文本密码用作密钥材料。
有关强大的生产就绪多平台加密代码,请参阅RNCryptor。还可以考虑使用 RNCryptor来实现完整的实现,包括密码派生、加密身份验证和版本控制。
注意密码和密钥:
密码和密钥之间是有区别的。AES 加密密钥恰好是 3 个长度之一:128、192 或 256 位,并且应该显示为随机位。密码/密码短语是人类可读的文本。当使用密码时,需要从中导出加密密钥,有一些函数可以实现此目的,例如 PBKDF2(基于密码的密钥导出函数 2)。RNCryptor 包含这样的导出函数。
基于密码的密钥派生 2 (Swift 3+)
基于密码的密钥派生既可用于从密码文本派生加密密钥,也可用于保存密码以进行身份验证。
可以使用多种哈希算法,包括本示例代码提供的 SHA1、SHA256、SHA512。
rounds 参数用于使计算变慢,以便攻击者必须在每次尝试上花费大量时间。典型的延迟值在 100ms 到 500ms 之间,如果性能不可接受,可以使用更短的值。
此示例需要 Common Crypto ,
因此必须有一个到项目的桥接标头:
#import <CommonCrypto/CommonCrypto.h>
将 添加Security.framework
到项目中。
参数:
password password String
salt salt Data
keyByteCount number of key bytes to generate
rounds Iteration rounds
returns Derived key
func pbkdf2SHA1(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? {
return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA1), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds)
}
func pbkdf2SHA256(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? {
return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds)
}
func pbkdf2SHA512(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? {
return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds)
}
func pbkdf2(hash :CCPBKDFAlgorithm, password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? {
let passwordData = password.data(using:String.Encoding.utf8)!
var derivedKeyData = Data(repeating:0, count:keyByteCount)
let derivationStatus = derivedKeyData.withUnsafeMutableBytes {derivedKeyBytes in
salt.withUnsafeBytes { saltBytes in
CCKeyDerivationPBKDF(
CCPBKDFAlgorithm(kCCPBKDF2),
password, passwordData.count,
saltBytes, salt.count,
hash,
UInt32(rounds),
derivedKeyBytes, derivedKeyData.count)
}
}
if (derivationStatus != 0) {
print("Error: \(derivationStatus)")
return nil;
}
return derivedKeyData
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
let password = "password"
//let salt = "saltData".data(using: String.Encoding.utf8)!
let salt = Data(bytes: [0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61])
let keyByteCount = 16
let rounds = 100000
let derivedKey = pbkdf2SHA1(password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds)
print("derivedKey (SHA1): \(derivedKey! as NSData)")
Run Code Online (Sandbox Code Playgroud)
示例输出:
derivedKey (SHA1): <6b9d4fa3 0385d128 f6d196ee 3f1d6dbf>
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5788 次 |
最近记录: |