I am using the ecdsa python library to generate keys and signatures for messages, but I am unable to verify those signatures in Swift.
I'm trying to simply verify a signature of the text "S". I've been able to successfully create a public SecKey, but no matter what I do, I cannot seem to get SecKeyVerifySignature to pass. The payload is hashed using SHA-1, and I've been able to verify the digest in Swift matches the digest in python. The failing code below should run in xCode.
// ecdsaVerify
import Foundation
import CommonCrypto
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return map { String(format: format, $0) }.joined()
}
}
extension String{
func sha1() -> String {
let data = Data(self.utf8)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
var hexadecimalStringToData: Data? {
// Convert a hexstring to data
var data = Data(capacity: self.count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in
let byteString = (self as NSString).substring(with: match!.range)
let num = UInt8(byteString, radix: 16)!
data.append(num)
}
guard data.count > 0 else { return nil }
return data
}
}
//prime256v1, a.k.a kSecECCurveSecp256r1
// .der with first 26 bits removed
let EC_PUBLIC_KEY = """
-----BEGIN BARE KEY-----
BOptLvuuwPYOaQC6v7hDh36fTqsuwFTCyHVg3uYvfdbPtV5c3urT8HeS/UaGgaO8tMFfpz1sljTnwWTPb8gQcD8=
-----END BARE KEY-----
"""
// Here for example purposes - to help others generate signatures.
let PRIVATE_KEY = """
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEINxdvHX9q9PGrJBWHcmvXGe3poOc0Qc1/C4HnScp6cP7oAoGCCqGSM49
AwEHoUQDQgAE6m0u+67A9g5pALq/uEOHfp9Oqy7AVMLIdWDe5i991s+1Xlze6tPw
d5L9RoaBo7y0wV+nPWyWNOfBZM9vyBBwPw==
-----END EC PRIVATE KEY-----
"""
class ecdsaVerifier{
var publicKeyData:Data
var publicKey: SecKey?
init(_ pemPublicKey: String){
let pemPublicKeyNoComments = pemPublicKey.replacingOccurrences(
of: #"(?m)^----.*"#,
with: "",
options: .regularExpression
).trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "\n", with: "")
self.publicKeyData = Data(base64Encoded:pemPublicKeyNoComments)!
var error: Unmanaged<CFError>? = nil
self.publicKey = SecKeyCreateWithData(self.publicKeyData as NSData, [
kSecAttrKeyType: kSecAttrKeyTypeECDSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits: 256
] as NSDictionary, &error)
print(self.publicKey)
}
func verifyFromHexString(content: String, hexSignature: String) -> Bool{
let cleanSignature = hexSignature.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "")
print("verify from", cleanSignature, content)
let cleanSignatureData = cleanSignature.hexadecimalStringToData!
let digest = content.sha1().hexadecimalStringToData!
//let digest = content.data(using: .utf8)!
print("Digest", digest.count, digest.hexEncodedString())
return self.verifyFromDigest(digest, signature: cleanSignatureData)
}
func verifyFromDigest(_ digest: Data, signature: Data) -> Bool{
var error: Unmanaged<CFError>? = nil
guard SecKeyVerifySignature(publicKey!, SecKeyAlgorithm.ecdsaSignatureDigestX962SHA1, digest as CFData, signature as CFData, &error) else {
print("error", error)
return false
}
print("true!")
return true
}
}
let verifier = ecdsaVerifier(EC_PUBLIC_KEY)
let signature = """
30 44 02 20 14 08 69 ac 3a 39 29 78 c2 fd e1 0a
c2 6d 87 4e 66 57 fd bf 1a 01 b8 4c b2 13 63 fc
f6 e8 0b 64 02 20 42 88 2e a0 c8 4a ca 04 a1 0a
59 be e3 c7 50 d6 53 d4 14 d3 a6 f7 e6 07 0d e2
e5 6b 8f 44 47 52
"""
let content = "S"
verifier.verifyFromHexString(content: content, hexSignature: signature)
Any help would be appriciated.