Hi, I'm trying to achieve the following OpenSSL workflow in Swift.
I have this intermediate certificate from Let's encrypt and I want to extract the public key from it and then hash it with SHA-256 and finally encide it in base64.
The OpenSSL commands that achieve this look like this:
openssl x509 -in isrgrootx1.pem -pubkey -noout > publickey.pem
openssl rsa -pubin -in publickey.pem -outform der | openssl dgst -sha256 -binary | openssl enc -base64
I've tried Security, CommonCrypto, CryptoKit frameworks with no success. I was able to get the public key out of the certificate but its PEM representation seems to slightly differ from what I get with OpenSSL. At the beginning of the public jet, the OpenSSL version has a string that is not present on what I get with Swift but the rest is the same.
This is the Swift code to use:
import Foundation
import Security
import CommonCrypto
// Step 1: Extract public key from the certificate
func extractPublicKey(from certificate: SecCertificate) -> SecKey? {
// Extract public key from the certificate
var publicKey: SecKey?
if let publicKeyRef = SecCertificateCopyKey(certificate) {
publicKey = publicKeyRef
}
return publicKey
}
// Step 2: Calculate SHA-256 hash of the public key
func calculateSHA256(of 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)
}
// Step 3: Encode data as base64
func base64EncodedString(from data: Data) -> String {
return data.base64EncodedString()
}
// Step 4: Main function to perform all steps
func processCertificate(certificate: SecCertificate) {
// Step 1: Extract public key
guard let publicKey = extractPublicKey(from: certificate) else {
return
}
// Step 2: Export public key as data
guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? else {
print("Failed to export public key data")
return
}
// Step 3: Calculate SHA-256 hash of the public key
let sha256Hash = calculateSHA256(of: publicKeyData)
// Step 4: Encode SHA-256 hash as base64
let base64EncodedHash = base64EncodedString(from: sha256Hash)
print("SHA-256 hash of public key (base64 encoded): \(base64EncodedHash)")
}
This is the Public Key I get with OpenSSL:
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAregkc/QUN/ObnitXKByHvty33ziQjG485legePd1wqL+9Wpu9gBPKNveaIZsRJO2sWP9FBJrvx/S6jGbIX7RMzy6SPXded+zuP8S8SGaS8GKhnFpSmZmbI9+PHC/rSkiBvPkwOaAruJLj7eZfpQDn9NHl3yZSCNT6DiuTwpvgy7RSVeMgHS22i/QOI17A3AhG3XyMDz6j67d2mOr6xZPwo4RS37PC+j/tXcu9LJ7SuBMEiUMcI0DKaDhUyTsE9nuGb8Qs0qMP4mjYVHerIcHlPRjcewu4m9bmIHhiVw0eWx27zuQYnnm26SaLybF0BDhDt7ZEI4W+7f3qPfH5QIHmI82CJXn4jeWDTZ1nvsOcrEdm7wD+UkF2IHdBbQq1kHprAF2lQoP2N/VvRIfNS8oF2zSmMGoCWR3bkc3us6sWV5onX9y1onFBkEpPlk+3Sb1JMkRp1qjTEAfRqGZtac6UW6GO559cqcSBXhZ7T5ReBULA4+N0C8Fsj57ShxLcwUS/Mbq4FATfEOTdLPKdOeOHwEI0DDUW3E2tAe6wTAwXEi3gjuYpn1giqKjKYLMur2DBBuigwNBodYF8RvCtvCofIY7RqhIKojcdpp2vx9qpT0Zj+s482TeyCsNCij/99viFULUItAnXeF5/hjncIitTubZizrG3SdRbv+8ZPUzQ08CAwEAAQ==
-----END PUBLIC KEY-----
and this is what I get with Swift:
-----BEGIN PUBLIC KEY-----
MIICCgKCAgEAregkc/QUN/ObnitXKByHvty33ziQjG485legePd1wqL+9Wpu9gBPKNveaIZsRJO2sWP9FBJrvx/S6jGbIX7RMzy6SPXded+zuP8S8SGaS8GKhnFpSmZmbI9+PHC/rSkiBvPkwOaAruJLj7eZfpQDn9NHl3yZSCNT6DiuTwpvgy7RSVeMgHS22i/QOI17A3AhG3XyMDz6j67d2mOr6xZPwo4RS37PC+j/tXcu9LJ7SuBMEiUMcI0DKaDhUyTsE9nuGb8Qs0qMP4mjYVHerIcHlPRjcewu4m9bmIHhiVw0eWx27zuQYnnm26SaLybF0BDhDt7ZEI4W+7f3qPfH5QIHmI82CJXn4jeWDTZ1nvsOcrEdm7wD+UkF2IHdBbQq1kHprAF2lQoP2N/VvRIfNS8oF2zSmMGoCWR3bkc3us6sWV5onX9y1onFBkEpPlk+3Sb1JMkRp1qjTEAfRqGZtac6UW6GO559cqcSBXhZ7T5ReBULA4+N0C8Fsj57ShxLcwUS/Mbq4FATfEOTdLPKdOeOHwEI0DDUW3E2tAe6wTAwXEi3gjuYpn1giqKjKYLMur2DBBuigwNBodYF8RvCtvCofIY7RqhIKojcdpp2vx9qpT0Zj+s482TeyCsNCij/99viFULUItAnXeF5/hjncIitTubZizrG3SdRbv+8ZPUzQ08CAwEAAQ==
-----END PUBLIC KEY-----
Interestingly, if I use the Swift version of the Public Key I get and then run the second command I still get the correct final result. Unfortunately in Swift I don't get the correct final result.
I suspect it must be something about headers since I was able to get the correct output on OpenSSL with the public key I got using the Swift.
Any ideas?