Decrypt CommonCrypto always returning nil with URLSession.shared.dataTask Data Swift - swift

I'm making an Application with a java backend and a Swift front-end. Using a REST Api to move data. I wish to encrypt the data with AES 128 CBC. The encrypting method is working, but the decrypting method is not.
First off all, this is the Swift code for de AES encrypting and decrypting:
import Foundation
import CommonCrypto
struct AES {
private let key: Data
private let iv: Data
init?() {
let ivProduct: String = "dkghepfowntislqn"
let keyProduct: String = "2949382094230487"
guard keyProduct.count == kCCKeySizeAES128 || keyProduct.count == kCCKeySizeAES256, let keyData = keyProduct.data(using: .utf8) else {
debugPrint("Error: Failed to set a key.")
return nil
}
guard ivProduct.count == kCCBlockSizeAES128, let ivData = ivProduct.data(using: .utf8) else {
debugPrint("Error: Failed to set an initial vector.")
return nil
}
self.key = keyData
self.iv = ivData
}
func encrypt(string: String) -> Data? {
return crypt(data: string.data(using: .utf8), option: CCOperation(kCCEncrypt))
}
func decrypt(data: Data?) -> String? {
guard let decryptedData = crypt(data: data, option: CCOperation(kCCDecrypt)) else { return nil }
return String(bytes: decryptedData, encoding: .utf8)
}
func crypt(data: Data?, option: CCOperation) -> Data? {
guard let data = data else { return nil }
let cryptLength = data.count + kCCBlockSizeAES128
var cryptData = Data(count: cryptLength)
let keyLength = key.count
let options = CCOptions(kCCOptionPKCS7Padding)
var bytesLength = Int(0)
let status = cryptData.withUnsafeMutableBytes { cryptBytes in
data.withUnsafeBytes { dataBytes in
iv.withUnsafeBytes { ivBytes in
key.withUnsafeBytes { keyBytes in
CCCrypt(option, CCAlgorithm(kCCAlgorithmAES), options, keyBytes.baseAddress, keyLength, ivBytes.baseAddress, dataBytes.baseAddress, data.count, cryptBytes.baseAddress, cryptLength, &bytesLength)
}
}
}
}
guard UInt32(status) == UInt32(kCCSuccess) else {
debugPrint("Error: Failed to crypt data. Status \(status)")
return nil
}
cryptData.removeSubrange(bytesLength..<cryptData.count)
return cryptData
}
}
The data is gathered from the REST API like so:
func getTestAllPayments(_ completion: #escaping ([Payment]) -> ()) {
let aes128 = AES()
if let url = URL(string: "\(localhostUrl)/payment") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode([Payment].self, from: (data))
print(res.self)
completion(res)
return
} catch let error {
print(error)
}
}
}.resume()
}
}
Now for the problem. I've ran a couple of test:
first check if the encrypt and decrypt methods work together:
let aes128 = AES()
let dataEncrypt = aes128?.encrypt(string:"Hello") //Will be :lG7Bqk0nwx732eOQLAzhqQ==
let dataDecrypt = aes128?.decrypt(data:dataEncrypt) //Will be: "Hello"
print(dataDecrypt) --> //output = "Hello"
First test works like a charm. For the second test:
let aes128 = AES()
if let url = URL(string: "\(localhostUrl)/payment") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
print(String(data: data, encoding: .utf8)) //Output = lG7Bqk0nwx732eOQLAzhqQ==
let dataDecrypt = aes128?.decrypt(data: data)
print(dataDecrypt) --> //output = nil
This is where it goes wrong. When fetching the data with the exact same encoding string, it'll always return nil. Has it something to do with the data format that URLSession returns?

Related

How to decrypt data with Security.framework on macOS?

