How So I Convert This Sha256 + Base64 from Swift to PHP

How so I convert this SHA256 + BASE64 from Swift to PHP?

You should set raw output to true for hash method in PHP. Notice the third method argument in hash

$hashedStr = hash('sha256', $inputStr, true);
$finalStr = base64_encode($hashedStr);
echo $finalStr;

This way the base64_encoded raw value from PHP should be equal to the one that you get from base64EncodedString

SHA256 in swift

You have to convert explicitly between Int and CC_LONG, because Swift does not
do implicit conversions, as in (Objective-)C.

You also have to define hash as an array of the required size.

func sha256(data : NSData) -> NSData {
var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))
return res
}

Alternatively, you can use NSMutableData to allocate the needed buffer:

func sha256(data : NSData) -> NSData {
let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256(data.bytes, CC_LONG(data.length), UnsafeMutablePointer(res.mutableBytes))
return res
}

Update for Swift 3 and 4:

func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0, CC_LONG(data.count), &hash)
}
return Data(bytes: hash)
}

Update for Swift 5:

func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
return Data(hash)
}

Hash string with SHA512 in Swift

Swift 3 implementatiomn at bottom.

The code does not need to be so complex or an extension:

func sha512Hex(string string: String) -> String {
var digest = [UInt8](count: Int(CC_SHA512_DIGEST_LENGTH), repeatedValue: 0)
if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
CC_SHA512(data.bytes, CC_LONG(data.length), &digest)
}

var digestHex = ""
for index in 0..<Int(CC_SHA512_DIGEST_LENGTH) {
digestHex += String(format: "%02x", digest[index])
}

return digestHex
}

//Test:

let hexDigest = sha512Hex(string:"8yOrBmkd")
print("hexDigest:\n\(hexDigest)")

Output:

hexDigest:
ae596d2d67966900ab7cd4d831ad02708b3499f2e81c60329eb77e77c1fae7eac60331d2fc1496b2e9b04a673117d6801bd4c8cd7c7e1ce8c0acfc8b2f785eae

Just returing the byte array:

func sha512(string string: String) -> [UInt8] {
var digest = [UInt8](count: Int(CC_SHA512_DIGEST_LENGTH), repeatedValue: 0)
let data = string.dataUsingEncoding(NSUTF8StringEncoding)!
CC_SHA512(data.bytes, CC_LONG(data.length), &digest)

return digest
}

//Test:

let digestRaw = sha512(string:"8yOrBmkd")
print("decimal array:\n\(digestRaw)")
print("hexadecimal:\n\(NSData(bytes:digestRaw, length:digestRaw.count).description)")

Output:

decimal array:
[174, 89, 109, 45, 103, 150, 105, 0, 171, 124, 212, 216, 49, 173, 2, 112, 139, 52, 153, 242, 232, 28, 96, 50, 158, 183, 126, 119, 193, 250, 231, 234, 198, 3, 49, 210, 252, 20, 150, 178, 233, 176, 74, 103, 49, 23, 214, 128, 27, 212, 200, 205, 124, 126, 28, 232, 192, 172, 252, 139, 47, 120, 94, 174]

hexadecimal:

ae596d2d67966900ab7cd4d831ad02708b3499f2e81c60329eb77e77c1fae7eac60331d2fc1496b2e9b04a673117d6801bd4c8cd7c7e1ce8c0acfc8b2f785eae

Base64 output:

func sha512Base64(string string: String) -> String {
let digest = NSMutableData(length: Int(CC_SHA512_DIGEST_LENGTH))!
if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
CC_SHA512(data.bytes, CC_LONG(data.length), UnsafeMutablePointer<UInt8>(digest.mutableBytes))
}
return digest.base64EncodedStringWithOptions(NSDataBase64EncodingOptions([]))
}

//Test:

let digestBase64 = sha512Base64(string:"8yOrBmkd")
print("Base64:\n\(digestBase64)")

Output:

Base64:
rlltLWeWaQCrfNTYMa0CcIs0mfLoHGAynrd+d8H65+rGAzHS/BSWsumwSmcxF9aAG9TIzXx+HOjArPyLL3herg==

Swift 3

func hashSHA512(data:Data) -> String? {
var hashData = Data(count: Int(CC_SHA512_DIGEST_LENGTH))

_ = hashData.withUnsafeMutableBytes {digestBytes in
data.withUnsafeBytes {messageBytes in
CC_SHA512(messageBytes, CC_LONG(data.count), digestBytes)
}
}

// For hexadecimal output:
return hashData.map { String(format: "%02hhx", $0) }.joined()
// For Base64 output use this instead of the above:
// return data.base64EncodedString()
}

Example from deprecated documentation section:

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>

MD2, MD4, 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 MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384 and SHA512
a
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.



This function takes a hash name and String to be hashed and returns a Data:


name: A name of a hash function as a String
string: The String to be hashed
returns: the hashed result as Data
func hash(name:String, string:String) -> Data? {
let data = string.data(using:.utf8)!
return hash(name:name, data:data)
}

Examples:

let clearString = "clearData0123456"
let clearData = clearString.data(using:.utf8)!
print("clearString: \(clearString)")
print("clearData: \(clearData as NSData)")

let hashSHA256 = hash(name:"SHA256", string:clearString)
print("hashSHA256: \(hashSHA256! as NSData)")

