For you first point i.e. How to generate SHA256 hex string from PEM file
Method 1:
Get public key via terminal command-
Step 1: If you have the pem file with you please use the below openSSL command to get the public key.
openssl rsa -in inputPemFile.pem -pubout -out outputPublicKey.pem
Here, please do make sure your PEM file is in correct format which contains the private key.
Step 2: Now, use below command to extract/read the public key from outputPublicKey.pem file
cat public_key.pem
Method 2:
Direct method
Step 1: Open Qualys SSL Labs
Step 2: Enter your domain hostname from which you want to extract the public key e.g. https://www.google.com/ and press submit button
Step 3: In the next screen you will get your SHA256 public key, see reference image below
==========================================================================
For you second point i.e. Implement root certificate public key pinning?
Now, if you are using url session then use URL session delegate method i.e. // User defined variables
private let rsa2048Asn1Header:[UInt8] = [
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00
]
private let yourPublicKey: "Your Public Key"
// MARK: URL session delegate:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// your code logic
}
Find below the logic which I basically used:
//MARK:- SSL Pinning with URL Session
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
var res = SecTrustResultType.invalid
guard
challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust,
SecTrustEvaluate(serverTrust, &res) == errSecSuccess,
let serverCert = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
if #available(iOS 12.0, *) {
if let serverPublicKey = SecCertificateCopyKey(serverCert), let serverPublicKeyData = SecKeyCopyExternalRepresentation(serverPublicKey, nil) {
let data: Data = serverPublicKeyData as Data
let serverHashKey = sha256(data: data)
print(serverHashKey, serverHashKey.toSHA256())
//comparing server and local hash keys
if serverHashKey.toSHA256() == yourPublicKey {
print("Public Key pinning is successfull")
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
print("Public Key pinning is failed")
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
} else {
// Fallback on earlier versions
if let serverPublicKey = SecCertificateCopyPublicKey(serverCert), let serverPublicKeyData = SecKeyCopyExternalRepresentation(serverPublicKey, nil) {
let data: Data = serverPublicKeyData as Data
let serverHashKey = sha256(data: data)
print(serverHashKey, serverHashKey.toSHA256())
//comparing server and local hash keys
if serverHashKey.toSHA256() == yourPublicKey {
print("Public Key pinning is successfull")
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
print("Public Key pinning is failed.")
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
}
Helper function to convert server certificate to SHA256
private func sha256(data : Data) -> String {
var keyWithHeader = Data(rsa2048Asn1Header)
keyWithHeader.append(data)
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
keyWithHeader.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(keyWithHeader.count), &hash)
}
return Data(hash).base64EncodedString()
}
If you are using Alamofire, then pass the domain path in the evaluators data in your alamofire session like below
let evaluators: [String: ServerTrustEvaluating] = [
"your.domain.com": PublicKeysTrustEvaluator(
performDefaultValidation: false,
validateHost: false
)
]
let serverTrustManager = ServerTrustManager(evaluators: evaluators)
let session = Session(serverTrustManager: serverTrustManager)
Now use this session while calling your alamofire network request.
Hope, I will be able to help you here.
Thanks and regards.