PureBasic
PureBasic
ebay: Add Digital Signature to HTTP Request
See more eBay Examples
Demonstrates how to add a digital signature to an ebay HTTP request.Chilkat PureBasic Downloads
IncludeFile "CkBinData.pb"
IncludeFile "CkDateTime.pb"
IncludeFile "CkPrivateKey.pb"
IncludeFile "CkHttp.pb"
IncludeFile "CkEdDSA.pb"
IncludeFile "CkStringBuilder.pb"
IncludeFile "CkHttpResponse.pb"
Procedure ChilkatExample()
success.i = 0
; This example requires the Chilkat API to have been previously unlocked.
; See Global Unlock Sample for sample code.
; Note: Ebay provides a Key Management API
; See https://developer.ebay.com/api-docs/developer/key-management/overview.html
; The following test keys can be used:
;
; Ed25519
;
; Private Key:
;
; -----BEGIN PRIVATE KEY-----
; MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikTmiCqirVv9mWG9qfSnF
; -----END PRIVATE KEY-----
strPrivateKey.s = "MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikTmiCqirVv9mWG9qfSnF"
;
; Public Key:
;
; -----BEGIN PUBLIC KEY-----
; MCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=
; -----END PUBLIC KEY-----
strPublicKey.s = "MCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs="
; This example assumes you got a JWE for your given private key from the Ebay Key Management REST API.
; This JWE is just for example:
strJwe.s = "eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoiSXh2dVRMb0FLS0hlS0Zoa3BxQ05CUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiaFd3YjNoczk2QzEyOTNucCJ9.2o02pR9SoTF4g_5qRXZm6tF4H52TarilIAKxoVUqjd8.3qaF0KJN-rFHHm_P.AMUAe9PPduew09mANIZ-O_68CCuv6EIx096rm9WyLZnYz5N1WFDQ3jP0RBkbaOtQZHImMSPXIHVaB96RWshLuJsUgCKmTAwkPVCZv3zhLxZVxMXtPUuJ-ppVmPIv0NzznWCOU5Kvb9Xux7ZtnlvLXgwOFEix-BaWNomUAazbsrUCbrp514GIea3butbyxXLNi6R9TJUNh8V2uan-optT1MMyS7eMQnVGL5rYBULk.9K5ucUqAu0DqkkhgubsHHw"
sbBody.i = CkStringBuilder::ckCreate()
If sbBody.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbBody,"{" + Chr(34) + "hello" + Chr(34) + ": " + Chr(34) + "world" + Chr(34) + "}")
Debug "Body of request:"
Debug CkStringBuilder::ckGetAsString(sbBody)
; -------------------------------------------------
; Build the signature base string...
sbSigBase.i = CkStringBuilder::ckCreate()
If sbSigBase.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbSigBase,Chr(34) + "content-digest" + Chr(34) + ": sha-256=:")
CkStringBuilder::ckAppend(sbSigBase,CkStringBuilder::ckGetHash(sbBody,"sha256","base64","utf-8"))
CkStringBuilder::ckAppend(sbSigBase,":" + Chr(10))
CkStringBuilder::ckAppend(sbSigBase,Chr(34) + "x-ebay-signature-key" + Chr(34) + ": ")
CkStringBuilder::ckAppend(sbSigBase,strJwe)
CkStringBuilder::ckAppend(sbSigBase,Chr(10))
CkStringBuilder::ckAppend(sbSigBase,Chr(34) + "@method" + Chr(34) + ": POST" + Chr(10))
; This is the path part of the URL without query params...
CkStringBuilder::ckAppend(sbSigBase,Chr(34) + "@path" + Chr(34) + ": ")
CkStringBuilder::ckAppend(sbSigBase,"/verifysignature")
CkStringBuilder::ckAppend(sbSigBase,Chr(10))
; The is the domain, such as "api.ebay.com" w/ port if the port is something unusual.
; In this example, we're testing against a local docker test server (see the info at https://developer.ebay.com/develop/guides/digital-signatures-for-apis)
; Normally, I think it would just be "api.ebay.com" instead of "localhost:8080".
CkStringBuilder::ckAppend(sbSigBase,Chr(34) + "@authority" + Chr(34) + ": ")
CkStringBuilder::ckAppend(sbSigBase,"localhost:8080")
CkStringBuilder::ckAppend(sbSigBase,Chr(10))
CkStringBuilder::ckAppend(sbSigBase,Chr(34) + "@signature-params" + Chr(34) + ": ")
sbSigInput.i = CkStringBuilder::ckCreate()
If sbSigInput.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbSigInput,"(" + Chr(34) + "content-digest" + Chr(34) + " " + Chr(34) + "x-ebay-signature-key" + Chr(34) + " " + Chr(34) + "@method" + Chr(34) + " " + Chr(34) + "@path" + Chr(34) + " " + Chr(34) + "@authority" + Chr(34) + ")")
CkStringBuilder::ckAppend(sbSigInput,";created=")
dt.i = CkDateTime::ckCreate()
If dt.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkDateTime::ckSetFromCurrentSystemTime(dt)
unixTimeNow.s = CkDateTime::ckGetAsUnixTimeStr(dt,0)
CkStringBuilder::ckAppend(sbSigInput,unixTimeNow)
CkStringBuilder::ckAppendSb(sbSigBase,sbSigInput)
; -------------------------------------------------
; Sign the signature base string using the Ed25519 private key
bdPrivKey.i = CkBinData::ckCreate()
If bdPrivKey.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkBinData::ckAppendEncoded(bdPrivKey,strPrivateKey,"base64")
privKey.i = CkPrivateKey::ckCreate()
If privKey.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
success = CkPrivateKey::ckLoadAnyFormat(privKey,bdPrivKey,"")
If success = 0
Debug CkPrivateKey::ckLastErrorText(privKey)
CkStringBuilder::ckDispose(sbBody)
CkStringBuilder::ckDispose(sbSigBase)
CkStringBuilder::ckDispose(sbSigInput)
CkDateTime::ckDispose(dt)
CkBinData::ckDispose(bdPrivKey)
CkPrivateKey::ckDispose(privKey)
ProcedureReturn
EndIf
bdToBeSigned.i = CkBinData::ckCreate()
If bdToBeSigned.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkBinData::ckAppendSb(bdToBeSigned,sbSigBase,"utf-8")
eddsa.i = CkEdDSA::ckCreate()
If eddsa.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
sigBase64.s = CkEdDSA::ckSignBdENC(eddsa,bdToBeSigned,"base64",privKey)
If CkEdDSA::ckLastMethodSuccess(eddsa) = 0
Debug CkEdDSA::ckLastErrorText(eddsa)
CkStringBuilder::ckDispose(sbBody)
CkStringBuilder::ckDispose(sbSigBase)
CkStringBuilder::ckDispose(sbSigInput)
CkDateTime::ckDispose(dt)
CkBinData::ckDispose(bdPrivKey)
CkPrivateKey::ckDispose(privKey)
CkBinData::ckDispose(bdToBeSigned)
CkEdDSA::ckDispose(eddsa)
ProcedureReturn
EndIf
Debug "sigBase64:"
Debug sigBase64
; ----------------------------------------------------------
; Send the JSON POST
http.i = CkHttp::ckCreate()
If http.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkHttp::ckSetRequestHeader(http,"x-ebay-signature-key",strJwe)
sbContentDigestHdr.i = CkStringBuilder::ckCreate()
If sbContentDigestHdr.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbContentDigestHdr,"sha-256=:")
CkStringBuilder::ckAppend(sbContentDigestHdr,CkStringBuilder::ckGetHash(sbBody,"sha256","base64","utf-8"))
CkStringBuilder::ckAppend(sbContentDigestHdr,":")
CkHttp::ckSetRequestHeader(http,"Content-Digest",CkStringBuilder::ckGetAsString(sbContentDigestHdr))
sbSigHdr.i = CkStringBuilder::ckCreate()
If sbSigHdr.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbSigHdr,"sig1=:")
CkStringBuilder::ckAppend(sbSigHdr,sigBase64)
CkStringBuilder::ckAppend(sbSigHdr,":")
CkHttp::ckSetRequestHeader(http,"Signature",CkStringBuilder::ckGetAsString(sbSigHdr))
CkStringBuilder::ckPrepend(sbSigInput,"sig1=")
CkHttp::ckSetRequestHeader(http,"Signature-Input",CkStringBuilder::ckGetAsString(sbSigInput))
; Add this header to make eBay actually check the signature.
CkHttp::ckSetRequestHeader(http,"x-ebay-enforce-signature","true")
; Set the OAuth2 access token to add the "Authorization: Bearer <access_token>" to the header.
CkHttp::setCkAuthToken(http, "your_oauth2_access_token")
; The signature base string constructed above is valid if we send this POST to "http://localhost:8080/verifysignature"
; Normally, you'll send your POST to some api.ebay.com endpoint.
url.s = "http://localhost:8080/verifysignature"
jsonStr.s = CkStringBuilder::ckGetAsString(sbBody)
resp.i = CkHttpResponse::ckCreate()
If resp.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
success = CkHttp::ckHttpStr(http,"POST","http://localhost:8080/verifysignature",jsonStr,"utf-8","application/json",resp)
If success = 0
Debug CkHttp::ckLastErrorText(http)
CkStringBuilder::ckDispose(sbBody)
CkStringBuilder::ckDispose(sbSigBase)
CkStringBuilder::ckDispose(sbSigInput)
CkDateTime::ckDispose(dt)
CkBinData::ckDispose(bdPrivKey)
CkPrivateKey::ckDispose(privKey)
CkBinData::ckDispose(bdToBeSigned)
CkEdDSA::ckDispose(eddsa)
CkHttp::ckDispose(http)
CkStringBuilder::ckDispose(sbContentDigestHdr)
CkStringBuilder::ckDispose(sbSigHdr)
CkHttpResponse::ckDispose(resp)
ProcedureReturn
EndIf
Debug "Response status code: " + Str(CkHttpResponse::ckStatusCode(resp))
Debug "Response body:"
Debug CkHttpResponse::ckBodyStr(resp)
CkStringBuilder::ckDispose(sbBody)
CkStringBuilder::ckDispose(sbSigBase)
CkStringBuilder::ckDispose(sbSigInput)
CkDateTime::ckDispose(dt)
CkBinData::ckDispose(bdPrivKey)
CkPrivateKey::ckDispose(privKey)
CkBinData::ckDispose(bdToBeSigned)
CkEdDSA::ckDispose(eddsa)
CkHttp::ckDispose(http)
CkStringBuilder::ckDispose(sbContentDigestHdr)
CkStringBuilder::ckDispose(sbSigHdr)
CkHttpResponse::ckDispose(resp)
ProcedureReturn
EndProcedure