let hashMD5 = hash(name:"MD5", data:clearData)
print("hashMD5: \(hashMD5! as NSData)")

Output:

clearString: clearData0123456
clearData: <636c6561 72446174 61303132 33343536>

hashSHA256: <aabc766b 6b357564 e41f4f91 2d494bcc bfa16924 b574abbd ba9e3e9d a0c8920a>
hashMD5: <4df665f7 b94aea69 695b0e7b baf9e9d6>

How to decrypt a SHA-256 encrypted string?

SHA-256 is a cryptographic (one-way) hash function, so there is no direct way to decode it. The entire purpose of a cryptographic hash function is that you can't undo it.

One thing you can do is a brute-force strategy, where you guess what was hashed, then hash it with the same function and see if it matches. Unless the hashed data is very easy to guess, it could take a long time though.

You may find the question "Difference between hashing a password and encrypting it" interesting.

Why is base64_encode() adding a slash / in the result?

No. The Base64 alphabet includes A-Z, a-z, 0-9 and + and /.

You can replace them if you don't care about portability towards other applications.

See: http://en.wikipedia.org/wiki/Base64#Variants_summary_table

You can use something like these to use your own symbols instead (replace - and _ by anything you want, as long as it is not in the base64 base alphabet, of course!).

The following example converts the normal base64 to base64url as specified in RFC 4648:

function base64url_encode($s) {
return str_replace(array('+', '/'), array('-', '_'), base64_encode($s));
}

function base64url_decode($s) {
return base64_decode(str_replace(array('-', '_'), array('+', '/'), $s));
}

How can I encode a string to Base64 in Swift?

I don’t have 6.2 installed but I don’t think 6.3 is any different in this regard:

dataUsingEncoding returns an optional, so you need to unwrap that.

NSDataBase64EncodingOptions.fromRaw has been replaced with NSDataBase64EncodingOptions(rawValue:). Slightly surprisingly, this is not a failable initializer so you don’t need to unwrap it.

But since NSData(base64EncodedString:) is a failable initializer, you need to unwrap that.

Btw, all these changes were suggested by Xcode migrator (click the error message in the gutter and it has a “fix-it” suggestion).

Final code, rewritten to avoid force-unwraps, looks like this:

import Foundation

let str = "iOS Developer Tips encoded in Base64"
println("Original: \(str)")

let utf8str = str.dataUsingEncoding(NSUTF8StringEncoding)

if let base64Encoded = utf8str?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
{

println("Encoded: \(base64Encoded)")

if let base64Decoded = NSData(base64EncodedString: base64Encoded, options: NSDataBase64DecodingOptions(rawValue: 0))
.map({ NSString(data: $0, encoding: NSUTF8StringEncoding) })
{
// Convert back to a string
println("Decoded: \(base64Decoded)")
}
}

(if using Swift 1.2 you could use multiple if-lets instead of the map)

Swift 5 Update:

import Foundation

let str = "iOS Developer Tips encoded in Base64"
print("Original: \(str)")

let utf8str = str.data(using: .utf8)

if let base64Encoded = utf8str?.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)) {
print("Encoded: \(base64Encoded)")

if let base64Decoded = Data(base64Encoded: base64Encoded, options: Data.Base64DecodingOptions(rawValue: 0))
.map({ String(data: $0, encoding: .utf8) }) {
// Convert back to a string
print("Decoded: \(base64Decoded ?? "")")
}
}

HMAC SHA256 in C# vs HMAC SHA256 in Swift don't match

The issue here seems to be a difference in the base64 strings between the given swift solution and then C# solution.

The output of the hash algorithm was the same at a byte level but it was being manipulated through various string conversions before being returned.

The function

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()
}

Which is part of the solution converts the raw hashed data to a string, which is then converted back to a data object in the calling class, which is then cast into a base64 string to be returned. As we are getting a base64 string of of the string that was returned and not the raw bytes, I think this is where the issue was.

By changing the hmac code from the solution to this, we can cast the raw output of the has to a base64 string and avoid the other steps. This matches the C# value.

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 a = UnsafeMutableBufferPointer(start: result, count: digestLen)
let b = Data(a)
result.deallocate()
let digest = b.base64EncodedString()
return digest
}

Remove trailing = when base64 encoding

The = is padding. <!------------>

Wikipedia says

An additional pad character is
allocated which may be used to force
the encoded output into an integer
multiple of 4 characters (or
equivalently when the unencoded binary
text is not a multiple of 3 bytes) ;
these padding characters must then be
discarded when decoding but still
allow the calculation of the effective
length of the unencoded text, when its
input binary length would not be a
multiple of 3 bytes (the last non-pad
character is normally encoded so that
the last 6-bit block it represents
will be zero-padded on its least
significant bits, at most two pad
characters may occur at the end of the
encoded stream).

If you control the other end, you could remove it when in transport, then re-insert it (by checking the string length) before decoding.

Note that the data will not be valid Base64 in transport.

Also, Another user pointed out (relevant to PHP users):

Note that in PHP base64_decode will accept strings without padding, hence if you remove it to process it later in PHP it's not necessary to add it back. – Mahn Oct 16 '14 at 16:33

So if your destination is PHP, you can safely strip the padding and decode without fancy calculations.



Related Topics



Leave a reply



Submit