So I have this code to generate for HMAC-SHA1
let key = "foo".toSHA1()
let data = "bar"
var results = [CUnsignedChar](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), key, key.count, data, data.count, &results)
let hmacData:NSData = NSData(bytes: results, length: (Int(CC_SHA1_DIGEST_LENGTH)))
var bytes = [UInt8](repeating: 0, count: hmacData.length)
hmacData.getBytes(&bytes, length: hmacData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02hhx", UInt8(byte))
}
print(hexString)
and this code for converting key string to SHA1
func toSHA1() -> 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: "%02x", $0) }
return hexBytes.joined()
}
and the result is
faa3c04b058d38cecf1243421a596742a6cf1188
so using this onlineHMAC Generator outputs the same result. But my desired output should be
38b24d28d64f2459d42d1ecd1c9fa375ffeb369f
and I can achieve this by changing the Key type to HEX in the page that I provided.
So my problem now is how do I get the same output in my code? Do I need to convert key to hex?
Fixed it by passing digest as key instead of converting it to string.
Here's the updated code
let key = "foo".toSHA1()
let data = "bar"
var results = [CUnsignedChar](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), key, key.count, data, data.count, &results)
let hmacData:NSData = NSData(bytes: results, length: (Int(CC_SHA1_DIGEST_LENGTH)))
var bytes = [UInt8](repeating: 0, count: hmacData.length)
hmacData.getBytes(&bytes, length: hmacData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02hhx", UInt8(byte))
}
print(hexString)
func toSHA1() -> [UInt8] {
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)
}
return digest
}
Related
We need to verify hash value coming from the server. Android part is working. On iOS side we use CryptoKit to generate hash value.
Passing as paramater "jsonString" from which the hash key needs to be generated and "hasValue" which needs to be matched. While both functions use the same algorythm the outcome is always different.
let digest = SHA512.hash(data: jsonStringWithSaltBytes)
let hasBytes = digest.bytes
In computeHas function when we try to get the let hasBytes = digest.bytes it always give different HashBytes after SHA512.hash bytes conversion.
import CryptoKit
extension Digest {
var bytes: [UInt8] { Array(makeIterator()) }
var data: Data { Data(bytes) }
}
func generateHasKey(jsonString: String, hasValue: String) -> Bool {
let data = Data(base64Encoded: hasValue)
let hashWithSaltByte: [UInt8] = Array(data!)
let hashSizeByts: Int = 512 / 8
if hashWithSaltByte.count < hashSizeByts {
return false
}
var saltBytesArr: [UInt8] = Array(repeating: 0, count: (hasWithSaltByte.count - hashSizeInByts))
for index in 0..<saltBytes.count {
saltBytesArr[index] = hasWithSaltByte[hashSizeInByts + index]
}
let expectedHasString = computeHas(jsonString: jsonString, saltBytes: saltBytesArr)
if expectedHashString == hasValue {
print("Hash value match..")
return true
}else {
print("Hash value did not match..")
return false
}
}
func computeHash(jsonString: String, saltBytes: [UInt8]) -> String {
let saltBytes = saltBytes
let jsonStringBytes: [UInt8] = Array(jsonString.utf8)
var jsonStringWithSaltBytes: [UInt8] = Array(repeating: 0, count: (jsonStringBytes.count + saltBytes.count))
for index in 0..<jsonStringBytes.count {
jsonStringWithSaltBytes[index] = jsonStringBytes[index]
}
for index in 0..<saltBytes.count {
jsonStringWithSaltBytes[jsonStringBytes.count + index] = saltBytes[index]
}
let digest = SHA512.hash(data: jsonStringWithSaltBytes)
let hashBytes = digest.bytes
var hashWithSaltBytes: [UInt8] = Array(repeating: 0, count: (hashBytes.count + saltBytes.count))
for index in 0..<hasBytes.count {
hashWithSaltBytes[index] = hashBytes[index]
}
for index in 0..<saltBytes.count {
hashWithSaltBytes[hashBytes.count + index] = saltBytes[index]
}
let FinalsaltData = Data(hasWithSaltBytes)
let finalHashString = FinalsaltData.base64EncodedString()
return finalHashString ?? ""
}
In Swift 4 we could use
var md5: String? {
guard let data = self.data(using: .utf8) else { return nil }
let hash = data.withUnsafeBytes { (bytes: UnsafePointer<Data>) -> [UInt8] in
var hash: [UInt8] = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(bytes, CC_LONG(data.count), &hash)
return hash
}
return hash.map { String(format: "%02x", $0) }.joined()
}
But in Swift 5 withUnsafeBytes uses UnsafeRawBufferPointer instead of UnsafePointer. How to change md5 function?
Swift 5 version: Use UnsafeRawBufferPointer as type of the closure argument, and bytes.baseAddress to pass address to the Common Crypto function:
import Foundation
import CommonCrypto
extension String {
var md5: String {
let data = Data(self.utf8)
let hash = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash)
return hash
}
return hash.map { String(format: "%02x", $0) }.joined()
}
}
(Note that the conversion of a string to UTF-8 data cannot fail, there is no need to return an optional.)
CC_MD5 has been deprecated with the iOS 13. Instead, you can use CC_SHA256.
In iOS 13 and above there is a framework CryptoKit which is a wrapper around CommonCrypto framework and around the MD5 hash function.
import CryptoKit
let d = "Hello"
let r = Insecure.MD5.hash(data: d.data(using: .utf8)!)
print(r)
/*Output: MD5 digest: 8b1a9953c4611296a827abf8c47804d7*/
In iOS 13 and above there is a framework CryptoKit. Try using this:
extension Data {
var md5: String {
Insecure.MD5
.hash(data: self)
.map {String(format: "%02x", $0)}
.joined()
}
}
Eskimo's solution
Below is a variant based on a solution proposed by Eskimo from Apple in Swift Forum post withUnsafeBytes Data API confusion:
extension String {
func md5() -> String {
let data = Data(utf8)
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
data.withUnsafeBytes { buffer in
_ = CC_MD5(buffer.baseAddress, CC_LONG(buffer.count), &hash)
}
return hash.map { String(format: "%02hhx", $0) }.joined()
}
}
Note it is effectively the same as Martin R's solution, but a line shorter (no return hash).
Solution using NSData
This is an even shorter solution using bridging to NSData.
extension String {
func md5() -> String {
let data = Data(utf8) as NSData
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(data.bytes, CC_LONG(data.length), &hash)
return hash.map { String(format: "%02hhx", $0) }.joined()
}
}
CC_MD5 gives back
'CC_MD5' was deprecated in iOS 13.0: This function is cryptographically broken and should not be used in security contexts. Clients should migrate to SHA256 (or stronger).
so to have a flexible solution:
//OLD
import CommonCrypto
//new:
import CryptoKit
extension String {
var md5: String {
if #available(iOS 13.0, *) {
guard let d = self.data(using: .utf8) else { return ""}
let digest = Insecure.MD5.hash(data: d)
let h = digest.reduce("") { (res: String, element) in
let hex = String(format: "%02x", element)
//print(ch, hex)
let t = res + hex
return t
}
return h
} else {
// Fall back to pre iOS13
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = [UInt8](repeating: 0, count: length)
if let d = self.data(using: .utf8) {
_ = d.withUnsafeBytes { body -> String in
CC_MD5(body.baseAddress, CC_LONG(d.count), &digest)
return ""
}
}
let result = (0 ..< length).reduce("") {
$0 + String(format: "%02x", digest[$1])
}
return result
}// end of fall back
}
}
to test:
func MD5Test() -> Bool{
let HASHED = "5D41402ABC4B2A76B9719D911017C592"
let s = "hello"
let md5_1 = s.md5
if md5_1.uppercased() != HASHED{
return false
}
return true
}
I am working with crypto swift library But I have to write below logic in swift as I am very new in kotlin to understand the syntax.
Any leads would be greatly appreciated
fun decryptAES(data: ByteArray, secretKey: ByteArray): ByteArray {
try {
val byteBuffer = ByteBuffer.wrap(data)
val ivLength = byteBuffer.int
if (ivLength < 12 || ivLength >= 16) {
throw IllegalArgumentException("invalid iv length")
}
val iv = ByteArray(ivLength)
byteBuffer.get(iv)
val cipherText = ByteArray(byteBuffer.remaining())
byteBuffer.get(cipherText)
val encryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding")
encryptCipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(secretKey, "AES"), GCMParameterSpec(128, iv))
return encryptCipher.doFinal(cipherText)
} finally {
Arrays.fill(secretKey, 0.toByte())
}
}
After spending 2 days finally I am able to convert this code in Swift Hope this helps others
func decryptCode(_ cipher:String, _ key:String)-> String{
var keyBytes: [UInt8] = []
var codeBytes: [UInt8] = []
var code = ""
if let keyData = NSData(base64Encoded:key, options: .ignoreUnknownCharacters) {
keyBytes = [UInt8](keyData as Data)
}
if let codeData = NSData(base64Encoded: cipher, options: .ignoreUnknownCharacters) {
codeBytes = [UInt8](codeData as Data)
}
// First 4 bytes define the IV length
// next 12 to 16 bytes are reserve for IV
//and remaining bytes are actual cipher text
debugPrint(codeBytes)
let sizeOfIV = 4
let ivUInt8Array = Array([UInt8](codeBytes)[0 ..< sizeOfIV])
let ivLength:Int = Int(ivUInt8Array.reduce(0, +))
if ivLength < 12 || ivLength >= 16{
return code
}
let codeBytescount = [UInt8](codeBytes).count
let remainingBytes = Array([UInt8](codeBytes)[sizeOfIV ..< codeBytescount])
let remainingBytescount = [UInt8](remainingBytes).count
let iv = Array([UInt8](remainingBytes)[0 ..< ivLength])
let cipher = Array([UInt8](remainingBytes)[ivLength ..< remainingBytescount])
do{
let gcm = GCM(iv: iv, mode: .combined)
let aes = try AES(key: keyBytes, blockMode: gcm, padding: .pkcs5)
IFLOG("aes created")
let decrypted = try aes.decrypt(cipher)
IFLOG("decrypted completed")
if let decryptedString = String(bytes: decrypted, encoding: .utf8) {
code = decryptedString
}
debugPrint(code)
}catch let error as AES.Error {
debugPrint(error.localizedDescription)
return code
} catch {
return code
}
return code
}
var val = 1240;
convert into little endian formate swift 3
Ex: 1500 (0x5DC) to 0xDC050000
let value = UInt16(bigEndian: 1500)
print(String(format:"%04X", value.bigEndian)) //05DC
print(String(format:"%04X", value.littleEndian)) //DC05
Make sure you are actually using the bigEndian initializer.
With 32-bit integers:
let value = UInt32(bigEndian: 1500)
print(String(format:"%08X", value.bigEndian)) //000005DC
print(String(format:"%08X", value.littleEndian)) //DC050000
If you want 1500 as an array of bytes in little-endian order:
var value = UInt32(littleEndian: 1500)
let array = withUnsafeBytes(of: &value) { Array($0) }
If you want that as a Data:
let data = Data(array)
Or, if you really wanted that as a hex string:
let string = array.map { String(format: "%02x", $0) }.joined()
let timeDevide = self.setmiliSecond/100
var newTime = UInt32(littleEndian: timeDevide)
let arrayTime = withUnsafeBytes(of: &newTime)
{Array($0)}
let timeDelayValue = [0x0B] + arrayTime
You can do something like
//: Playground - noun: a place where people can play
import UIKit
extension String {
func hexadecimal() -> Data? {
var data = Data(capacity: count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, range: NSRange(location: 0, length: utf16.count)) { match, _, _ in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard !data.isEmpty else { return nil }
return data
}
}
func convertInputValue<T: FixedWidthInteger>(_ inputValue: Data) -> T where T: CVarArg {
let stride = MemoryLayout<T>.stride
assert(inputValue.count % (stride / 2) == 0, "invalid pack size")
let fwInt = T.init(littleEndian: inputValue.withUnsafeBytes { $0.pointee })
let valuefwInt = String(format: "%0\(stride)x", fwInt).capitalized
print(valuefwInt)
return fwInt
}
var inputString = "479F"
var inputValue: Data! = inputString.hexadecimal()
let val: UInt16 = convertInputValue(inputValue) //9F47
inputString = "479F8253"
inputValue = inputString.hexadecimal()
let val2: UInt32 = convertInputValue(inputValue) //53829F47
I am relatively new to Swift and i'm stuck encrypting using HMAC and SHA1. I Found the following answer https://stackoverflow.com/a/24411522/4188344 but i can't work out how to implement this properly. Any help would be amazing.
Problem solved! First off i wasn't using the string function properly... I ended up with this:
let hmacResult:String = "myStringToHMAC".hmac(HMACAlgorithm.SHA1, key: "myKey")
Then I had forgotten I needed to base64 encode the hmac result. So i modified the string function linked in my question to...
enum HMACAlgorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
func toCCHmacAlgorithm() -> CCHmacAlgorithm {
var result: Int = 0
switch self {
case .MD5:
result = kCCHmacAlgMD5
case .SHA1:
result = kCCHmacAlgSHA1
case .SHA224:
result = kCCHmacAlgSHA224
case .SHA256:
result = kCCHmacAlgSHA256
case .SHA384:
result = kCCHmacAlgSHA384
case .SHA512:
result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}
func digestLength() -> Int {
var result: CInt = 0
switch self {
case .MD5:
result = CC_MD5_DIGEST_LENGTH
case .SHA1:
result = CC_SHA1_DIGEST_LENGTH
case .SHA224:
result = CC_SHA224_DIGEST_LENGTH
case .SHA256:
result = CC_SHA256_DIGEST_LENGTH
case .SHA384:
result = CC_SHA384_DIGEST_LENGTH
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
extension String {
func hmac(algorithm: HMACAlgorithm, key: String) -> String {
let cKey = key.cStringUsingEncoding(NSUTF8StringEncoding)
let cData = self.cStringUsingEncoding(NSUTF8StringEncoding)
var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatedValue: 0)
CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, strlen(cKey!), cData!, strlen(cData!), &result)
var hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
var hmacBase64 = hmacData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding76CharacterLineLength)
return String(hmacBase64)
}
}
This is giving me the correct result of
lGCtbW+DNHFraNoxPGK3trgM/98=
Here is #David Wood's solution updated for Swift 3:
enum HMACAlgorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
func toCCHmacAlgorithm() -> CCHmacAlgorithm {
var result: Int = 0
switch self {
case .MD5:
result = kCCHmacAlgMD5
case .SHA1:
result = kCCHmacAlgSHA1
case .SHA224:
result = kCCHmacAlgSHA224
case .SHA256:
result = kCCHmacAlgSHA256
case .SHA384:
result = kCCHmacAlgSHA384
case .SHA512:
result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}
func digestLength() -> Int {
var result: CInt = 0
switch self {
case .MD5:
result = CC_MD5_DIGEST_LENGTH
case .SHA1:
result = CC_SHA1_DIGEST_LENGTH
case .SHA224:
result = CC_SHA224_DIGEST_LENGTH
case .SHA256:
result = CC_SHA256_DIGEST_LENGTH
case .SHA384:
result = CC_SHA384_DIGEST_LENGTH
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
extension String {
func hmac(algorithm: HMACAlgorithm, key: String) -> String {
let cKey = key.cString(using: String.Encoding.utf8)
let cData = self.cString(using: String.Encoding.utf8)
var result = [CUnsignedChar](repeating: 0, count: Int(algorithm.digestLength()))
CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, Int(strlen(cKey!)), cData!, Int(strlen(cData!)), &result)
let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)
return String(hmacBase64)
}
}
// usage:
let hmacResult: String = "myStringToHMAC".hmac(algorithm: HMACAlgorithm.SHA1, key: "foo")
Here is how to create a Swift 4 extension:
Bridging headers file
#import <CommonCrypto/CommonCrypto.h>
Code
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)
return data.map { String(format: "%02hhx", $0) }.joined()
}
}
Example
let result = "test".hmac(key: "test")
Result
0c94515c15e5095b8a87a50ba0df3bf38ed05fe6
If you want the same result in hexadecimal format, you can use the following extension:
extension String {
func hmac(algorithm: HMACAlgorithm, key: String) -> String {
let cKey = key.cStringUsingEncoding(NSUTF8StringEncoding)
let cData = self.cStringUsingEncoding(NSUTF8StringEncoding)
var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatedValue: 0)
let length : Int = Int(strlen(cKey!))
let data : Int = Int(strlen(cData!))
CCHmac(algorithm.toCCHmacAlgorithm(), cKey!,length , cData!, data, &result)
let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
var bytes = [UInt8](count: hmacData.length, repeatedValue: 0)
hmacData.getBytes(&bytes, length: hmacData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02hhx", UInt8(byte))
}
return hexString
}
}
I have used this module which I added to my project as a framework:
https://github.com/CryptoCoinSwift/SHA256-Swift
And I have also added the following String extension to SHA256.swift:
public extension String {
func sha256(key: String) -> String {
let inputData: NSData = self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let keyData: NSData = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
let algorithm = HMACAlgorithm.SHA256
let digestLen = algorithm.digestLength()
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
CCHmac(algorithm.toCCEnum(), keyData.bytes, UInt(keyData.length), inputData.bytes, UInt(inputData.length), result)
let data = NSData(bytes: result, length: digestLen)
result.destroy()
return data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
}
}
This way producing a base64-encoded signature from a String can be done like this:
let signature: String = "\(payload)".sha256(secretKey)
I have checked the above answers and found it so lengthy.
Solution: I got third party: IDZSwiftCommonCrypto
Use pod: pod 'IDZSwiftCommonCrypto'
and use the following function to achieve desired output:
func getHMacSHA1(forMessage message: String, key: String) -> String? {
let hMacVal = HMAC(algorithm: HMAC.Algorithm.sha1, key: key).update(string: message)?.final()
if let encryptedData = hMacVal {
let decData = NSData(bytes: encryptedData, length: Int(encryptedData.count))
let base64String = decData.base64EncodedString(options: .lineLength64Characters)
print("base64String: \(base64String)")
return base64String
} else {
return nil
}
}
For check the result use following website:
https://hash.online-convert.com/sha1-generator
Tested in Swift 4.0
Using raw bytes for key and message and not encoding to utf8:
static func getHmac_X_Sha1() -> [UInt8] {
let msg:[UInt8] = message_uint8;
let msgLen = message_uint8.count;
let digestLen = Int(CC_SHA1_DIGEST_LENGTH)
let digest = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
let keyStr:[UInt8] = key_uint8
let keyLen = key_uint8.count
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), keyStr, keyLen, msg, msgLen, digest)
//Build a hex string of result
let hash_hex_string = NSMutableString()
for i in 0..<digestLen {
hash_hex_string.appendFormat("%02x", result[i])
}
//print(hash_hex_string)
result.deallocate()
// Resolve hash_hex_string to byte array
let hash_bytes:[UInt8] = hexStringToBytes(String(hash_hex_string))
return hash_bytes
}
//Return a byte array from hex string input
private static func hexStringToBytes(_ string: String) -> [UInt8]? {
let length = string.characters.count
if length & 1 != 0 {
return nil
}
var bytes = [UInt8]()
bytes.reserveCapacity(length/2)
var index = string.startIndex
for _ in 0..<length/2 {
let nextIndex = string.index(index, offsetBy: 2)
if let b = UInt8(string[index..<nextIndex], radix: 16) {
bytes.append(b)
} else {
return nil
}
index = nextIndex
}
return bytes
}
In Swift 4 You need library CommonCrypto https://forums.developer.apple.com/thread/46477
#import <CommonCrypto/CommonCrypto.h>
And you can create extension with base64
extension String {
func hmac(key: String) -> String {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), key, key.count, self, self.count, &digest)
let data = Data(bytes: digest)
return data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
}
}
Usege:
print("HMAC_SHA256:".hmac(key: "MyKey"))
Result:
6GM2evJeNZYdP3OjPcKmg8TDzILSQAjy4NGhCHnBH5M=