IDZSwiftCommonCrypto
IDZSwiftCommonCrypto 是 Apple 的 CommonCrypto
库的 Swift 封装。
IDZSwiftCommonCrypto 可以与 CocoaPods 和 Cathage 一起使用。有关如何将其安装到项目的详细信息,请参阅 INSTALL.md
如果您正在使用 CocoacaPods,您必须在升级 Xcode 后运行 pod cache clean IDZSwiftCommonCrypto --all
。这是为了避免使用 CocoaPods 缓存中过时的模块映射,简单地删除 Podfile.lock 和 Pods 文件夹是不够的。
IDZSwiftCommonCrypto 提供以下类:
- 用于计算消息摘要的
Digest
- 用于计算基于哈希的消息认证码的
HMAC
- 用于加密和解密固定缓冲区的
Cryptor
- 用于加密和解密流信息的
StreamCryptor
- 用于从密码或密语中推导密钥材料的
PKBDK
使用哪个版本
您使用的版本取决于您当前使用的 Xcode 和 Swift 版本。请参考下表:
- 0.7.4 -- Xcode 7.3.1, Swift 2.2
- 0.8.0 -- Xcode 7.3.1, Swift 2.2,增加了针对
CCMode
的额外 API - 0.8.3 -- Xcode 8.0, Swift 2.3
- 0.9.x -- Xcode 8.0, Swift 3.0
- 0.10.x -- Xcode 9.0, Swift 4.0
- 0.11.x -- Xcode 10.0, Swift 4.2
- 0.12.x -- Xcode 10.2, Swift 5.0
- 0.13.x -- Xcode 11.0, Swift 5.1, iOS 13.0
Digest
使用要计算消息摘要,你需要创建一个Digest
实例,对将被计算摘要的数据调用update
一次或多次,最后调用final
以获得摘要本身。
update
方法可以接受一个String
let s = "The quick brown fox jumps over the lazy dog."
var md5s2 : Digest = Digest(algorithm:.MD5)
md5s2.update(s)
let digests2 = md5s2.final()
// According to Wikipedia this should be
// e4d909c290d0fb1ca068ffaddf22cbd0
hexStringFromArray(digests2)
assert(digests2 == arrayFromHexString("e4d909c290d0fb1ca068ffaddf22cbd0"))
或一个包含UInt8
元素的数组
let b : [UInt8] =
[0x54,0x68,0x65,0x20,0x71,0x75,0x69,0x63,
0x6b,0x20,0x62,0x72,0x6f,0x77,0x6e,0x20,
0x66,0x6f,0x78,0x2e]
var md5s1 : Digest = Digest(algorithm:.MD5)
md5s1.update(b)
let digests1 = md5s1.final()
如果你只有一个缓冲区,你可以简单写出
var digests3 = Digest(algorithm: .md5).update(b)?.final() // digest is of type [UInt8]?
或者
var digests4 = Digest(algorithm: .md5).update(s)?.final() // digest is of type [UInt8]?
支持算法
Digest
类支持以下算法
md2
md4
md5
sha1
sha224
sha256
sha384
sha512
HMAC
使用计算密钥散列消息认证码(HMAC)与计算消息摘要非常类似,只是初始化程序现在还需要一个密钥和一个算法参数。
var keys5 = arrayFrom(hexString: "0102030405060708090a0b0c0d0e0f10111213141516171819")
var datas5 : [UInt8] = Array(count:50, repeatedValue:0xcd)
var expecteds5 = arrayFrom(hexString: "4c9007f4026250c6bc8414f9bf50c86c2d7235da")
var hmacs5 = HMAC(algorithm:.sha1, key:keys5).update(datas5)?.final()
// RFC2202 says this should be 4c9007f4026250c6bc8414f9bf50c86c2d7235da
let expectedRFC2202 = arrayFrom(hexString: "4c9007f4026250c6bc8414f9bf50c86c2d7235da")
assert(hmacs5! == expectedRFC2202)
支持算法
md5
sha1
sha224
sha256
sha384
sha512
Cryptor
使用var key = arrayFrom(hexString: "2b7e151628aed2a6abf7158809cf4f3c")
var plainText = "The quick brown fox jumps over the lazy dog. The fox has more or less had it at this point."
var cryptor = Cryptor(operation:.encrypt, algorithm:.aes, options:.PKCS7Padding, key:key, iv:Array<UInt8>())
var cipherText = cryptor.update(plainText)?.final()
cryptor = Cryptor(operation:.decrypt, algorithm:.aes, options:.PKCS7Padding, key:key, iv:Array<UInt8>())
var decryptedPlainText = cryptor.update(cipherText!)?.final()
var decryptedString = decryptedPlainText!.reduce("") { $0 + String(UnicodeScalar($1)) }
decryptedString
assert(decryptedString == plainText)
支持算法
AES
DES
TripleDES
CAST
RC2
Blowfish
StreamCryptor
使用 要加密一个大文件或网络流,请使用 StreamCryptor
。StreamCryptor
类不会累积加密或解密数据,而是每个对 update
的调用都产生一个输出缓冲区。
下面的示例显示了如何使用 StreamCryptor
加密和解密图像文件。
func crypt(sc : StreamCryptor, inputStream: NSInputStream, outputStream: NSOutputStream, bufferSize: Int)
{
var inputBuffer = Array<UInt8>(count:1024, repeatedValue:0)
var outputBuffer = Array<UInt8>(count:1024, repeatedValue:0)
inputStream.open()
outputStream.open()
var cryptedBytes : UInt = 0
while inputStream.hasBytesAvailable
{
let bytesRead = inputStream.read(&inputBuffer, maxLength: inputBuffer.count)
let status = sc.update(inputBuffer, byteCountIn: UInt(bytesRead), bufferOut: &outputBuffer, byteCapacityOut: UInt(outputBuffer.count), byteCountOut: &cryptedBytes)
assert(status == Status.Success)
if(cryptedBytes > 0)
{
let bytesWritten = outputStream.write(outputBuffer, maxLength: Int(cryptedBytes))
assert(bytesWritten == Int(cryptedBytes))
}
}
let status = sc.final(&outputBuffer, byteCapacityOut: UInt(outputBuffer.count), byteCountOut: &cryptedBytes)
assert(status == Status.Success)
if(cryptedBytes > 0)
{
let bytesWritten = outputStream.write(outputBuffer, maxLength: Int(cryptedBytes))
assert(bytesWritten == Int(cryptedBytes))
}
inputStream.close()
outputStream.close()
}
let imagePath = NSBundle.mainBundle().pathForResource("Riscal", ofType:"jpg")!
let tmp = NSTemporaryDirectory()
let encryptedFilePath = tmp.stringByAppendingPathComponent("Riscal.xjpgx")
var decryptedFilePath = tmp.stringByAppendingPathComponent("RiscalDecrypted.jpg")
var imageInputStream = NSInputStream(fileAtPath: imagePath)
var encryptedFileOutputStream = NSOutputStream(toFileAtPath: encryptedFilePath, append:false)
var encryptedFileInputStream = NSInputStream(fileAtPath: encryptedFilePath)
var decryptedFileOutputStream = NSOutputStream(toFileAtPath: decryptedFilePath, append:false)
var sc = StreamCryptor(operation:.encrypt, algorithm:.aes, options:.PKCS7Padding, key:key, iv:Array<UInt8>())
crypt(sc, imageInputStream, encryptedFileOutputStream, 1024)
// Uncomment this line to verify that the file is encrypted
//var encryptedImage = UIImage(contentsOfFile:encryptedFile)
sc = StreamCryptor(operation:.decrypt, algorithm:.aes, options:.PKCS7Padding, key:key, iv:Array<UInt8>())
crypt(sc, encryptedFileInputStream, decryptedFileOutputStream, 1024)
var image = UIImage(named:"Riscal.jpg")
var decryptedImage = UIImage(contentsOfFile:decryptedFilePath)
PBKDF
使用 PBKDF
类提供了一种从用户密码派生密钥的方法。以下示例生成了一个 20 字节数组密钥
let keys6 = PBKDF.deriveKey("password", salt: "salt", prf: .SHA1, rounds: 1, derivedKeyLength: 20)
// RFC 6070 - Should derive 0c60c80f961f0e71f3a9b524af6012062fe037a6
let expectedRFC6070 = arrayFrom(hexString: "0c60c80f961f0e71f3a9b524af6012062fe037a6")
assert(keys6 == expectedRFC6070)
支持的伪随机函数
sha1
sha224
sha256
sha384
sha512