I need to decrypt data with a RSA public key on macOS, by googling I know we can use method SecKeyCreateDecryptedData of Security.framework to achieve that, but it leads to two problems:
SecKeyCreateDecryptedData accepts a private key to execute decryption, but in my situation, the data is encrypted with private key in the server-end, and needs to be decrypted with public key in the client-end.
I tried to create SecKey from a RSA public key string, but failed.
My code:
import Foundation
func getPublicKey(from data: Data) throws -> SecKey {
var error: Unmanaged<CFError>? = nil
let publicKeyMaybe = SecKeyCreateWithData(
data as NSData,
[
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic
] as NSDictionary,
&error)
guard let publicKey = publicKeyMaybe else {
throw error!.takeRetainedValue() as Error
}
return publicKey
}
func decrypt(key: SecKey, data cipherTextData: Data) -> Data? {
let algorithm: SecKeyAlgorithm = .eciesEncryptionCofactorVariableIVX963SHA256AESGCM
guard SecKeyIsAlgorithmSupported(key, .decrypt, algorithm) else {
print("Can't decrypt. Algorithm not supported.")
return nil
}
var error: Unmanaged<CFError>? = nil
let clearTextData = SecKeyCreateDecryptedData(key,
algorithm,
cipherTextData as CFData,
&error) as Data?
if let error = error {
print("Can't decrypt. %#", (error.takeRetainedValue() as Error).localizedDescription)
return nil
}
guard clearTextData != nil else {
print("Can't decrypt. No resulting cleartextData.")
return nil
}
print("Decrypted data.")
return clearTextData
}
func testDecrypt() {
let rawString = "0ed3a2c57f5dEJgqXT9760269b8cc5cd76f3afcf"
let decodedData = Data.init(base64Encoded: rawString, options: [])!
let pubKey = try! getPublicKey(from: kPubKey.data(using: .utf8)!) // Error: RSA public key creation from data failed
let decryptedData = decrypt(key: pubKey, data: decodedData)!
let decrypted = String.init(data: decryptedData, encoding: .utf8)!
print(">>>>>>> decrypted string: \(decrypted)")
}
testDecrypt()
With method of Kazunori Takaishi, I tested all the algorithm, none of them is supported:
func decrypt(key: SecKey, data cipherTextData: Data) -> Data? {
let algorithms: [SecKeyAlgorithm] = [
.rsaSignatureRaw,
.rsaSignatureDigestPKCS1v15Raw,
.rsaSignatureDigestPKCS1v15SHA1,
.rsaSignatureDigestPKCS1v15SHA224,
.rsaSignatureDigestPKCS1v15SHA256,
.rsaSignatureDigestPKCS1v15SHA384,
.rsaSignatureDigestPKCS1v15SHA512,
.rsaSignatureMessagePKCS1v15SHA1,
.rsaSignatureMessagePKCS1v15SHA224,
.rsaSignatureMessagePKCS1v15SHA256,
.rsaSignatureMessagePKCS1v15SHA384,
.rsaSignatureMessagePKCS1v15SHA512,
.rsaSignatureDigestPSSSHA1,
.rsaSignatureDigestPSSSHA224,
.rsaSignatureDigestPSSSHA256,
.rsaSignatureDigestPSSSHA384,
.rsaSignatureDigestPSSSHA512,
.rsaSignatureMessagePSSSHA1,
.rsaSignatureMessagePSSSHA224,
.rsaSignatureMessagePSSSHA256,
.rsaSignatureMessagePSSSHA384,
.rsaSignatureMessagePSSSHA512,
.ecdsaSignatureRFC4754,
.ecdsaSignatureDigestX962,
.ecdsaSignatureDigestX962SHA1,
.ecdsaSignatureDigestX962SHA224,
.ecdsaSignatureDigestX962SHA256,
.ecdsaSignatureDigestX962SHA384,
.ecdsaSignatureDigestX962SHA512,
.ecdsaSignatureMessageX962SHA1,
.ecdsaSignatureMessageX962SHA224,
.ecdsaSignatureMessageX962SHA256,
.ecdsaSignatureMessageX962SHA384,
.ecdsaSignatureMessageX962SHA512,
.rsaEncryptionRaw,
.rsaEncryptionPKCS1,
.rsaEncryptionOAEPSHA1,
.rsaEncryptionOAEPSHA224,
.rsaEncryptionOAEPSHA256,
.rsaEncryptionOAEPSHA384,
.rsaEncryptionOAEPSHA512,
.rsaEncryptionOAEPSHA1AESGCM,
.rsaEncryptionOAEPSHA224AESGCM,
.rsaEncryptionOAEPSHA256AESGCM,
.rsaEncryptionOAEPSHA384AESGCM,
.rsaEncryptionOAEPSHA512AESGCM,
.eciesEncryptionStandardX963SHA1AESGCM,
.eciesEncryptionStandardX963SHA224AESGCM,
.eciesEncryptionStandardX963SHA256AESGCM,
.eciesEncryptionStandardX963SHA384AESGCM,
.eciesEncryptionStandardX963SHA512AESGCM,
.eciesEncryptionCofactorX963SHA1AESGCM,
.eciesEncryptionCofactorX963SHA224AESGCM,
.eciesEncryptionCofactorX963SHA256AESGCM,
.eciesEncryptionCofactorX963SHA384AESGCM,
.eciesEncryptionCofactorX963SHA512AESGCM,
.eciesEncryptionStandardVariableIVX963SHA224AESGCM,
.eciesEncryptionStandardVariableIVX963SHA256AESGCM,
.eciesEncryptionStandardVariableIVX963SHA384AESGCM,
.eciesEncryptionStandardVariableIVX963SHA512AESGCM,
.eciesEncryptionCofactorVariableIVX963SHA224AESGCM,
.eciesEncryptionCofactorVariableIVX963SHA256AESGCM,
.eciesEncryptionCofactorVariableIVX963SHA384AESGCM,
.eciesEncryptionCofactorVariableIVX963SHA512AESGCM,
.ecdhKeyExchangeStandard,
.ecdhKeyExchangeStandardX963SHA1,
.ecdhKeyExchangeStandardX963SHA224,
.ecdhKeyExchangeStandardX963SHA256,
.ecdhKeyExchangeStandardX963SHA384,
.ecdhKeyExchangeStandardX963SHA512,
.ecdhKeyExchangeCofactor,
.ecdhKeyExchangeCofactorX963SHA1,
.ecdhKeyExchangeCofactorX963SHA224,
.ecdhKeyExchangeCofactorX963SHA256,
.ecdhKeyExchangeCofactorX963SHA384,
.ecdhKeyExchangeCofactorX963SHA512
]
for a in algorithms {
if SecKeyIsAlgorithmSupported(key, .decrypt, a) {
print(">>>>>>>>>>>>> supported algorithm: \(a)")
}
}
//==================
let algorithm: SecKeyAlgorithm = .rsaEncryptionPKCS1 // .rsaSignatureMessagePKCS1v15SHA256 // .rsaSignatureDigestPKCS1v15SHA1 // .rsaSignatureDigestPKCS1v15Raw // .rsaSignatureDigestPKCS1v15SHA256 //.rsaEncryptionPKCS1
// guard SecKeyIsAlgorithmSupported(key, .decrypt, algorithm) else {
// print("Can't decrypt. Algorithm not supported.")
// return nil
// }
var error: Unmanaged<CFError>? = nil
let clearTextData = SecKeyCreateDecryptedData(key,
algorithm,
cipherTextData as CFData,
&error) as Data?
if let error = error {
print("Can't decrypt. \((error.takeRetainedValue() as Error).localizedDescription)")
return nil
}
guard clearTextData != nil else {
print("Can't decrypt. No resulting cleartextData.")
return nil
}
print("Decrypted data.")
return clearTextData
}
If your code only works on Mac OS, you may be able to create a Seckey by using the SecKeyCreateFromData method instead of the SecKeyCreateWithData method.
Here is Sample Code:
func getPublicKey(from data: Data?) throws -> SecKey {
var error: Unmanaged<CFError>? = nil
guard let data = data else { throw error!.takeRetainedValue() as Error }
let publicKeyMaybe = SecKeyCreateFromData([:] as CFDictionary, data as NSData, &error)
guard let publicKey = publicKeyMaybe else {
throw error!.takeRetainedValue() as Error
}
return publicKey
}
And You should convert RSA public key string to data using ASCII encoding.
let pubKey = try! getPublicKey(from: kPubKey.data(using: .ascii))

