Signature Validation
Partner wajib melakukan validasi pada request callback yang kami kirimkan
Public Key
Dalam validasi signature callback partner memerlukan public key milik speedcash pada prosesnya. Untuk production akan dikirimkan via email dengan format tertentu.
Rumus Signature
Signature pada validasi callback mengambil data pada header request yang dikirimkan speedcash.
stringToSign = HTTPMethod +":"+ EndpointUrl +":"+ Lowercase(HexEncode(SHA-256(minify(RequestBody)))) + ":" + TimeStamp
signature = base64_decode(SHA256withRSA(private_key, stringToSign))
Source Code Validasi Signature Callback
- PHP
- Node JS
- Go
$path = '/v1.0/debit/notify';
$timestamp = ''; //ambil dari header X-TIMESTAMP
$signature = ''; //ambil dari header X-SIGNATURE
$httpMethod = 'POST';
$partnerId = ''; //ambil dari header X-PARTNER-ID
$body = '' // ambil dari body payload
$payload = json_encode($body, JSON_UNESCAPED_SLASHES);
$stringToSignArr = [
$httpMethod,
$path,
strtolower(bin2hex(hash('sha256', $payload, true))),
$timestamp
];
$stringToSign = implode(':', $stringToSignArr);
try {
$publicKey = openssl_get_publickey($publicKey);
$verify = openssl_verify($stringToSign, base64_decode($signature), $publicKey, OPENSSL_ALGO_SHA256);
if($verify !== 1){
$response = [
'message' => 'Cannot verify signature'
];
print_r($response);
} else {
$response = [
'responseCode' => '2005600',
'responseMessage' => 'Successful'
];
print_r($response);
}
} catch(Exception $e) {
$response = [
'message' => 'Invalid signature {'.$e->getMessage().'}'
];
print_r($response);
}
const path = "/v1.0/debit/notify";
const timestamp = ""; //ambil dari header X-TIMESTAMP
const signature = ""; //ambil dari header X-SIGNATURE
const httpMethod = "POST";
const partnerId = ""; //ambil dari header X-PARTNER-ID
const body = ""; // ambil dari body payload
const payload = JSON.stringify(body);
const stringToSignArr = [
httpMethod,
path,
hash(payload).toLowerCase(),
timestamp,
];
const stringToSign = stringToSignArr.join(":");
try {
const publicKey = openssl_get_publickey(publicKey);
const verify = openssl_verify(
stringToSign,
Buffer.from(signature, "base64"),
publicKey,
"sha256"
);
if (verify !== 1) {
const response = {
message: "Cannot verify signature",
};
console.log(response);
} else {
const response = {
responseCode: "2005600",
responseMessage: "Successful",
};
console.log(response);
}
} catch (e) {
const response = {
message: "Invalid signature {" + e.getMessage() + "}",
};
console.log(response);
}
package main
import (
"bytes"
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"strings"
)
func main() {
path := "/v1.0/debit/notify"
timestamp := "" // ambil dari header X-TIMESTAMP
method := "POST" // http method
signature := "" // ambil dari header X-SIGNATURE
payloadStr := "" // ambil dari body payload
minifyBody, _ := Minify([]byte(payloadStr))
stringToSign := fmt.Sprintf("%s:%s:%s:%s", method, path, string(minifyBody), timestamp)
signatureOk := ValidateSignature(stringToSign, signature)
fmt.Println("Apakah signature valid?")
fmt.Println(signatureOk)
}
func Minify(body []byte) ([]byte, error) {
buff := new(bytes.Buffer)
errCompact := json.Compact(buff, body)
if errCompact != nil {
newErr := fmt.Errorf("failure encountered compacting json := %v", errCompact)
return nil, newErr
}
b, err := io.ReadAll(buff)
if err != nil {
readErr := fmt.Errorf("read buffer error encountered := %v", err)
return nil, readErr
}
// LowerCase(HexEncode(SHA-256(stringToMinify)))
hasher := sha256.New()
hasher.Write([]byte(b))
hash := hex.EncodeToString(hasher.Sum(nil))
return []byte(strings.ToLower(hash)), nil
}
func ValidateSignature(data string, signature string) bool {
publicKey := "" // ambil dari public key yang di kirimkan speedcash
pubKeyBytes, err := base64.StdEncoding.DecodeString(publicKey)
if err != nil {
panic(err)
}
parsedPubKey, err := x509.ParsePKIXPublicKey(pubKeyBytes)
if err != nil {
panic(err)
}
pubKey, ok := parsedPubKey.(*rsa.PublicKey)
if !ok {
panic("public key is not RSA")
}
hasher := sha256.New()
hasher.Write([]byte(data))
signatureBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
panic(err)
}
err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hasher.Sum(nil), signatureBytes)
if err != nil {
return false
}
return true
}