How to Hash Nsstring With Sha1 in Swift

How to hash NSString with SHA1 in Swift?

Your Objective-C code (using a NSString category) can be directly translated to Swift
(using a String extension).

First you have to create a "bridging header" and add

#import <CommonCrypto/CommonCrypto.h>

Then:

extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
for byte in digest {
output.appendFormat("%02x", byte)
}
return output as String
}
}

println("Hello World".sha1())

This can be written slightly shorter and Swifter as

extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = map(digest) { String(format: "%02hhx", $0) }
return "".join(hexBytes)
}
}

Update for Swift 2:

extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joinWithSeparator("")
}
}

To return a Base-64 encoded string instead of a hex encoded string,
just replace

        let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joinWithSeparator("")

with

        return NSData(bytes: digest, length: digest.count).base64EncodedStringWithOptions([])

Update for Swift 3:

extension String {
func sha1() -> String {
let data = self.data(using: String.Encoding.utf8)!
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}

To return a Base-64 encoded string instead of a hex encoded string,
just replace

        let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()

by

        return Data(bytes: digest).base64EncodedString()

Update for Swift 4:

The bridging header file is no longer needed, one can import CommonCrypto instead:

import CommonCrypto

extension String {
func sha1() -> String {
let data = Data(self.utf8)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}

Update for Swift 5:

The Data.withUnsafeBytes() method now calls the closure with an UnsafeRawBufferPointer to, and baseAddress is used to pass the initial address to the C function:

import CommonCrypto

extension String {
func sha1() -> String {
let data = Data(self.utf8)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}

Creating SHA1 Hash from NSString

I have this in a category on NSString (available at https://github.com/hypercrypt/NSString-Hashes):

#import <CommonCrypto/CommonDigest.h>

...

- (NSString *)sha1
{
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
uint8_t digest[CC_SHA1_DIGEST_LENGTH];

CC_SHA1(data.bytes, (CC_LONG)data.length, digest);

NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];

for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
{
[output appendFormat:@"%02x", digest[i]];
}

return output;
}

Starting with Xcode 10.0, you should use import CommonCrypto instead since it is now natively available in Swift! If you have recently migrated to Xcode 10.0 and use the old approach, this can be your cue to make the change:

Command CompileSwift failed with a nonzero exit code

Swift 3 making sha1, sha256 and md5 functions

You'd better use Swift Data in Swift 3.

Data

And when working with Data, you need to use withUnsafeBytes(_:) or withUnsafeMutableBytes(_:), where you were using bytes or mutableBytes respectively.

withUnsafeBytes(_:)

withUnsafeMutableBytes(_:)

extension Data {
func hexString() -> String {
let string = self.map{Int($0).hexString()}.joined()
return string
}

func MD5() -> Data {
var result = Data(count: Int(CC_MD5_DIGEST_LENGTH))
_ = result.withUnsafeMutableBytes {resultPtr in
self.withUnsafeBytes {(bytes: UnsafePointer<UInt8>) in
CC_MD5(bytes, CC_LONG(count), resultPtr)
}
}
return result
}

/*
... nearly the same for `SHA1` and `SHA256`.
*/
}

extension String {
func hexString() -> String {
return self.data(using: .utf8)!.hexString()
}

func MD5() -> String {
return self.data(using: .utf8)!.MD5().hexString()
}

/*
... nearly the same for `SHA1` and `SHA256`.
*/
}

I prefer making computed properties than no-argument methods (for relatively light-tasks). You need to fix all parts using them, but you can write something like this:

extension Int {
var hexString: String {
return ...
}
}
extension Data {
var hexString: String {
let string = self.map{Int($0).hexString}.joined()
return string
}

var MD5: Data {
var result = Data(count: Int(CC_MD5_DIGEST_LENGTH))
_ = result.withUnsafeMutableBytes {resultPtr in
self.withUnsafeBytes {(bytes: UnsafePointer<UInt8>) in
CC_MD5(bytes, CC_LONG(count), resultPtr)
}
}
return result
}

/*
... nearly the same for `SHA1` and `SHA256`.
*/
}

extension String {
var hexString: String {
return self.data(using: .utf8)!.hexString
}

var MD5: String {
return self.data(using: .utf8)!.MD5.hexString
}

/*
... nearly the same for `SHA1` and `SHA256`.
*/
}

There may be a quicker fix for your code using NSData, but I recommend you to move to Data in Swift 3.

Create hash in swift using key and message

The last parameter of the CCHmac() function has the type UnsafeMutablePointer<Void> because
that's where the result is written to. You have to declare cHMAC as variable
and pass it as an in-out expression with &. In addition, some type conversions
are necessary:

var cHMAC = [CUnsignedChar](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), cKey, UInt(cKey.count), cData, UInt(cData.count), &cHMAC)

How to capture NSData with SHA1 hash in NSString?

SHA-1 is a 20-bytes binary chunk, not a valid UTF-8 string. This is why NSString's initWithData:encoding: fails. What is commonly used for a representation of hash sums is a HEX string. Basically, what you need is [hashedData description].

How to do HMAC-SHA1 in swift

You should definitely use CommonCrypto because it is already available on every device, well tested and fast. You just need to add a bridging header containing

#import <CommonCrypto/CommonCrypto.h>

As already stated, you can look up here how to add the bridging header.

To calculate the HMAC you just need this extension:

extension String {

func hmac(key: String) -> String {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), key, key.count, self, self.count, &digest)
let data = Data(bytes: digest, count: Int(CC_SHA1_DIGEST_LENGTH))
return data.map { String(format: "%02hhx", $0) }.joined()
}

}

