0

I am using the following code for getting SHA 256 of UIImage

(source: https://stackoverflow.com/a/50931949/10451073)

extension UIImage{

    public func sha256() -> String{
        if let imageData = cgImage?.dataProvider?.data as? Data {
            return hexStringFromData(input: digest(input: imageData 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
    }
}

I'm using it as:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

        if let file = info[UIImagePickerControllerOriginalImage]
        {
            print((file as? UIImage)?.sha256())
        }
        self.dismiss(animated: true, completion: nil)


    }

However I'm getting SHA 256 of any UIImage wrong. For example, upon checking SHA 256 for this image- https://drive.google.com/open?id=1p9n01qOFahr6I1Q7FPzoDCKcjLjYVivl, I am getting value as 1d6a7c377157c4511183706033898c76c090924ecbf9d47ddff7243237dc9243 instead of getting correct value being 360cd85c64b5e672c48ef948df689a17e41b80a84b9d3d2039143e47a9473395 (verified from: https://hash.online-convert.com/sha256-generator )

Please help me understand what's wrong here and how I should change the above code to get the correct SHA 256 value. Help is much appreciated. Thank you.

EDIT: Upon discussion with @Paulw11 in his answer below I got it that I need to get hash of the file instead of the image. However I am not sure how to change this code to get the same. Please help me with the same.

Enrik Qaz
  • 243
  • 4
  • 14
  • 2
    A `UIImage` is not a PNG or a JPEG file; You won't get the same hash for a PNG and a `UIImage` created from that PNG. You need to perform the hash on the file, not the `UIImage` – Paulw11 Nov 06 '18 at 06:29

1 Answers1

1

A UIImage is not a PNG or a JPEG file; You won't get the same hash for a PNG file and a UIImage created from that file.

You need to perform the hash on the file, not the UIImage if you want to get a value you can compare to another hash of the same file.

You can use the imageURL entry of the dictionary to get the file url for the image.

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

    if let file = info[UIImagePickerControllerImageURL] as? URL
    {

        try {
            let imageData = Data(contentsOf:file)
            let hash = imageData.sha256
        } catch {
            print(error)
        }
    }
    self.dismiss(animated: true, completion: nil)
}

extension Data {

    public var sha256:String {
        get {
            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
    }
}
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • can you help rewrite a similar algo for sha 256 for a file rather than UIImage? Thank you. – Enrik Qaz Nov 06 '18 at 06:34
  • The code would be basically the same except you need to get the `Data` instance from the file; You can use [`Data(contentsOf:options)`](https://developer.apple.com/documentation/foundation/data/1779617-init) to load the data from a file or server url – Paulw11 Nov 06 '18 at 06:37
  • Please check my edited question details and please help me understand what I can fix there for this? – Enrik Qaz Nov 06 '18 at 06:39
  • This code doesn't run on iOS version below 11.0 :( – Enrik Qaz Nov 06 '18 at 07:27
  • Prior to iOS 11 there is no way to get the underlying file. – Paulw11 Nov 06 '18 at 07:30
  • Is there a way I can get SHA 256 of just UIImageJPEGRepresentation(image, 1.0) than runs on all versions from 9.3? Sorry to fire you up with so many questions, but it is great to help people like us. Thank you. – Enrik Qaz Nov 06 '18 at 07:31
  • You could try, but jpeg is lossy, so there is a possibility that the hash won’t match. If the source was a png then the hash definitely won’t match – Paulw11 Nov 06 '18 at 07:32
  • Yes that's why I want to match hash of imageData after I do- let imageData = UIImageJPEGRepresentation(image, 1.0). Trying to get hash of image after getting its jpeg and then sending the jpeg rather than original image. – Enrik Qaz Nov 06 '18 at 07:35
  • I marked this is as correct answer as it was helpful for iOS 11+. – Enrik Qaz Nov 06 '18 at 08:40