DataFlex Requires Chilkat v11.5.0+
DataFlex
Box: Verify a Captured V2 Webhook Signature
See more Box Examples
Verify raw payload bytes and captured headers using the configured primary and secondary signing keys. The example rejects stale/future timestamps and compares randomized HMACs of the signatures to avoid exposing a signature-prefix timing comparison. It does not host an HTTP listener. The receiver must persist/deduplicate the verified event and acknowledge promptly.
See the Box API guide for setup and the complete workflow.
Chilkat DataFlex Downloads
Use ChilkatAx-win32.pkg
Procedure Test
Boolean iSuccess
Variant vRawBody
Handle hoRawBody
String sTimestamp
String sPrimarySignature
String sSecondarySignature
String sSignatureVersion
String sSignatureAlgorithm
String sPrimaryKey
String sSecondaryKey
Handle hoDelivered
Handle hoNow
Integer iAge
Variant vSignedBytes
Handle hoSignedBytes
Handle hoMac
String sExpectedPrimary
String sExpectedSecondary
Handle hoRandom
String sComparisonKey
String sPExpected
String sPReceived
String sSExpected
String sSReceived
Boolean iValidPrimary
Boolean iValidSecondary
Handle hoWebhookEvent
String sTemp1
Integer iTemp1
Integer iTemp2
Boolean bTemp1
Move False To iSuccess
// Requires Chilkat 11.5.0 or later and a previously unlocked Chilkat bundle.
// See Global Unlock Sample.
// Guide: Box API Guide.
Move False To iSuccess
// Save the exact HTTP body bytes to this file, without JSON reserialization.
Get Create (RefClass(cComChilkatBinData)) To hoRawBody
If (Not(IsComObjectCreated(hoRawBody))) Begin
Send CreateComObject of hoRawBody
End
Get ComLoadFile Of hoRawBody "qa_data/box_webhook_body.bin" To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoRawBody To sTemp1
Showln sTemp1
Procedure_Return
End
// Read these fields case-insensitively from the incoming request headers.
// For this offline example, enter the captured header values.
Move "BOX_DELIVERY_TIMESTAMP" To sTimestamp
Move "BOX_SIGNATURE_PRIMARY" To sPrimarySignature
Move "BOX_SIGNATURE_SECONDARY" To sSecondarySignature
Move "1" To sSignatureVersion
Move "HmacSHA256" To sSignatureAlgorithm
// Retrieve configured keys from protected application configuration.
Move "BOX_PRIMARY_SIGNATURE_KEY" To sPrimaryKey
Move "BOX_SECONDARY_SIGNATURE_KEY" To sSecondaryKey
If ((sSignatureVersion <> "1") Or (sSignatureAlgorithm <> "HmacSHA256")) Begin
Showln "Unsupported signature version or algorithm."
Procedure_Return
End
Get Create (RefClass(cComCkDateTime)) To hoDelivered
If (Not(IsComObjectCreated(hoDelivered))) Begin
Send CreateComObject of hoDelivered
End
Get ComSetFromTimestamp Of hoDelivered sTimestamp To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoDelivered To sTemp1
Showln sTemp1
Procedure_Return
End
Get Create (RefClass(cComCkDateTime)) To hoNow
If (Not(IsComObjectCreated(hoNow))) Begin
Send CreateComObject of hoNow
End
Get ComSetFromCurrentSystemTime Of hoNow To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoNow To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComGetAsUnixTime Of hoNow False To iTemp1
Get ComGetAsUnixTime Of hoDelivered False To iTemp2
Move (iTemp1 - iTemp2) To iAge
// Box recommends rejecting deliveries older than ten minutes.
// Allow at most 60 seconds of future clock skew; keep the server clock synchronized.
If ((iAge > 600) Or (iAge < -60)) Begin
Showln "Delivery timestamp is outside the accepted window."
Procedure_Return
End
Get Create (RefClass(cComChilkatBinData)) To hoSignedBytes
If (Not(IsComObjectCreated(hoSignedBytes))) Begin
Send CreateComObject of hoSignedBytes
End
Get pvComObject of hoRawBody to vRawBody
Get ComAppendBd Of hoSignedBytes vRawBody To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoSignedBytes To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComAppendString Of hoSignedBytes sTimestamp "utf-8" To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoSignedBytes To sTemp1
Showln sTemp1
Procedure_Return
End
Get Create (RefClass(cComChilkatCrypt2)) To hoMac
If (Not(IsComObjectCreated(hoMac))) Begin
Send CreateComObject of hoMac
End
Set ComMacAlgorithm Of hoMac To "hmac"
Set ComHashAlgorithm Of hoMac To "sha256"
Set ComEncodingMode Of hoMac To "base64"
Get ComSetMacKeyString Of hoMac sPrimaryKey To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
Get pvComObject of hoSignedBytes to vSignedBytes
Get ComMacBdENC Of hoMac vSignedBytes To sExpectedPrimary
Get ComLastMethodSuccess Of hoMac To bTemp1
If (bTemp1 <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComSetMacKeyString Of hoMac sSecondaryKey To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
Get pvComObject of hoSignedBytes to vSignedBytes
Get ComMacBdENC Of hoMac vSignedBytes To sExpectedSecondary
Get ComLastMethodSuccess Of hoMac To bTemp1
If (bTemp1 <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
// Blind both sides with a fresh secret random key before equality comparison.
// Never compare the original HMAC strings using a prefix-short-circuit comparison.
// A host-language receiver can instead use its standard constant-time byte comparison.
Get Create (RefClass(cComChilkatPrng)) To hoRandom
If (Not(IsComObjectCreated(hoRandom))) Begin
Send CreateComObject of hoRandom
End
Get ComGenRandom Of hoRandom 32 "base64" To sComparisonKey
Get ComLastMethodSuccess Of hoRandom To bTemp1
If (bTemp1 <> True) Begin
Get ComLastErrorText Of hoRandom To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComSetMacKeyEncoded Of hoMac sComparisonKey "base64" To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComMacStringENC Of hoMac sExpectedPrimary To sPExpected
Get ComLastMethodSuccess Of hoMac To bTemp1
If (bTemp1 <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComMacStringENC Of hoMac sPrimarySignature To sPReceived
Get ComLastMethodSuccess Of hoMac To bTemp1
If (bTemp1 <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComMacStringENC Of hoMac sExpectedSecondary To sSExpected
Get ComLastMethodSuccess Of hoMac To bTemp1
If (bTemp1 <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComMacStringENC Of hoMac sSecondarySignature To sSReceived
Get ComLastMethodSuccess Of hoMac To bTemp1
If (bTemp1 <> True) Begin
Get ComLastErrorText Of hoMac To sTemp1
Showln sTemp1
Procedure_Return
End
Move ((sPrimaryKey <> "") And (sPrimarySignature <> "") And (sPExpected = sPReceived)) To iValidPrimary
Move ((sSecondaryKey <> "") And (sSecondarySignature <> "") And (sSExpected = sSReceived)) To iValidSecondary
If ((iValidPrimary = False) And (iValidSecondary = False)) Begin
Showln "Signature verification failed."
Procedure_Return
End
// Parse JSON only after signature verification.
Get Create (RefClass(cComChilkatJsonObject)) To hoWebhookEvent
If (Not(IsComObjectCreated(hoWebhookEvent))) Begin
Send CreateComObject of hoWebhookEvent
End
Get ComGetString Of hoRawBody "utf-8" To sTemp1
Get ComLoad Of hoWebhookEvent sTemp1 To iSuccess
If (iSuccess <> True) Begin
Get ComLastErrorText Of hoWebhookEvent To sTemp1
Showln sTemp1
Procedure_Return
End
Get ComStringOf Of hoWebhookEvent "id" To sTemp1
Showln "Verified event ID: " sTemp1
Get ComStringOf Of hoWebhookEvent "trigger" To sTemp1
Showln "Trigger: " sTemp1
// Deduplicate using event.id, not BOX-DELIVERY-ID, which changes on retries.
// Verify the target belongs to your configured webhook before dispatching work.
End_Procedure