Apple的CommonCrypto
库的Swift封装。
IDZSwiftCommonCrypto 可以与 CocoaPods 和 Carthage 一起使用。有关如何将其安装到您的项目的更多详细信息,请参阅INSTALL.md
如果您使用的是 CocoaPods,则在升级 Xcode 后必须使用 pod cache clean IDZSwiftCommonCrypto --all
。 这是为了避免使用 CocoaPods 缓存中的过期模块映射。 仅删除您的 Podfile.lock 和 Pods 目录是不够的。
IDZSwiftCommonCrypto 提供了以下类
Digest
用于计算消息摘要,HMAC
用于计算基于哈希的消息认证码,Cryptor
用于加密和解密有界缓冲区,StreamCryptor
用于加密和解密流式信息,以及PBKDF
用于从密码或密码短语派生密钥材料。您使用的版本取决于您当前使用的 Xcode 和 Swift 版本。 请参考下面的列表
CCMode
的附加 API要计算消息摘要,您需要创建一个 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) 与计算消息摘要非常相似,不同之处在于初始化例程现在需要一个密钥以及一个算法参数。
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
let algorithm = Cryptor.Algorithm.aes
var iv = try! Random.generateBytes(byteCount: algorithm.blockSize())
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:algorithm, options:.PKCS7Padding, key:key, iv:iv)
var cipherText = cryptor.update(plainText)?.final()
cryptor = Cryptor(operation:.decrypt, algorithm:algorithm, options:.PKCS7Padding, key:key, iv:iv)
var decryptedPlainText = cryptor.update(cipherText!)?.final()
var decryptedString = String(bytes: decryptedPlainText!, encoding: .utf8)
decryptedString
assert(decryptedString == plainText)
.AES
.DES
.TripleDES
.CAST
.RC2
.Blowfish
要加密大型文件或网络流,请使用 StreamCryptor
。 StreamCryptor
类不会累积加密或解密的数据,而是每次调用 update
都会产生一个输出缓冲区。
下面的示例展示了如何使用 StreamCryptor
来加密和解密图像文件。
func crypt(sc : StreamCryptor, inputStream: InputStream, outputStream: OutputStream, bufferSize: Int) -> (bytesRead: Int, bytesWritten: Int)
{
var inputBuffer = Array<UInt8>(repeating:0, count:1024)
var outputBuffer = Array<UInt8>(repeating:0, count:1024)
var cryptedBytes : Int = 0
var totalBytesWritten = 0
var totalBytesRead = 0
while inputStream.hasBytesAvailable
{
let bytesRead = inputStream.read(&inputBuffer, maxLength: inputBuffer.count)
totalBytesRead += bytesRead
let status = sc.update(bufferIn: inputBuffer, byteCountIn: bytesRead, bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
assert(status == Status.success)
if(cryptedBytes > 0)
{
let bytesWritten = outputStream.write(outputBuffer, maxLength: Int(cryptedBytes))
assert(bytesWritten == Int(cryptedBytes))
totalBytesWritten += bytesWritten
}
}
let status = sc.final(bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
assert(status == Status.success)
if(cryptedBytes > 0)
{
let bytesWritten = outputStream.write(outputBuffer, maxLength: Int(cryptedBytes))
assert(bytesWritten == Int(cryptedBytes))
totalBytesWritten += bytesWritten
}
return (totalBytesRead, totalBytesWritten)
}
let imagePath = Bundle.main.path(forResource: "Riscal", ofType:"jpg")!
let tmp = NSTemporaryDirectory() as NSString
let encryptedFilePath = "\(tmp)/Riscal.xjpgx"
var decryptedFilePath = "\(tmp)/RiscalDecrypted.jpg"
// Prepare the input and output streams for the encryption operation
guard let imageInputStream = InputStream(fileAtPath: imagePath) else {
fatalError("Failed to initialize the image input stream.")
}
imageInputStream.open()
guard let encryptedFileOutputStream = OutputStream(toFileAtPath: encryptedFilePath, append:false) else
{
fatalError("Failed to open output stream.")
}
encryptedFileOutputStream.open()
// Generate a new, random initialization vector
let initializationVector = try! Random.generateBytes(byteCount: algorithm.blockSize())
// A common way to communicate the initialization vector is to write it at the beginning of the encrypted data.
let bytesWritten = encryptedFileOutputStream.write(initializationVector, maxLength: initializationVector.count)
// Now write the encrypted data
var sc = StreamCryptor(operation:.encrypt, algorithm:algorithm, options:.PKCS7Padding, key:key, iv:initializationVector)
guard bytesWritten == initializationVector.count else
{
fatalError("Failed to write initialization vector to encrypted output file.")
}
let outputResult = crypt(sc: sc, inputStream: imageInputStream, outputStream: encryptedFileOutputStream, bufferSize: 1024)
encryptedFileOutputStream.close()
outputResult
// Uncomment this line to verify that the file is encrypted
//var encryptedImage = NSImage(contentsOfFile:encryptedFile)
// Prepare the input and output streams for the decryption operation
guard let encryptedFileInputStream = InputStream(fileAtPath: encryptedFilePath) else
{
fatalError("Failed to open the encrypted file for input.")
}
encryptedFileInputStream.open()
guard let decryptedFileOutputStream = OutputStream(toFileAtPath: decryptedFilePath, append:false) else {
fatalError("Failed to open the file for the decrypted output file.")
}
decryptedFileOutputStream.open()
// Read back the initialization vector.
var readbackInitializationVector = Array<UInt8>(repeating: 0, count: algorithm.blockSize())
let bytesRead = encryptedFileInputStream.read(&readbackInitializationVector, maxLength: readbackInitializationVector.count)
// Uncomment this to verify that we did indeed read back the initialization vector.
//assert(readbackInitializationVector == initializationVector)
// Now use the read back initialization vector (along with the key) to
sc = StreamCryptor(operation:.decrypt, algorithm:algorithm, options:.PKCS7Padding, key:key, iv:readbackInitializationVector)
let inputResult = crypt(sc: sc, inputStream: encryptedFileInputStream, outputStream: decryptedFileOutputStream, bufferSize: 1024)
// Uncomment this to verify that decrypt operation consumed all the encrypted data
// and produced the correct number of bytes of plaintext output.
//assert(inputResult.bytesRead == outputResult.bytesWritten && inputResult.bytesWritten == outputResult.bytesRead)
var image = NSImage(named:"Riscal.jpg")
var decryptedImage = NSImage(contentsOfFile:decryptedFilePath)
decryptedImage
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