// swift-tools-version:6.2import PackageDescription let package = Package( name: "howto", platforms: [.macOS(.v15)], dependencies: [ // CryptoKit's API on Linux; on Apple platforms "import CryptoKit" needs no package .package(url: "https://github.com/apple/swift-crypto.git", exact: "5.0.3"), ], targets: [ .executableTarget(name: "verify", dependencies: [.product(name: "Crypto", package: "swift-crypto")]), ]) import Cryptoimport Foundation let publicKey = try P256.Signing.PublicKey(pemRepresentation: String(contentsOfFile: "pub.pem", encoding: .utf8))let message = try Data(contentsOf: URL(fileURLWithPath: "message.txt")) // The signature type states the encoding: DER from OpenSSL and Java, raw r‖s from JWS and WebCryptolet der = try P256.Signing.ECDSASignature(derRepresentation: Data(contentsOf: URL(fileURLWithPath: "sig.der")))let raw = try P256.Signing.ECDSASignature(rawRepresentation: Data(contentsOf: URL(fileURLWithPath: "sig.raw"))) // isValidSignature(_:for:) hashes the data with SHA-256 itselfprint(publicKey.isValidSignature(der, for: message) ? "valid" : "INVALID")print(publicKey.isValidSignature(raw, for: message) ? "valid" : "INVALID")print(publicKey.isValidSignature(der, for: Data("hello ECDSA".utf8)) ? "valid" : "INVALID") How it works
P256.Signing.PublicKey(pemRepresentation:)reads aBEGIN PUBLIC KEYblock;derRepresentation:,x963Representation:(the04 ‖ x ‖ ypoint) andrawRepresentation:cover the other common forms.ECDSASignature(derRepresentation:)andECDSASignature(rawRepresentation:)make the encoding explicit — the most common ECDSA interop bug cannot slip through unnoticed.isValidSignature(_:for:)hashes the data with SHA-256 (the hash CryptoKit binds to P-256) and returns aBool; aSHA256Digestcan be passed instead of the data when you hashed it yourself.import Cryptois swift-crypto; on Apple platforms you canimport CryptoKitinstead, and the code is the same.
Gotchas
- Constructors throw while verification does not: a signature of the wrong length throws
incorrectParameterSize, DER bytes passed as raw throw too, but a well-formed signature that does not match just returnsfalse. - Key types are per curve: a P-384 PEM loaded as
P256.Signing.PublicKeythrowsincorrectParameterSize, and a certificate PEM throwsinvalidPEMDocument— extract the key with swift-certificates first. - CryptoKit accepts high-S signatures (both s and n − s verify). Bitcoin-style rules that require low-S must be checked separately.
- The algorithm is fixed by the type —
P256always uses SHA-256 here. Verifying a P-256 signature made over SHA-384 needs the digest variant with aSHA384Digest.