Add Notes and Images to serialized VCF contact

I am trying to save a contact as a vcf using CNContactVCardSerialization which has worked out relatively well. I did find that apple doesn't include Notes or Images as part of the VCF. I did use a stackoverflow answer to help the images serialized but now I'm struggling with adding notes as well.
Here is what I've tried:
extension CNContactVCardSerialization {
internal class func vcardDataAppendingPhoto(vcard: Data, photoAsBase64String photo: String) -> Data? {
let vcardAsString = String(data: vcard, encoding: .utf8)
let vcardPhoto = "PHOTO;TYPE=JPEG;ENCODING=BASE64:".appending(photo)
let vcardPhotoThenEnd = vcardPhoto.appending("\nEND:VCARD")
if let vcardPhotoAppended = vcardAsString?.replacingOccurrences(of: "END:VCARD", with: vcardPhotoThenEnd) {
return vcardPhotoAppended.data(using: .utf8)
}
return nil
}
internal class func vcardDataAppendingNote(vcard: Data, note:String) -> Data? {
let vcardAsString = String(data: vcard, encoding: .utf8)
let vcardNote = "NOTE:".appending(note)
let vcardNoteThenEnd = vcardNote.appending("\nEND:VCARD")
if let vcardNoteAppended = vcardAsString?.replacingOccurrences(of: "END:VCARD", with: vcardNoteThenEnd) {
return vcardNoteAppended.data(using: .utf8)
}
return nil
}
class func data(jpegPhotoContacts: [CNContact]) throws -> Data {
var overallData = Data()
for contact in jpegPhotoContacts {
let data = try CNContactVCardSerialization.data(with: [contact])
if (contact.note != "") || contact.imageDataAvailable {
if contact.imageDataAvailable {
if let base64imageString = contact.thumbnailImageData?.base64EncodedString(),
let updatedData = vcardDataAppendingPhoto(vcard: data, photoAsBase64String: base64imageString) {
overallData.append(updatedData)
}
}
if contact.note != ""{
if let updatedData = vcardDataAppendingNote(vcard: data, note: contact.note){
overallData.append(updatedData)
}
}
} else {
overallData.append(data)
}
}
return overallData
}
}
This doesn't seem to be working. I can get either the notes or the images section to work, but not both.
I'm likely not adding to the VCF file properly.
Any help is greatly appreciated.
Its not pretty but here's what I've done and its working for now:
extension CNContactVCardSerialization {
internal class func vcardDataAppendingPhoto(vcard: Data, photoAsBase64String photo: String) -> Data? {
let vcardAsString = String(data: vcard, encoding: .utf8)
let vcardPhoto = "PHOTO;TYPE=JPEG;ENCODING=BASE64:".appending(photo)
if let vcardPhotoAppended = vcardAsString?.appending(vcardPhoto) {
return vcardPhotoAppended.data(using: .utf8)
}
return nil
}
internal class func vcardDataAppendingNote(vcard: Data, note:String) -> Data? {
let vcardAsString = String(data: vcard, encoding: .utf8)
let vcardNote = "NOTE:".appending(note)
let vcardFinal = vcardNote.appending("\n")
if let vcardNoteAppended = vcardAsString?.appending(vcardFinal) {
return vcardNoteAppended.data(using: .utf8)
}
return nil
}
internal class func vcardDataAppendingEnd(vcard: Data) -> Data? {
let vcardAsString = String(data: vcard, encoding: .utf8)
let vcardEnd = "\nEND:VCARD"
if let vcardEndAppended = vcardAsString?.appending(vcardEnd) {
return vcardEndAppended.data(using: .utf8)
}
return nil
}
internal class func vcardDataCleanEnd(vcard: Data) -> Data? {
let vcardAsString = String(data: vcard, encoding: .utf8)
if let vcardCleaned = vcardAsString?.replacingOccurrences(of: "END:VCARD", with: "\n"){
return vcardCleaned.data(using: .utf8)
}
return nil
}
class func data(jpegPhotoContacts: [CNContact]) throws -> Data {
var overallData = Data()
for contact in jpegPhotoContacts {
let data = try CNContactVCardSerialization.data(with: [contact])
if (contact.note != "") || contact.imageDataAvailable {
if let updatedData = vcardDataCleanEnd(vcard: data){
overallData = updatedData
}
if contact.note != ""{
if let updatedData = vcardDataAppendingNote(vcard: overallData, note: contact.note){
overallData = updatedData
}
}
if contact.imageDataAvailable {
if let base64imageString = contact.thumbnailImageData?.base64EncodedString(),
let updatedData = vcardDataAppendingPhoto(vcard: overallData, photoAsBase64String: base64imageString) {
overallData = updatedData
}
}
if let updatedData = vcardDataAppendingEnd(vcard: overallData){
overallData = updatedData
}
} else {
overallData.append(data)
}
}
return overallData
}
}

