[IOS] Swift Cryptokit - P256 ECDSA Signature derRepresentation incompatibility

Hello,

I'm working with Cryptokit and I'm currently unable to export a P256 ECDSA signature data to base64Encoding and convert it back to derRepresentation.

Here is my code:

let privateKey = P256.Signing.PrivateKey()
//Generated successfully
/* For example pemRepresentation:
-----BEGIN PRIVATE KEY-----

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgK+lUhkqFo637XuvJ

Q6YiqZx04c33UUyWnLnpwfTwrtChRANCAARy55pVFhjgq3jKnQTx7s1XDIaNm20m

29ZEiBJnvzYfQGUQ+4ldzJYm34z8W2X/waqTqXsY4/oSO1sDFSQ6XwrG

-----END PRIVATE KEY-----
*/
let publicKey = privateKey.publicKey
//Picked up successfully
/* For example pemRepresentation:
-----BEGIN PUBLIC KEY-----

MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcueaVRYY4Kt4yp0E8e7NVwyGjZtt

JtvWRIgSZ782H0BlEPuJXcyWJt+M/Ftl/8Gqk6l7GOP6EjtbAxUkOl8Kxg==

-----END PUBLIC KEY-----
*/

//Basic message to test, "something" here
 let messageDigest = "something".data(using: .utf8)!

                

let signature = try privateKey.signature(for: messageDigest)
//Successfully generated
                
//Now from the PublicKey, let's check the signature for the message
let isValidSignature = publicKey.isValidSignature(signature, for: messageDigest)
//Successfully Return true


//So far everything works fine

//Now lets try to export the derRepresentation of the signature we just created to a base64EncodedString and try to use it later

let signDataDerRepresentation = signature.derRepresentation
//Here the signDataDerRepresentation is 70 bytes length

//Here we get the signature derRepresentation to base64EncodedString, perfect to export
let signDataDerRepresentationBase64String = signature.derRepresentation.base64EncodedString()
/*
"MEQCIGVC/zOGKEauy9AetVViTZiMTtFIeNtW9xALMTu6aIjSAiB+QPz9nGwzy51k3p3osu9OY6oQXkuLHTPoSWxPorg8GA=="
*/
//Here the signDataDerRepresentationBase64String is 96 bytes length

//Now when I try to create a signature from a derRepresentation it works fine when I use the original derRepresentation data:
let signature1 = try! P256.Signing.ECDSASignature(derRepresentation: signDataDerRepresentation)
//signature1 is created

//But when I try to create exactly the same signature from the base64EncodedString, an exception is throw

//Convert base64String to Data
let signDataDerRepresentationBase64StringData = signDataDerRepresentationBase64String.data(using: .utf8)!

do {
//Try to create a signature from the base64EncodedData of de the derRepresentation
let signature2 = try P256.Signing.ECDSASignature(derRepresentation: signDataDerRepresentationBase64StringData)
//Here it fails, signature2 is not created
}catch{
//It fails with error: invalidASN1Object
print("Signature failed \(error)")
/*
Signature failed invalidASN1Object
*/
}


So now, I would like to know how to convert de derRepresentation of the signature I created into a base64EncodedString to the derRepresentation the ECDSASignature(derRepresentation:) func is looking for to create a signature from derRepresentation?

I don't understand how I can convert my base64 String into that derRepresentation this func is looking for, I can't find the doc :

/// An ECDSA (Elliptic Curve Digital Signature Algorithm) Signature

@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *)

extension P256.Signing {



    public struct ECDSASignature : ContiguousBytes {



        /// Returns the raw signature.

        /// The raw signature format for ECDSA is r || s

        public var rawRepresentation: Data



        /// Initializes ECDSASignature from the raw representation.

        /// The raw signature format for ECDSA is r || s

        /// As defined in https://tools.ietf.org/html/rfc4754

        public init<D>(rawRepresentation: D) throws where D : DataProtocol



        /// Initializes ECDSASignature from the DER representation.

        public init<D>(derRepresentation: D) throws where D : DataProtocol



        /// Calls the given closure with the contents of underlying storage.

        ///

        /// - note: Calling `withUnsafeBytes` multiple times does not guarantee that

        ///         the same buffer pointer will be passed in every time.

        /// - warning: The buffer argument to the body should not be stored or used

        ///            outside of the lifetime of the call to the closure.

        public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R



        /// A DER-encoded representation of the signature

        public var derRepresentation: Data { get }

    }

}

Thank you for your help.

Hélie

Replies

@eskimo  Please help :'(

The problem is this:

let signDataDerRepresentationBase64StringData = signDataDerRepresentationBase64String.data(using: .utf8)!

That does create a Data value from a String but it does so by rendering the string to UTF-8. That doesn’t undo the Base64 encoding. The droid you’re looking for is the Data.init(base64Encoded:options:) initialiser.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • Daaamn Bro thanks a lot ! :D You saved me a lot of time :)

    I Will complete the code above.

    Hélie

Add a Comment

So, as @eskimo  says, the problem was about the base64 string decoding.

Just replace this line of code :

let signDataDerRepresentationBase64StringData = signDataDerRepresentationBase64String.data(using: .utf8)!

by this one:

let signDataDerRepresentationBase64StringData = Data.init(base64Encoded:signB64String, options:.ignoreUnknownCharacters)

Thanks again @eskimo 

If use P256, It will only support IOS above 14. And If I want to support IOS below 14. How can do it ?

If use P256, It will only support iOS above 14.

I recommend that you start a new thread for this. Please tag it with Security so that I see it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"