Example:

let result = "test".hmac(key: "test")

Result:

0c94515c15e5095b8a87a50ba0df3bf38ed05fe6

Get a macOS Keychain certificate's SHA1 hash in Swift

You can achieve it by performing the hash yourself. The fingerprints are not part of the certificate itself. More info about that over here.

import CryptoKit

let certificate = ...

let der = SecCertificateCopyData(certificate) as Data
let sha1 = Insecure.SHA1.hash(data: der)
let sha256 = SHA256.hash(data: der)

This can be created in an extension too. I've used CommonCrypto in the extension.

import CommonCrypto

extension SecCertificate {
var sha1: Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
let der = SecCertificateCopyData(self) as Data
_ = CC_SHA1(Array(der), CC_LONG(der.count), &digest)
return Data(digest)
}

var sha256: Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
let der = SecCertificateCopyData(self) as Data
_ = CC_SHA256(Array(der), CC_LONG(der.count), &digest)
return Data(digest)
}
}

I'd like to mention that SHA-1 hashes of certificates are deprecated since like 2017 and websites and tech giants are starting to drop support for them.

Playground example

import CryptoKit
import Foundation

class CertificateStuff: NSObject, URLSessionDelegate {
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard let serverTrust = challenge.protectionSpace.serverTrust else {
completionHandler(.rejectProtectionSpace, nil)
return
}

for index in 0 ..< SecTrustGetCertificateCount(serverTrust) {
let certificate = SecTrustGetCertificateAtIndex(serverTrust, index)!

let der = SecCertificateCopyData(certificate)
let sha1 = Insecure.SHA1.hash(data: der as Data)
let sha256 = SHA256.hash(data: der as Data)

print(certificate)
print(sha1)
print(sha256)
print()
}
completionHandler(.performDefaultHandling, nil)
}

func request(_ done: @escaping (Result<Data, Error>) -> Void) {
let url = URL(string: "https://security.stackexchange.com/questions/14330/what-is-the-actual-value-of-a-certificate-fingerprint")!
let request = URLRequest(url: url)
URLSession(configuration: .default, delegate: self, delegateQueue: nil).dataTask(with: request) { (d, r, e) in
if let e = e {
print(e)
return
}
print(d!)
}.resume()
}
}

CertificateStuff().request { result in print(result) }



Related Topics



Leave a reply



Submit