Converting Swift ios Networking to use Alamofire

I got a source code from a github page written in swift and implementing GoogleMaps. I now want to refactor the codes to use Alamofire and SwiftyJSON so that I can improve the code but I got confused because through my learning of swift I used Alamofire and swiftyJSON for every networking process so I am confused currently. the code below
typealias PlacesCompletion = ([GooglePlace]) -> Void
typealias PhotoCompletion = (UIImage?) -> Void
class GoogleDataProvider {
private var photoCache: [String: UIImage] = [:]
private var placesTask: URLSessionDataTask?
private var session: URLSession {
return URLSession.shared
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
func fetchPlacesNearCoordinate(_ coordinate: CLLocationCoordinate2D, radius: Double, types:[String], completion: #escaping PlacesCompletion) -> Void {
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(coordinate.latitude),\(coordinate.longitude)&radius=\(radius)&rankby=prominence&sensor=true&key=\(appDelegate.APP_ID)"
let typesString = types.count > 0 ? types.joined(separator: "|") : "food"
urlString += "&types=\(typesString)"
urlString = urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? urlString
guard let url = URL(string: urlString) else {
completion([])
return
}
if let task = placesTask, task.taskIdentifier > 0 && task.state == .running {
task.cancel()
}
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
placesTask = session.dataTask(with: url) { data, response, error in
var placesArray: [GooglePlace] = []
defer {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
completion(placesArray)
}
}
guard let data = data,
let json = try? JSON(data: data, options: .mutableContainers),
let results = json["results"].arrayObject as? [[String: Any]] else {
return
}
results.forEach {
let place = GooglePlace(dictionary: $0, acceptedTypes: types)
placesArray.append(place)
if let reference = place.photoReference {
self.fetchPhotoFromReference(reference) { image in
place.photo = image
}
}
}
}
placesTask?.resume()
}
func fetchPhotoFromReference(_ reference: String, completion: #escaping PhotoCompletion) -> Void {
if let photo = photoCache[reference] {
completion(photo)
} else {
let urlString = "https://maps.googleapis.com/maps/api/place/photo?maxwidth=200&photoreference=\(reference)&key=\(appDelegate.APP_ID)"
guard let url = URL(string: urlString) else {
completion(nil)
return
}
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
session.downloadTask(with: url) { url, response, error in
var downloadedPhoto: UIImage? = nil
defer {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
completion(downloadedPhoto)
}
}
guard let url = url else {
return
}
guard let imageData = try? Data(contentsOf: url) else {
return
}
downloadedPhoto = UIImage(data: imageData)
self.photoCache[reference] = downloadedPhoto
}
.resume()
}
}
}
any help to refactor the codes to use Alamofire and swiftyJSON would be appreciated.
Both Alamofire and SwiftyJSON have pretty decent instructions, and there are plenty of examples online to look for. However, this would be a decent starting point - you need to replace your session.dataTask and session.downloadTask with Alamofire methods. For example, instead of:
session.downloadTask(with: url) { url, response, error in
var downloadedPhoto: UIImage? = nil
defer {
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
completion(downloadedPhoto)
}
}
guard let url = url else {
return
}
guard let imageData = try? Data(contentsOf: url) else {
return
}
downloadedPhoto = UIImage(data: imageData)
self.photoCache[reference] = downloadedPhoto
}
.resume()
use this skeleton and implement your models and logic:
Alamofire
.request(url)
.responseJSON { dataResponse in
switch dataResponse.result {
case .success:
guard let json = JSON(dataResponse.data) else {
return
}
// Continue parsing
case .failure(let error):
// Handle error
print("\(error)")
}
}

