Related
Issue: "Type HASH256 has no member hash"
Background: Trying to implement Apple sign in with Firebase on Swift
Tried to resolve the issue with the following:
-all pods update
-import CommonCrypto + import CryptoKit
-clean build folder / build
The error is still present
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
private func randomNonceString(length: Int = 32) -> String {
precondition(length > 0)
let charset: Array<Character> =
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
var result = ""
var remainingLength = length
while remainingLength > 0 {
let randoms: [UInt8] = (0 ..< 16).map { _ in
var random: UInt8 = 0
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
if errorCode != errSecSuccess {
fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)")
}
return random
}
randoms.forEach { random in
if length == 0 {
return
}
if random < charset.count {
result.append(charset[Int(random)])
remainingLength -= 1
}
}
}
return result
}
//Start Apple's sign-in flow
// Unhashed nonce.
fileprivate var currentNonce: String?
#available(iOS 13, *)
func startSignInWithAppleFlow() {
let nonce = randomNonceString()
currentNonce = nonce
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
request.nonce = sha256(nonce)
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self as! ASAuthorizationControllerDelegate
authorizationController.presentationContextProvider = self as! ASAuthorizationControllerPresentationContextProviding
authorizationController.performRequests()
}
#available(iOS 13, *)
private func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
let hashString = hashedData.compactMap {
return String(format: "%02x", $0)
}.joined()
return hashString
}
// func SHA256() -> String {
//
// let data = self.data(using: String.Encoding.utf8)
// let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
// CC_SHA256(((data! as NSData)).bytes, CC_LONG(data!.count), res?.mutableBytes.assumingMemoryBound(to: UInt8.self))
// let hashedString = "\(res!)".replacingOccurrences(of: "", with: "").replacingOccurrences(of: " ", with: "")
// let badchar: CharacterSet = CharacterSet(charactersIn: "\"<\",\">\"")
// let cleanedstring: String = (hashedString.components(separatedBy: badchar) as NSArray).componentsJoined(by: "")
// return cleanedstring
//
// }
}
//Apple extension
#available(iOS 13.0, *)
extension AuthViewController: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
guard let nonce = currentNonce else {
fatalError("Invalid state: A login callback was received, but no login request was sent.")
}
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetch identity token")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
// Initialize a Firebase credential.
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idTokenString,
accessToken: nonce)
// Sign in with Firebase.
Auth.auth().signIn(with: credential) { (authResult, error) in
if (error != nil) {
// Error. If error.code == .MissingOrInvalidNonce, make sure
// you're sending the SHA256-hashed nonce as a hex string with
// your request to Apple.
print(error?.localizedDescription)
return
}
// User is signed in to Firebase with Apple.
// ...
}
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
// Handle error.
print("Sign in with Apple errored: \(error)")
}
}
Image of error
I encountered the same problem, I spend two days figured it out!
The reason is we mistaken installed 'CryptoKit' in our Podfile. which apple also has a build-in 'CryptoKit' for iOS version 13+.
Solution :
1.deleted pod ''CryptoKit' in our pod file.
2. pod install
after that, we will use apple build in 'CryptoKit' which has the build-in method hash.
This should work: add this outside of your class and then instead of request.nonce = sha256(nonce), type request.nonce = nonce.sha256()
extension String {
func sha256() -> String{
if let stringData = self.data(using: String.Encoding.utf8) {
return hexStringFromData(input: digest(input: stringData as NSData))
}
return ""
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
credit
I have a java code, where i used cryptography( Encryption and decryption). I want to use the same process on swift code.
//my java code for encryption in crypto class
class Crypto {
private SecretKeySpec skc;
private final static char[] hexArray = "0123456789abcdef".toCharArray();
String TAG = "TAG";
#RequiresApi(api = Build.VERSION_CODES.O)
Crypto(String token) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(("YourStrongKey" + token).getBytes("UTF-8"));
skc = new SecretKeySpec(thedigest, "AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len/2];
for(int i = 0; i < len; i+=2){
data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
public byte[] encrypt(String clear) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skc);
byte[] input = clear.getBytes("utf-8");
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
cipher.doFinal(cipherText, ctLength);
Log.d(TAG, "encrypt: ciper: "+cipher.toString());
return cipherText;
}
public byte[] decrypt(byte[] buf, int offset, int len) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skc);
byte[] dec = new byte[cipher.getOutputSize(len - offset)];
int ctLength = cipher.update(buf, offset, len - offset, dec, 0);
cipher.doFinal(dec, ctLength);
Log.d(TAG, "encrypt: dec: "+dec.toString());
return dec;
}
}
//uses of java form main activity class
Crypto crypto;
String token = "Jpc3MiOiJodHRwczovL3NlY3VyZXRva2";
crypto = new Crypto(token);
JSONObject msg = new JSONObject();
msg.put("phoneData", "data1");
msg.put("countryData", "data2");
Log.d(TAG, "startHttpApiRequest: msg: "+msg);
String ss = msg.toString();
byte[] msgD = crypto.encrypt(ss);
//my swift code on playground
class SecretSpec {
var algorithm: String = "AES/ECB/PKCS7padding"
var key = [UInt8]()
func secretSpec(key: [UInt8], algorithm: String){
self.key = key
self.algorithm = algorithm
}
func getAlgorithm() -> String {
return self.algorithm
}
func getFormat() -> String {
return "RAW"
}
func getEncoded() -> [UInt8] {
return self.key
}
func hashCode() -> Int {
var retval: Int = 0
for i in 1...key.count-1 {
retval = retval + Int(key[i]) * Int(i)
}
if (algorithm.lowercased() == "tripledes"){
retval = retval ^ Int("desede".hashValue)
return retval
}else{
retval = retval ^ Int(algorithm.lowercased().hashValue)
return retval
}
}
}
extension String {
func aesEncrypt(key: String) -> String? {
if let keyData = key.data(using: String.Encoding.utf8),
let data = self.data(using: String.Encoding.utf8),
let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation, algoritm, options,
(keyData as NSData).bytes,keyLength,nil, (data as NSData).bytes, data.count,cryptData.mutableBytes, cryptData.length,&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
var bytes = [UInt8](repeating: 0, count: cryptData.length)
cryptData.getBytes(&bytes, length: cryptData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
else {
return nil
}
}
return nil
}
}
func MD5(_ string: String) -> String? {
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = [UInt8](repeating: 0, count: length)
if let d = string.data(using: String.Encoding.utf8) {
_ = d.withUnsafeBytes { (body: UnsafePointer<UInt8>) in
CC_MD5(body, CC_LONG(d.count), &digest)
}
}
return (0..<length).reduce("") {
$0 + String(format: "%02x", digest[$1])
}
}
var token = "YourStrongKeyJpc3MiOiJodHRwczovL3NlY3VyZXRva2"
var algorithm: String = "AES/ECB/PKCS5padding"
var tokenRes = MD5(token)
let digest = hexstringToBytes(tokenRes!)
var skcSpec = SecretSpec()
skcSpec.secretSpec(key: digest!, algorithm: algorithm)
print("hello: \(skcSpec)")
let msg: NSMutableDictionary = NSMutableDictionary()
msg.setValue("data1", forKey: "phoneData")
msg.setValue("data2", forKey: "countryData")
let msgData: NSData
var msgStr: String = ""
var requestUrl: String = ""
do {
msgData = try JSONSerialization.data(withJSONObject: msg, options: JSONSerialization.WritingOptions()) as NSData
msgStr = NSString(data: msgData as Data, encoding: String.Encoding.utf8.rawValue)! as String
} catch _ {
print ("JSON Failure")
}
var str = skcSpec.getEncoded()
print(str)
let skc = NSString(bytes: str, length: str.count, encoding: String.Encoding.ascii.rawValue)! as String
let eneMsg = msgStr.aesEncrypt(key: skc)!
print("encoded: \(eneMsg)")
it does not gives me the same output. please help me to finding same output. nb: java code is fixed for encryption and decryption.
Outputs:
in java:
ffe957f00bdd93cfe1ef1133993cc9d2d8682310c648633660b448d92098e7fa07ae25f600467894ac94ccdcbe4039b8
in swift:
2e130be30aa3d8ff7fdc31dc8ffe1c39afe987ccbf8481caed9c78b49624a31c68df63a899df130128af6852c82d9aea
I have tried to convert your Crypto class directly into Swift, and got this:
import Foundation
import CommonCrypto
func MD5(_ data: Data) -> Data {
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = Data(count: length)
data.withUnsafeBytes {bytes in
_ = digest.withUnsafeMutableBytes {mutableBytes in
CC_MD5(bytes.baseAddress, CC_LONG(bytes.count), mutableBytes.bindMemory(to: UInt8.self).baseAddress)
}
}
return digest
}
func hexStringToData(_ s: String) -> Data {
let len = s.count
var data = Data(count: len/2)
var start = s.startIndex
for i in 0..<len/2 {
let end = s.index(start, offsetBy: 2)
data[i] = UInt8(s[start..<end], radix: 16)!
start = end
}
return data
}
class Crypto {
private var key: Data
init(_ token: String) {
let theDigest = MD5(("YourStrongKey" + token).data(using: .utf8)!)
key = theDigest
}
public func encrypt(_ clear: String) -> Data? {
let input = clear.data(using: .utf8)!
var cipherText = Data(count: input.count + kCCBlockSizeAES128)
let keyLength = size_t(kCCKeySizeAES128)
assert(key.count >= keyLength)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var ctLength: size_t = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
input.withUnsafeBytes {inputBytes in
cipherText.withUnsafeMutableBytes {mutableBytes in
CCCrypt(operation, algoritm, options,
keyBytes.baseAddress, keyLength, nil,
inputBytes.baseAddress, inputBytes.count,
mutableBytes.baseAddress, mutableBytes.count,
&ctLength)
}
}
}
if cryptStatus == CCCryptorStatus(kCCSuccess) {
cipherText.count = Int(ctLength)
return cipherText
} else {
return nil
}
}
public func decrypt(_ buf: Data, _ offset: Int, _ len: Int) -> Data? {
var dec = Data(count: len)
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var ctLength :size_t = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
buf.withUnsafeBytes {inputBytes in
dec.withUnsafeMutableBytes {mutableBytes in
CCCrypt(operation, algoritm, options,
keyBytes.baseAddress, keyLength, nil,
inputBytes.baseAddress, inputBytes.count,
mutableBytes.baseAddress, mutableBytes.count,
&ctLength)
}
}
}
if cryptStatus == CCCryptorStatus(kCCSuccess) {
dec.count = Int(ctLength)
return dec
} else {
return nil
}
}
}
let token = "Jpc3MiOiJodHRwczovL3NlY3VyZXRva2"
let crypto = Crypto(token)
let msg = [
"phoneData": "data1",
"countryData": "data2",
]
let msgData = try! JSONSerialization.data(withJSONObject: msg)
let ss = String(data: msgData, encoding: .utf8)!
print(ss) //->{"phoneData":"data1","countryData":"data2"}
//### Be careful, this may be different than the result of JSONObject#toString() in Java.
//### Sometimes, outputs->{"countryData":"data2","phoneData":"data1"}
let msgD = crypto.encrypt(ss)!
print(msgD as NSData) //-><ffe957f0 0bdd93cf e1ef1133 993cc9d2 24e8b9a1 162520e5 54a3d8af 8f478db7 07ae25f6 00467894 ac94ccdc be4039b8>
//### When `ss` is {"phoneData":"data1","countryData":"data2"}
let decrypted = crypto.decrypt(msgD, 0, msgD.count)!
print(String(data: decrypted, encoding: .utf8)!) //->{"phoneData":"data1","countryData":"data2"}
In your Java code, your key String ("YourStrongKey" + token) gets two conversions until used as a binary key data for AES:
key: "YourStrongKey" + token
↓
UTF-8 bytes
↓
MD5 digest
But, in your Swift code, you are converting the key more times:
token: "YourStrongKeyJpc3MiOiJodHRwczovL3NlY3VyZXRva2"
↓
UTF-8 bytes (`d` in `MD5(_:)`)
↓
MD5 digest
↓
binary to HEX String conversion (return value from `MD5(_:)`)
↓
UTF-8 bytes (`keyData` in `aesEncrypt(key:)`)
There's no binary to HEX String conversion in your Java code and you should have not done that.
By the way, ECB is not considered to be a safe way to encrypt, you should better update your Java code.
I have an android class which is encoding and decoding my text using a key but I want to create the same class for swift.
“I'm setting up a new server, and want to support UTF-8 fully in my web application. Where do I need to set the encoding/charsets?”
public class EncryptionDecryption {
String strResult;
public String Encrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
Log.v("GET Result from final:", results.toString());
strResult = Base64.encodeToString(results, 1);
Log.v("Encrypt01:", strResult);
Log.v("Encrypt02:", results.toString());
return strResult;
}
public String Decrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //this parameters should not be changed
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] results = new byte[text.length()];
//BASE64Decoder decoder = new BASE64Decoder();
try {
results = cipher.doFinal(Base64.decode(text, Base64.DEFAULT));
} catch (Exception e) {
Log.i("Error in Decryption", e.toString());
}
Log.i("Data", new String(results, "UTF-8"));
//return new String(results, "UTF-8"); // it returns the result as a String
return new String(results, "UTF-8");
}
For swift 4 I found this answer thanks all
import CommonCrypto
extension String {
func aesEncrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? {
if let keyData = key.data(using: String.Encoding.utf8),
let data = self.data(using: String.Encoding.utf8),
let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(options)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
return base64cryptString
}
else {
return nil
}
}
return nil
}
func aesDecrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? {
if let keyData = key.data(using: String.Encoding.utf8),
let data = NSData(base64Encoded: self, options: .ignoreUnknownCharacters),
let cryptData = NSMutableData(length: Int((data.length)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(options)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
data.bytes, data.length,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let unencryptedMessage = String(data: cryptData as Data, encoding:String.Encoding.utf8)
return unencryptedMessage
}
else {
return nil
}
}
return nil
}
}
Starting from Xcode 10.1 and Swift 4.2 you can use CommonCrypto pretty easy without to take care about Objective-C to Swift bridging.
just:
import CommonCrypto
read more about framework methods here https://developer.apple.com/security/
I want to encrypt some files using AES256 in my cocoa application.
The resulting files should be as small as possible.
Here's my code:
static func encryptFile(pathToFile:String, destinationPath:String, key:String) -> Bool {
var error: NSError?
let fileContents = NSData(contentsOfFile: pathToFile, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &error)
if let fileContents = fileContents {
println("Trying to encrypt file \(pathToFile)")
let startTime = NSDate().timeIntervalSince1970
let encryptedContents = FileEncryptor.AES256Encryption(fileContents, key: key)
println("ENCRYPTION TIME: \(NSDate().timeIntervalSince1970 - startTime)")
if let encryptedContents = encryptedContents {
var writeError: NSError?
if (encryptedContents.writeToFile(pathToFile, options: NSDataWritingOptions.DataWritingAtomic, error: &writeError)) {
return true
} else {
if (writeError != nil) {
println("Cannot write to file \(pathToFile). Error: \(writeError)")
} else {
println("Cannot write to file \(pathToFile)")
}
return false
}
} else {
return false
}
} else {
println("Unable to read file \(pathToFile). Error: \(error)");
return false
}
}
static func AES256Encryption(dataToEncrypt:NSData, key:String) -> NSData? {
let keyData: NSData! = (key as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let keyBytes = UnsafeMutablePointer<Void>(keyData.bytes)
print("keyLength = \(keyData.length), keyData = \(keyData)")
let dataLength = size_t(dataToEncrypt.length)
let dataBytes = UnsafeMutablePointer<Void>(dataToEncrypt.bytes)
print("dataLength = \(dataLength)")
let cryptData = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
let cryptPointer = UnsafeMutablePointer<Void>(cryptData!.mutableBytes)
let cryptLength = size_t(cryptData!.length)
let keyLength = size_t(kCCKeySizeAES256)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding + kCCModeCBC)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
keyBytes, keyLength,
nil,
dataBytes, dataLength,
cryptPointer, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
// let x: UInt = numBytesEncrypted
cryptData!.length = Int(numBytesEncrypted)
print("cryptLength = \(numBytesEncrypted)")
return cryptData
} else {
print("Error: \(cryptStatus)")
return nil
}
}
I have an issue with two things:
1. Encrypted file content look like [small part of the file]:
a56f 707a 24cb e965 08dd be3d 1a0d 9e7b
b73f c982 2014 fdae fa29 c63d 4588 ee29
7b03 5fb2 96a5 00be 2871 e3e8 af23 4eac
I would rather have it like:
ã.FrÚQ÷¬_ÄC¯Q%,™˜$žKÝôÙ„gC€È•GBvn&V9&´fÓ
I cannot decrypt some of the files.
Most of them are fine, however some are not. I've been looking for some kind of pattern, and the only thing I have found is that in most cases, the files which are correct, are larger when encrypted then they are when decrypted/original.
Here's a function I use to decrypt:
static func decryptFile(pathToFile:String, destinationPath:String, key:String) -> Bool {
var error: NSError?
let fileContents = NSData(contentsOfFile: pathToFile, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &error)
if let fileContents = fileContents {
println("Trying to decrypt file \(pathToFile)")
let startTime = NSDate().timeIntervalSince1970
let decryptedContents = FileEncryptor.AES256Decryption(fileContents, key: key)
println("DECRYPTION TIME: \(NSDate().timeIntervalSince1970 - startTime)")
return NSFileManager.defaultManager().createFileAtPath(destinationPath, contents:decryptedContents, attributes:nil)
} else {
println("Unable to read file \(pathToFile). Error: \(error)");
return false
}
}
static func AES256Decryption(data:NSData, key: String) -> NSData? {
let keyData: NSData! = (key as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let keyBytes = UnsafeMutablePointer<Void>(keyData.bytes)
print("keyLength = \(keyData.length)")
let dataLength = size_t(data.length)
let dataBytes = UnsafeMutablePointer<Void>(data.bytes)
print("dataLength = \(dataLength), ")
let cryptData = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
let cryptPointer = UnsafeMutablePointer<Void>(cryptData!.mutableBytes)
let cryptLength = size_t(cryptData!.length)
let keyLength = size_t(kCCKeySizeAES256)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding + kCCModeCBC)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
keyBytes, keyLength,
nil,
dataBytes, dataLength,
cryptPointer, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
// let x: UInt = numBytesEncrypted
cryptData!.length = Int(numBytesEncrypted)
return cryptData
} else {
print("Error: \(cryptStatus)")
return nil
}
}
Almost all of the code has been taken from SO, it seems to work ok, apart from these issues I have.
The password I use is same for all of the files.
Any help will be appreciated!
EDIT:
About issue 2. It seems the code I used was prepared for ECB mode which I have changed to CBC. This means I have to increase size of the encrypted data. After making this change, almost all of the files work perfectly, apart from one... which worked ok when using the smaller value. Damn.
I want to use sha256 in my project, but I had some troubles rewriting objC code to swift code. Help me please. I used this answer: How can I compute a SHA-2 (ideally SHA 256 or SHA 512) hash in iOS?
Here's my code
var hash : [CUnsignedChar]
CC_SHA256(data.bytes, data.length, hash)
var res : NSData = NSData.dataWithBytes(hash, length: CC_SHA256_DIGEST_LENGTH)
it gives me error everything because swift cannot convert Int to CC_LONG, for example.
You have to convert explicitly between Int and CC_LONG, because Swift does not
do implicit conversions, as in (Objective-)C.
You also have to define hash as an array of the required size.
func sha256(data : NSData) -> NSData {
var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))
return res
}
Alternatively, you can use NSMutableData to allocate the needed buffer:
func sha256(data : NSData) -> NSData {
let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256(data.bytes, CC_LONG(data.length), UnsafeMutablePointer(res.mutableBytes))
return res
}
Update for Swift 3 and 4:
func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0, CC_LONG(data.count), &hash)
}
return Data(bytes: hash)
}
Update for Swift 5:
func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
return Data(hash)
}
Updated for Swift 5.
Put this extension somewhere in your project and use it on a string like this: mystring.sha256(), or on data with data.sha256()
import Foundation
import CommonCrypto
extension Data{
public func sha256() -> String{
return hexStringFromData(input: digest(input: self as NSData))
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
public extension String {
func sha256() -> String{
if let stringData = self.data(using: String.Encoding.utf8) {
return stringData.sha256()
}
return ""
}
}
With CryptoKit added in iOS13, we now have native Swift API:
import Foundation
import CryptoKit
// CryptoKit.Digest utils
extension Digest {
var bytes: [UInt8] { Array(makeIterator()) }
var data: Data { Data(bytes) }
var hexStr: String {
bytes.map { String(format: "%02X", $0) }.joined()
}
}
func example() {
guard let data = "hello world".data(using: .utf8) else { return }
let digest = SHA256.hash(data: data)
print(digest.data) // 32 bytes
print(digest.hexStr) // B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9
}
Because utils are defined for protocol Digest, you can use it for all digest type in CryptoKit, like SHA384Digest, SHA512Digest, SHA1Digest, MD5Digest...
Functions giving the SHA from NSData & String (Swift 3):
func sha256(_ data: Data) -> Data? {
guard let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH)) else { return nil }
CC_SHA256((data as NSData).bytes, CC_LONG(data.count), res.mutableBytes.assumingMemoryBound(to: UInt8.self))
return res as Data
}
func sha256(_ str: String) -> String? {
guard
let data = str.data(using: String.Encoding.utf8),
let shaData = sha256(data)
else { return nil }
let rc = shaData.base64EncodedString(options: [])
return rc
}
Include in your bridging header:
#import "CommonCrypto/CommonCrypto.h"
A version for Swift 5 that uses CryptoKit on iOS 13 and falls back to CommonCrypto otherwise:
import CommonCrypto
import CryptoKit
import Foundation
private func hexString(_ iterator: Array<UInt8>.Iterator) -> String {
return iterator.map { String(format: "%02x", $0) }.joined()
}
extension Data {
public var sha256: String {
if #available(iOS 13.0, *) {
return hexString(SHA256.hash(data: self).makeIterator())
} else {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
self.withUnsafeBytes { bytes in
_ = CC_SHA256(bytes.baseAddress, CC_LONG(self.count), &digest)
}
return hexString(digest.makeIterator())
}
}
}
Usage:
let string = "The quick brown fox jumps over the lazy dog"
let hexDigest = string.data(using: .ascii)!.sha256
assert(hexDigest == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592")
Also available via Swift package manager:
https://github.com/ralfebert/TinyHashes
I researched many answers and I summarized it:
import CryptoKit
import CommonCrypto
extension String {
func hash256() -> String {
let inputData = Data(utf8)
if #available(iOS 13.0, *) {
let hashed = SHA256.hash(data: inputData)
return hashed.compactMap { String(format: "%02x", $0) }.joined()
} else {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
inputData.withUnsafeBytes { bytes in
_ = CC_SHA256(bytes.baseAddress, UInt32(inputData.count), &digest)
}
return digest.makeIterator().compactMap { String(format: "%02x", $0) }.joined()
}
}
}
import CommonCrypto
public extension String {
var sha256: String {
let data = Data(utf8)
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes { buffer in
_ = CC_SHA256(buffer.baseAddress, CC_LONG(buffer.count), &hash)
}
return hash.map { String(format: "%02hhx", $0) }.joined()
}
}
Here's my simple 3-line Swift 4 function for this using the Security Transforms API, which is part of Foundation on macOS. (Unfortunately iOS programmers cannot use this technique.)
import Foundation
extension Data {
public func sha256Hash() -> Data {
let transform = SecDigestTransformCreate(kSecDigestSHA2, 256, nil)
SecTransformSetAttribute(transform, kSecTransformInputAttributeName, self as CFTypeRef, nil)
return SecTransformExecute(transform, nil) as! Data
}
}
Here's a method that uses the CoreFoundation Security Transforms API, so you don't even need to link to CommonCrypto. For some reason in 10.10/Xcode 7 linking to CommmonCrypto with Swift is drama so I used this instead.
This method reads from an NSInputStream, which you can either get from a file, or you can make one that reads an NSData, or you can make bound reader/writer streams for a buffered process.
// digestType is from SecDigestTransform and would be kSecDigestSHA2, etc
func digestForStream(stream : NSInputStream,
digestType type : CFStringRef, length : Int) throws -> NSData {
let transform = SecTransformCreateGroupTransform().takeRetainedValue()
let readXform = SecTransformCreateReadTransformWithReadStream(stream as CFReadStreamRef).takeRetainedValue()
var error : Unmanaged<CFErrorRef>? = nil
let digestXform : SecTransformRef = try {
let d = SecDigestTransformCreate(type, length, &error)
if d == nil {
throw error!.takeUnretainedValue()
} else {
return d.takeRetainedValue()
}
}()
SecTransformConnectTransforms(readXform, kSecTransformOutputAttributeName,
digestXform, kSecTransformInputAttributeName,
transform, &error)
if let e = error { throw e.takeUnretainedValue() }
if let output = SecTransformExecute(transform, &error) as? NSData {
return output
} else {
throw error!.takeUnretainedValue()
}
}
Tested in Swift5.
In case you want to get the hash in String,
this is how I did.
private func getHash(_ phrase:String) -> String{
let data = phrase.data(using: String.Encoding.utf8)!
let length = Int(CC_SHA256_DIGEST_LENGTH)
var digest = [UInt8](repeating: 0, count: length)
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &digest)
}
return digest.map { String(format: "%02x", $0) }.joined(separator: "")
}
For Swift 5:
guard let data = self.data(using: .utf8) else { return nil }
var sha256 = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
sha256.withUnsafeMutableBytes { sha256Buffer in
data.withUnsafeBytes { buffer in
let _ = CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), sha256Buffer.bindMemory(to: UInt8.self).baseAddress)
}
}
return sha256
The other answers will have performance problems for calculating digests from large amounts of data (e.g. large files). You will not want to load all data into memory at once. Consider the following approach using update/finalize:
final class SHA256Digest {
enum InputStreamError: Error {
case createFailed(URL)
case readFailed
}
private lazy var context: CC_SHA256_CTX = {
var shaContext = CC_SHA256_CTX()
CC_SHA256_Init(&shaContext)
return shaContext
}()
private var result: Data? = nil
init() {
}
func update(url: URL) throws {
guard let inputStream = InputStream(url: url) else {
throw InputStreamError.createFailed(url)
}
return try update(inputStream: inputStream)
}
func update(inputStream: InputStream) throws {
guard result == nil else {
return
}
inputStream.open()
defer {
inputStream.close()
}
let bufferSize = 4096
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
defer {
buffer.deallocate()
}
while true {
let bytesRead = inputStream.read(buffer, maxLength: bufferSize)
if bytesRead < 0 {
//Stream error occured
throw (inputStream.streamError ?? InputStreamError.readFailed)
} else if bytesRead == 0 {
//EOF
break
}
self.update(bytes: buffer, length: bytesRead)
}
}
func update(data: Data) {
guard result == nil else {
return
}
data.withUnsafeBytes {
self.update(bytes: $0, length: data.count)
}
}
func update(bytes: UnsafeRawPointer, length: Int) {
guard result == nil else {
return
}
_ = CC_SHA256_Update(&self.context, bytes, CC_LONG(length))
}
func finalize() -> Data {
if let calculatedResult = result {
return calculatedResult
}
var resultBuffer = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256_Final(&resultBuffer, &self.context)
let theResult = Data(bytes: resultBuffer)
result = theResult
return theResult
}
}
extension Data {
private static let hexCharacterLookupTable: [Character] = [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"a",
"b",
"c",
"d",
"e",
"f"
]
var hexString: String {
return self.reduce(into: String(), { (result, byte) in
let c1: Character = Data.hexCharacterLookupTable[Int(byte >> 4)]
let c2: Character = Data.hexCharacterLookupTable[Int(byte & 0x0F)]
result.append(c1)
result.append(c2)
})
}
}
You could use it as follows:
let digest = SHA256Digest()
try digest.update(url: fileURL)
let result = digest.finalize().hexString
print(result)
I prefer to use:
extension String {
var sha256:String? {
guard let stringData = self.data(using: String.Encoding.utf8) else { return nil }
return digest(input: stringData as NSData).base64EncodedString(options: [])
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
}
The hasded String is base64 encoded.