I want to implement digest authentication with Swift. Unfortunately after hours of testing I saw that using this method of creating the md5 hash gives me the wrong result.
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()
}
}
using this string
let test = "test:testrealm#host.com:pwd123".md5
test has the value: 4ec2086d6f09366e4683dbdc5809444a but it should have 939e7578ed9e3c518a452acee763bce9 (following a digest auth. documentation). So me digest was always calculated in the wrong manner.
Thanks
Arnold
My error, it gives me the right result. I had an error computing the hash. The string extension is ok.
Related
So when logging in to steam, I need my password encrypted with RSA public key they provided. But when I encrypt with SecKeyEncryptedData and sent it for authentication, it says my password is incorrect. I think maybe it's a problem with the format of encryption but I cannot figure it out. Please help me with that.
static func encrypt(string: String, mod: String, exp: String) -> String? {
let keyString = self.rsaPublicKeyder(mod: mod, exp: exp)
guard let data = Data(base64Encoded: keyString) else { return nil }
var attributes: CFDictionary {
return [kSecAttrKeyType : kSecAttrKeyTypeRSA,
kSecAttrKeyClass : kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits : 2048,
kSecReturnPersistentRef : kCFBooleanTrue!] as CFDictionary
}
var error: Unmanaged<CFError>? = nil
guard let secKey = SecKeyCreateWithData(data as CFData, attributes, &error) else {
print(error.debugDescription)
return nil
}
guard let result = SecKeyCreateEncryptedData(secKey, SecKeyAlgorithm.rsaEncryptionPKCS1, string.data(using: .utf8)! as CFData, &error) else{
print(error.debugDescription)
return nil
}
return (result as Data).base64EncodedString()
}
}
The self.rsaPublicKeyder function's role is to convert the mod and exp format to PKCS#8 DER format and it can import with SecKeyCreateWithData. And I tried the result Der in cyberchef which seems no problem.
I tried using BigInt library to encrypt myself, but still failed. How????
class RSA{
static func encrypt(string: String, mod: String, exp: String) -> String
{
let secret = pkcs1pad2(data: string.data(using: .utf8)!, keySize: mod.count / 2)!
return secret.power(BigUInt(exp, radix: 16)!, modulus: BigUInt(mod, radix: 16)!).serialize().base64EncodedString()
}
static func pkcs1pad2(data: Data, keySize: Int) -> BigUInt?{
if (keySize < data.count + 11){
return nil;
}
var rndData: [UInt8] = [UInt8](repeating: 0, count: keySize - 3 - data.count)
let status = SecRandomCopyBytes(kSecRandomDefault, rndData.count, &rndData)
for i in 0..<rndData.count{
if rndData[i] == 0{
rndData[i] = UInt8(i+1)
}
}
guard status == errSecSuccess else{
return nil
}
return BigUInt(Data([0x00, 0x02]) + Data(rndData) + Data([0x00]) + data)
}
}
I have cost a bunch of time on this problem and I still don't know which part of my code is doing wrong. I have put all codes on Github. If you can help me with it I'll be so grateful.
https://github.com/MTAwsl/iAuth/tree/dev
I tried to debug myself using Fiddler and I found the answer.
Firstly, when a base64 string is transferred through HTTP GET method, it needs to be encoded by "%" encoding. So, String.addingPercentEncoding should be called to encode the string with proper encoding. But in CharacterSet.urlHostAllowed set, it did not include the "+" character so when the server decoding the data from base64, it treats "+" as space, which is definitely not what we wanted. I add the extension to String module and it solved the problem. Besides, yeah, both the BigInt and SecKey method works. There's nothing about the RSA encryption.
extension String{
var encoded: String? {
var urlB64Encoded: CharacterSet = .urlHostAllowed
urlB64Encoded.remove(charactersIn: "+")
return self.addingPercentEncoding(withAllowedCharacters: urlB64Encoded)
}
}
I have a string and a key, which i want to generate an HMAC SHA256 from it. Although i'm using 2 libs
IDZSwiftCommonCrypto and CryptoSwift
and this answer
Nothing really worked for me. My source of truth are those 2 websites
https://myeasywww.appspot.com/utility/free/online/Crypt_Decrypt-MD5-AES-HMAC-SHA-DES-RABBIT/en?command=UTILITY&ID=2
and
https://www.freeformatter.com/hmac-generator.html#ad-output
Which they always generate the correct hash key for my case.
Any idea in what can work here? Some code samples
For IDZSwiftCommonCrypto
func getHMacSHA256(forMessage message: String, key: String) -> String? {
let hMacVal = HMAC(algorithm: HMAC.Algorithm.sha256, 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
}
}
And for CryptoSwift
let password: Array<UInt8> = Array(payload.utf8)
let salt: Array<UInt8> = Array("somekey".utf8)
let signedBody = try? HKDF(password: password, salt: salt, variant: .sha256).calculate()
But nothing really works like the sources of truth.Any idea?
If you target iOS 13.0+ or macOS 10.15+, use Apple's CryptoKit
import CryptoKit
let secretString = "my-secret"
let key = SymmetricKey(data: Data(secretString.utf8))
let string = "An apple a day keeps anyone away, if you throw it hard enough"
let signature = HMAC<SHA256>.authenticationCode(for: Data(string.utf8), using: key)
print(Data(signature).map { String(format: "%02hhx", $0) }.joined()) // 1c161b971ab68e7acdb0b45cca7ae92d574613b77fca4bc7d5c4effab89dab67
I've been using this:
import Foundation
enum CryptoAlgorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
var HMACAlgorithm: 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)
}
var digestLength: Int {
var result: Int32 = 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: CryptoAlgorithm, key: String) -> String {
let str = self.cString(using: String.Encoding.utf8)
let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
let digestLen = algorithm.digestLength
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
let keyStr = key.cString(using: String.Encoding.utf8)
let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))
CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
let digest = stringFromResult(result: result, length: digestLen)
result.deallocate(capacity: digestLen)
return digest
}
private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
let hash = NSMutableString()
for i in 0..<length {
hash.appendFormat("%02x", result[i])
}
return String(hash).lowercased()
}
}
You'll need to add #import <CommonCrypto/CommonHMAC.h> to your Objective-C bridging header.
Source: #thevalyreangroup on this github thread
You're doing it wrong with CryptoSwift.
For future readers, here's how to do it:
let result = try! HMAC(key: key, variant: .sha256).authenticate(message.bytes)
Swift 4.2 solution for HMAC encryption
Not so long ago I had the same problem, so I wrote simple framework for use in Swift on all platforms - iOS macOS and tvOS
It's called EasyCrypt and you can find it here:
https://github.com/lukszar/EasyCrypt
This framework let you encrypt message with your key, using HMAC algorithms.
Usage is simple, like following:
let crypto = EasyCrypt(secret: "mySecretKey", algorithm: .sha256)
let result = crypto.hash("This is very secret text to encrypt")
let otherResult = crypto.hash("This is another secret text to encrypt")
print("result: ", result)
print("otherResult: ", otherResult)
You can fast install using Carthage. Inside project you can find Playground for demo usage with instructions.
I've tried generating the hash_hmac('sha256', $key, $secret_key) php function equivalent in Swift 4 without success, after using libraries like CommonCrypto, CryptoSwift. I need these function for API authentication, using Alamofire library, which is a great library. Since i use Swift 4 the compatibility with other Swift libraries is not so good. Even with CryptoSwift which has the latest version(0.7.1) for Swift 4 i still get a lot of compatibility errors likes
enter image description here
Swift 3/4:
HMAC with MD5, SHA1, SHA224, SHA256, SHA384, SHA512 (Swift 3)
These functions will hash either String or Data input with one of eight cryptographic hash algorithms.
The name parameter specifies the hash function name as a String
Supported functions are MD5, SHA1, SHA224, SHA256, SHA384 and SHA512
This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.
These functions takes a hash name, message to be hashed, a key and return a digest:
hashName: name of a hash function as String
message: message as Data
key: key as Data
returns: digest as Data
func hmac(hashName:String, message:Data, key:Data) -> Data? {
let algos = ["SHA1": (kCCHmacAlgSHA1, CC_SHA1_DIGEST_LENGTH),
"MD5": (kCCHmacAlgMD5, CC_MD5_DIGEST_LENGTH),
"SHA224": (kCCHmacAlgSHA224, CC_SHA224_DIGEST_LENGTH),
"SHA256": (kCCHmacAlgSHA256, CC_SHA256_DIGEST_LENGTH),
"SHA384": (kCCHmacAlgSHA384, CC_SHA384_DIGEST_LENGTH),
"SHA512": (kCCHmacAlgSHA512, CC_SHA512_DIGEST_LENGTH)]
guard let (hashAlgorithm, length) = algos[hashName] else { return nil }
var macData = Data(count: Int(length))
macData.withUnsafeMutableBytes {macBytes in
message.withUnsafeBytes {messageBytes in
key.withUnsafeBytes {keyBytes in
CCHmac(CCHmacAlgorithm(hashAlgorithm),
keyBytes, key.count,
messageBytes, message.count,
macBytes)
}
}
}
return macData
}
hashName: name of a hash function as String
message: message as String
key: key as String
returns: digest as Data
func hmac(hashName:String, message:String, key:String) -> Data? {
let messageData = message.data(using:.utf8)!
let keyData = key.data(using:.utf8)!
return hmac(hashName:hashName, message:messageData, key:keyData)
}
hashName: name of a hash function as String
message: message as String
key: key as Data
returns: digest as Data
func hmac(hashName:String, message:String, key:Data) -> Data? {
let messageData = message.data(using:.utf8)!
return hmac(hashName:hashName, message:messageData, key:key)
}
// Examples
let clearString = "clearData0123456"
let keyString = "keyData8901234562"
let clearData = clearString.data(using:.utf8)!
let keyData = keyString.data(using:.utf8)!
print("clearString: \(clearString)")
print("keyString: \(keyString)")
print("clearData: \(clearData as NSData)")
print("keyData: \(keyData as NSData)")
let hmacData1 = hmac(hashName:"SHA1", message:clearData, key:keyData)
print("hmacData1: \(hmacData1! as NSData)")
let hmacData2 = hmac(hashName:"SHA1", message:clearString, key:keyString)
print("hmacData2: \(hmacData2! as NSData)")
let hmacData3 = hmac(hashName:"SHA1", message:clearString, key:keyData)
print("hmacData3: \(hmacData3! as NSData)")
Output:
clearString: clearData0123456
keyString: keyData8901234562
clearData: <636c6561 72446174 61303132 33343536>
keyData: <6b657944 61746138 39303132 33343536 32>
hmacData1: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
hmacData2: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
hmacData3: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
First of all it might be better to go straight for SHA512, SHA is notoriously easy to crack with GPU's, thus upping the memory scale a bit is not a bad idea.
Second, using CommonCrypto it is actually extremely easy to generate HMAC's, this is the implementation that I use:
static func hmac(_ secretKey: inout [UInt8], cipherText: inout [UInt8], algorithm: CommonCrypto.HMACAlgorithm = .sha512) -> [UInt8] {
var mac = [UInt8](repeating: 0, count: 64)
CCHmac(algorithm.value, &secretKey, secretKey.count, &cipherText, cipherText.count, &mac)
return mac
}
Where the algorithm is defined as such:
enum HMACAlgorithm {
case sha512
var value: UInt32 {
switch(self) {
case .sha512:
return UInt32(kCCHmacAlgSHA512)
}
}
}
My cipher text is cipherText+IV in this instance. When you are not using AES-GCM it seems suggested / recommended to HMAC IV+Cipher, but I cannot give you the technical details as to why.
Converting Data or NSData to a byte array:
var byteArray = data.withUnsafeBytes { [UInt8](UnsafeBufferPointer(start: $0, count: data.count) }
The reason for using an array is a substantial performance increase over Data, I don't know what the core team is doing but Data performs worse than NSMutableData even.
I am trying to get MD5 hash of my data (image downloaded from the interweb). Unfortunately I have upgraded the framework to swift 3 and the method I have been using doesn't work now.
I have converted most of it but I am unable to get bytes out of the data:
import Foundation
import CommonCrypto
struct MD5 {
static func get(data: Data) -> String {
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(data.bytes, CC_LONG(data.count), &digest)
var digestHex = ""
for index in 0..<Int(CC_MD5_DIGEST_LENGTH) {
digestHex += String(format: "%02x", digest[index])
}
return digestHex
}
}
the CommonCrypto is already imported as a custom module. Problem is I am getting 'bytes' is unavailable: use withUnsafeBytes instead on CC_MD5(data.bytes,...
So the question really is, how do I get the bytes out of the data and will this solution work?
CC_MD5(data.bytes, CC_LONG(data.count), &digest)
As noted, bytes is unavailable because it's dangerous. It's a raw pointer into memory than can vanish. The recommended solution is to use withUnsafeBytes which promises that the target cannot vanish during the scope of the pointer. From memory, it would look something like this:
data.withUnsafeBytes { bytes in
CC_MD5(bytes, CC_LONG(data.count), &digest)
}
The point is that the bytes pointer can't escape into scopes where data is no longer valid.
For an example of this with CCHmac, which is pretty similar to MD5, see RNCryptor.
Here's a one liner:
import CryptoKit
let md5String = Insecure.MD5.hash(data: data).map { String(format: "%02hhx", $0) }.joined()
And for anyone that's interested, here's an example that you could build upon to support different Algorithms:
Usage:
Checksum.hash(data: data, using: .md5) == "MyMD5Hash"
Code Snippet:
import Foundation
import CommonCrypto
struct Checksum {
private init() {}
static func hash(data: Data, using algorithm: HashAlgorithm) -> String {
/// Creates an array of unsigned 8 bit integers that contains zeros equal in amount to the digest length
var digest = [UInt8](repeating: 0, count: algorithm.digestLength())
/// Call corresponding digest calculation
data.withUnsafeBytes {
algorithm.digestCalculation(data: $0.baseAddress, len: UInt32(data.count), digestArray: &digest)
}
var hashString = ""
/// Unpack each byte in the digest array and add them to the hashString
for byte in digest {
hashString += String(format:"%02x", UInt8(byte))
}
return hashString
}
/**
* Hash using CommonCrypto
* API exposed from CommonCrypto-60118.50.1:
* https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60118.50.1/include/CommonDigest.h.auto.html
**/
enum HashAlgorithm {
case md5
case sha256
func digestLength() -> Int {
switch self {
case .md5:
return Int(CC_MD5_DIGEST_LENGTH)
case .sha256:
return Int(CC_SHA256_DIGEST_LENGTH)
}
}
/// CC_[HashAlgorithm] performs a digest calculation and places the result in the caller-supplied buffer for digest
/// Calls the given closure with a pointer to the underlying unsafe bytes of the data's contiguous storage.
func digestCalculation(data: UnsafeRawPointer!, len: UInt32, digestArray: UnsafeMutablePointer<UInt8>!) {
switch self {
case .md5:
CC_MD5(data, len, digestArray)
case .sha256:
CC_SHA256(data, len, digestArray)
}
}
}
}
I am using CryptoSwift to encrypt data. I am learning how to use it however I cannot get past the first basic tutorial. I am unable to convert the encrypted data back to a String - which kind of defeats the purpose of encrypting it in the first place if I cannot legibly decrypt the data
Code:
let string = "Hi. This is Atlas"
let input: [UInt8] = Array(string.utf8)
print(input)
let key: [UInt8] = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
let iv: [UInt8] = AES.randomIV(AES.blockSize)
do {
let encryptedBytes: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).encrypt(input, padding: PKCS7())
print(encryptedBytes)
let decrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).decrypt(encryptedBytes, padding: PKCS7())
print(decrypted) // << need to convert this array of byted to a string (should be equal to original input)
} catch {
} catch {
}
Thank you for the help
You'll want Foundation to decode the UTF8 for you since there's no way to generate a String.UTF8View directly. So convert to NSData first.
let decrypted: [UInt8] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]
let data = NSData(bytes: decrypted, length: decrypted.count)
let str = String(data: data, encoding: NSUTF8StringEncoding)
If you want to do it without Foundation, you can, but it's a little work. You have to manage the decoding yourself.
extension String {
init?(utf8Bytes: [UInt8]) {
var decoder = UTF8()
var g = utf8Bytes.generate()
var characters: [Character] = []
LOOP:
while true {
let result = decoder.decode(&g)
switch result {
case .Result(let scalar): characters.append(Character(scalar))
case .EmptyInput: break LOOP
case .Error: return nil
}
}
self.init(characters)
}
}
let unicode = String(utf8Bytes: bytes)
(I'm very surprised that this isn't built into Swift stdlib since it's so common and can be quickly built out of other parts of Swift stdlib. Often when that's the case, there's a reason that I'm just not aware of yet, so there may be some subtle problem with my approach here.)
let stringDecrypted = String(decrypted.map { Character(UnicodeScalar($0)) })
So it maps each UInt8 to UnicodeScalar and then to Character. After that it uses String's initializer to create String from array of Characters.