skip to Main Content

I struggled and messed around for about two days to create the digital signature for the specific API calls mentioned at:
https://developer.ebay.com/develop/guides/digital-signatures-for-apis

As the documentation – especially for C# – is pretty poor, particularly for this critical change in calling the Restful APIs, I want to share my finally working solution.

Overall, my question was to create all the headers and the signed signature base.


Already using the ‘OAuth scope’ with access token created with the client credentials grant flow, creating the signing keys by the ‘Key Management API’ was not a big deal.

My first problem was Error 215114 saying ‘The create time of signature parameters is not in right range’, as I thought I need to use the creation time of the created signing keys. But you need to use the time of the call.

I first created the signing keys with RSA-SHA256 cipher, but as I struggled to sign the signature base and eBay recommends Ed25519 cipher, I gave Ed25519 a try and created new keys with it.

Nevertheless I struggled with signing the signature base for the ‘Signature’-header, received 215002 error (‘Internal errors as fetching master key’) in the meantime, and after all ended with error 215120 resp. 215122 ‘Signature validation failed’.

I’m using .NET 6 and RestSharp for the actual API call, tried the ‘RSA Class’ from System.Security.Cryptography, but now use BouncyCastle for signing in my working solution.

2

Answers


  1. Chosen as BEST ANSWER

    So, this is my working solution with successful response, using RestSharp and BouncyCastle, with given eBay access token and Ed25519-signing keys.

    The keys are not in PEM format, like so:
    JWE:
    eyJ6aXAiOiJERUYiLCJraWQiOiJiNmI4ZW[...]
    Private Key:
    MC4CAQAwBQYDK2VwBCIEIDIpPLbihtModG[...]
    Public Key:
    MCowBQYDK2VwAyEA0CjbDt2NDS7LKbQS6i[...]

    I think I solved my problem with successfully signing the base by noting it with "-----BEGIN ... KEY-----" and "-----END ... KEY-----" and create the key parameters with PemReader.

    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.OpenSsl;
    using Org.BouncyCastle.Security;
    using RestSharp;
    
    // EXTERNAL METHOD TO RETRIEVE A VALID ACCESS TOKEN
    EbayAccessToken ebayAccessToken = EbayAccessToken.Instance;
    if (await ebayAccessToken.GetValidToken(CommonInterfaces.Resources.CommonDataPath) == null)
    {
        ebayAccessToken = await WebViewCommonCalls.RenewEbayApiToken(ebayAccessToken);
    }
    
    // EXTERNAL METHOD TO RETRIEVE THE SIGNING KEYS
    SigningKeyResponse keyResponse = await KeyManagement.Instance(CommonInterfaces.Resources.CommonDataPath).GetSigningKeys(ebayAccessToken.AccessToken.Token);
    
    long timeStamp = DateTimeOffset.Now.ToUnixTimeSeconds();
    string url = "https://apiz.ebay.com";
    string endpoint = "/sell/finances/v1/seller_funds_summary"; // EXAMPLE CALL FOR TESTING
    
    // BEGAN TO IMPLEMENT THE Content-Digest FOR THE PAYLOAD IN POST CALLS
    //string sha256Hash = ComputeSha256Hash(JsonConvert.SerializeObject(payload));
    
    string signatureParams = "("x-ebay-signature-key" "@method" "@path" "@authority");created=" + timeStamp;
    
    string signatureBase = 
        $""x-ebay-signature-key": {keyResponse.Jwe}n" +
        $""@method": GETn" +
        $""@path": {endpoint}n" +
        $""@authority": apiz.ebay.comn" +
        $""@signature-params": {signatureParams}";
    byte[] signatureBaseBytes = Encoding.UTF8.GetBytes(signatureBase);
    string signatureBase64 = string.Empty;
    
    // FINALLY THIS WAS THE ONLY METHOD WHICH WORKED FOR ME, BY NOTING THE KEYS IN PEM FORMAT AND LOAD IT WITH PEM READER
    string privKeyString = $"-----BEGIN PRIVATE KEY-----n{keyResponse.PrivateKey}n-----END PRIVATE KEY-----";
    string pubKeyString = $"-----BEGIN PUBLIC KEY-----n{keyResponse.PublicKey}n-----END PUBLIC KEY-----";
    
    // Sign
    PemReader pemReader = new(new StringReader(privKeyString));
    AsymmetricKeyParameter pemPrivKey = (AsymmetricKeyParameter)pemReader.ReadObject();
    ISigner signer = SignerUtilities.GetSigner("Ed25519");
    signer.Init(true, pemPrivKey);
    signer.BlockUpdate(signatureBaseBytes , 0, signatureBaseBytes.Length);
    byte[] signedSignatureBytes = signer.GenerateSignature();
    signatureBase64 = Convert.ToBase64String(signedSignatureBytes);
    
    // Verify
    PemReader pemPubReader = new(new StringReader(pubKeyString));
    AsymmetricKeyParameter pemPubKey = (AsymmetricKeyParameter)pemPubReader.ReadObject();
    ISigner verifier = SignerUtilities.GetSigner("Ed25519");
    verifier.Init(false, pemPubKey);
    verifier.BlockUpdate(Encoding.UTF8.GetBytes(signatureBase), 0, signatureBase.Length);
    bool verified = verifier.VerifySignature(signedSignatureBytes);
    
    //if (!verified) return;
    
    RestClient client = new(url)
    {
        Authenticator = new RestSharp.Authenticators.OAuth2.OAuth2AuthorizationRequestHeaderAuthenticator(ebayAccessToken.AccessToken.Token, "Bearer"),
    };
    
    RestRequest request = new(endpoint, method: Method.Get);
    Dictionary<string, string> headers = new()
    {
        { "Content-Type", "application/json" },
        { "X-EBAY-C-MARKETPLACE-ID", "EBAY-DE" },
        { "x-ebay-signature-key", keyResponse.Jwe },
        { "x-ebay-enforce-signature", "true" },
        { "Signature", $"sig1=:{signatureBase64}:" },
        { "Signature-Input", "sig1=" + signatureParams },
    };
    request.AddHeaders(headers);
    
    RestResponse response = await client.ExecuteAsync(request);
    
    if (response.StatusCode != HttpStatusCode.OK)
    {
        // CUSTOM CLASS 'RestfulErrorResponse' FOR DESERIALIZING RESPONSE ERRORS
        RestfulErrorResponse errors = JsonConvert.DeserializeObject<RestfulErrorResponse>(response.Content);
    }
    

  2. For the decoding of the keys from the response, it appears that you could decode the private key like this:

    Org.BouncyCastle.Security.PrivateKeyFactory.CreateKey(Convert.FromBase64String(keyResponse.PrivateKey))
    

    Similarly the public key:

    Org.BouncyCastle.Security.PublicKeyFactory.CreateKey(Convert.FromBase64String(keyResponse.PublicKey))
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search