Encoded JWT using PKCS8 RSA256 algorithm on jwt.io but not in application

I has a problem when I try to implementing JWT in swift 4.
I use JWT library from jwt.io.
I am trying to encrypt the payload with the PKCS8 pem filetype and RSA256 algorithm certificate.
but always error with message "The operation could not be completed. (OSStatus error -50 - RSA private key creation from data failed)"
can someone help me?
code:
let payload: [AnyHashable:Any] = ["payload":"hiden_information"]
let algorithmName = "RS256"
let path = Bundle.main.path(forResource: "priv", ofType: "pem")
let privateKeySecretData = try? Data(contentsOf: URL(fileURLWithPath: path!))
let privateKey = String(data: privateKeySecretData!, encoding: .utf8)!
let signDataHolder: JWTAlgorithmRSFamilyDataHolder = JWTAlgorithmRSFamilyDataHolder()
_ = signDataHolder.keyExtractorType(JWTCryptoKeyExtractor.privateKeyWithPEMBase64().type)
_ = signDataHolder.algorithmName(algorithmName)
_ = signDataHolder.secret(privateKey)
let signBuilder : JWTEncodingBuilder = JWTEncodingBuilder.encodePayload(payload)
_ = signBuilder.addHolder(signDataHolder)
let signResult = signBuilder.result
if ((signResult?.successResult) != nil) {
print(signResult!.successResult.encoded)
} else {
print(signResult?.errorResult.error.localizedDescription ?? "Unknown")
}
JWT version 3.0.0.-beta7 not suported PKCS8.
i use PKCS1 with RSA256 and it worked!
func encryptPayload(payload:[AnyHashable:Any])->String?
{
var resultStr: String?
var publicKeyCrypto: JWTCryptoKeyProtocol? = nil
do {
publicKeyCrypto = try JWTCryptoKeyPublic(pemEncoded: AppConstant.Scurity.publicKey, parameters: nil)
}
catch {
NSLog("error: \(error)")
}
guard let theCrypto = publicKeyCrypto else {
return nil
}
do {
let privateKeyCrypto = try JWTCryptoKeyPrivate(pemEncoded: privateKey, parameters: nil)
guard let holder = JWTAlgorithmRSFamilyDataHolder().signKey(privateKeyCrypto)?.secretData(AppConstant.Scurity.privateKey.data(using: .utf8))?.algorithmName(JWTAlgorithmNameRS256) else {return nil}
let headers : [AnyHashable:Any] = ["alg": "RS256","typ": "JWT"]
guard let encoding = JWTEncodingBuilder.encodePayload(payload).headers(headers)?.addHolder(holder) else {return nil}
let result = encoding.result
print(result?.successResult?.encoded ?? "Encoding failed")
print(result?.errorResult?.error ?? "No encoding error")
let verifyDataHolder = JWTAlgorithmRSFamilyDataHolder().signKey(theCrypto)?.secretData(publicKey.data(using: .utf8)!)?.algorithmName(JWTAlgorithmNameRS256)
let verifyResult = JWTDecodingBuilder.decodeMessage(result?.successResult?.encoded).addHolder(verifyDataHolder)?.result
if verifyResult?.successResult != nil, let result = verifyResult?.successResult.encoded {
print("Verification successful, result: \(result)")
} else {
print("Verification error: \(verifyResult!.errorResult.error)")
}
resultStr = result?.successResult.encoded
} catch {
print(error)
}
return resultStr
}

Mutable data pack into json

How could I send hashed password with sha256 via post request ?
func sha256(string: String) -> Data? {
guard let messageData = string.data(using:String.Encoding.utf8) else { return nil; }
var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes {digestBytes in
messageData.withUnsafeBytes {messageBytes in
CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
return digestData
}
This is how I am hasing a password.
Then whenever I try to pack it into an array as a [String: Any] it throws an exception in JSONSErialization 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Foundation._SwiftNSData)'
guard let loginURL = URL(string: LOGIN_URL) else {
print("Error: cannot create URL")
return
}
var loginURLRequest = URLRequest(url: loginURL)
loginURLRequest.httpMethod = "POST"
let content: [String: Any] = ["username": username, "passwordHash": password]
let json: Data
do {
json = try JSONSerialization.data(withJSONObject: content, options: [])
loginURLRequest.httpBody = json
} catch {
print("Error: Can not create JSON")
return
}
Thanks in advance!
What you probably want to do is encode the hash as a hex string. Given your sha256 function defined above, The following will do that:
let password = sha256(string: "myPassword")
.map { return String(format: "%02x", $0) }
.joined()
let content = [
"username": username,
"passwordHash": password
]
Alternatively, you could change your sha256 function to return a string (and make it an extension of string):
func sha256() -> String? {
guard let messageData = data(using:String.Encoding.utf8) else {
return nil
}
var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes { digestBytes in
withUnsafeBytes { messageBytes in
CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
return digestData.map { return String(format: "%02x", $0) }.joined()
}
Note: I'm winging this a little since CommonCrypto won't work in a playground and I'm too lazy to wrap a whole project around this, but the important parts are here.
Use data.map to iterate over each byte in the hash
convert it to a hex string with String(format: "%02x"...)
put it all back together with joined()