# Signature - chữ kí điện tử

**VinID** sử dụng **chữ ký điện tử (signature)** để xác thực dữ liệu đầu vào và ra trên mỗi HTTP Request.

Hệ thống **VinID** sử dụng thuật toán **SHA256 RSA ( key size = 2048 bit)** để tạo signature.

### **Tạo chữ ký điện tử**

{% tabs %}
{% tab title="\*Unix" %}

```
openssl genrsa -out RS256-2048-Private.rsa 2048
openssl rsa -in RS256-2048-Private.rsa -pubout > RS256-2048-Public.rsa
```

{% endtab %}

{% tab title="MacOS (Brew)" %}

```
brew update
brew install openssl
echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile

openssl genrsa -out RS256-2048-Private.rsa 2048
openssl rsa -in RS256-2048-Private.rsa -pubout > RS256-2048-Public.rsa
```

{% endtab %}

{% tab title="Windows" %}

* Tải và cài đặt OpenSSL từ <https://wiki.openssl.org/index.php/Binaries>
* Thêm đường dẫn vào biến môi trường PATH của Windows

```
openssl genrsa -out RS256-2048-Private.rsa 2048
openssl rsa -in RS256-2048-Private.rsa -pubout > RS256-2048-Public.rsa
```

{% endtab %}
{% endtabs %}

### **Format chữ ký điện tử**

* **PrivateKey**: do merchant tự generate ra theo định dạng SHA256 RSA ( 2048 )
* **URL**: Endpoint AP**I**

{% tabs %}
{% tab title="POST" %}

```
RawData = {url};{method};{X-Nonce};{X-Timestamp};{X-Key-Code};{body}
X-Signature = ​SHA256WithRSA(PrivateKey, RawData)
```

{% endtab %}

{% tab title="GET" %}

```
RawData = {url};{method};{X-Nonce};{X-Timestamp};{X-Key-Code};
X-Signature = ​SHA256WithRSA(PrivateKey, RawData)
```

{% endtab %}
{% endtabs %}

Ví dụ:&#x20;

{% tabs %}
{% tab title="POST" %}

```
url: '/merchant-integration/v1/qr/gen-transaction-qr'
method: 'POST'
X-Nonce: '00a81e60-2684-4cf9-878d-f37559213059'
X-Timestamp: 1570723375
X-Key-Code: 'b7bdf002-4948-44d2-99d1-99c8c81c3f47'
Body: {"callback_url":"https://webhook.site/17d9577f-70ca-4918-8388-1d6d53d8bc69","description":"Kiểm thử thanh toán","order_amount":10000,"order_currency":"VND","pos_code":"IPOS002","service_type":"PURCHASE","store_code":"ISTORE002"}
RawData: /merchant-integration/v1/qr/gen-transaction-qr;POST;00a81e60-2684-4cf9-878d-f37559213059;1570723375;b7bdf002-4948-44d2-99d1-99c8c81c3f47;{"callback_url":"https://webhook.site/17d9577f-70ca-4918-8388-1d6d53d8bc69","description":"Kiểm thử thanh toán","order_amount":10000,"order_currency":"VND","pos_code":"IPOS002","service_type":"PURCHASE","store_code":"ISTORE002"}
X-Signature: SHA256WithRSA(PrivateKey, RawData)
=> X-Signature: ERSt3cZoijwJf8QIZdcpHPygcDQJ+tA7l/2EVyJyhO8fwpp6mbrYWsyhyqDjD4zkhGwcXVJ7zJQUDWFpCPitG9GlmssEnkp57YK94/6GR54x6COzPqOlJjoQ4Lq6Fvw99QVmjiL+WjGWSOTUkusXh9dr871vCrjcYmyFSCZ9Ydfw6l4iv2evOnibUtalIkdsNM6evrVa7qW28Uno5t8fmz68QJeXd0dqL4lm2tsFAr9094jbWh/zlynqAW9MOIIYjXQC7XwEVKiBEmoZyTnSd8SLITLNIXvLCJjjVT5XYqZAMNIyoNYZKom8dUjLuirBBurcUpXuxlu01O8dU9qHdg==
```

{% endtab %}

{% tab title="GET" %}

```
url: '/merchant-integration/v2/qr/query/20200623T0017FB54CBB'
method: 'GET'
X-Nonce: '00a81e60-2684-4cf9-878d-f37559213059'
X-Timestamp: 1570723375
X-Key-Code: 'b7bdf002-4948-44d2-99d1-99c8c81c3f47'
RawData: /merchant-integration/v2/qr/query/20200623T0017FB54CBB;GET;00a81e60-2684-4cf9-878d-f37559213059;1570723375;b7bdf002-4948-44d2-99d1-99c8c81c3f47;
X-Signature: SHA256WithRSA(PrivateKey, RawData)
=> X-Signature: ERSt3cZoijwJf8QIZdcpHPygcDQJ+tA7l/2EVyJyhO8fwpp6mbrYWsyhyqDjD4zkhGwcXVJ7zJQUDWFpCPitG9GlmssEnkp57YK94/6GR54x6COzPqOlJjoQ4Lq6Fvw99QVmjiL+WjGWSOTUkusXh9dr871vCrjcYmyFSCZ9Ydfw6l4iv2evOnibUtalIkdsNM6evrVa7qW28Uno5t8fmz68QJeXd0dqL4lm2tsFAr9094jbWh/zlynqAW9MOIIYjXQC7XwEVKiBEmoZyTnSd8SLITLNIXvLCJjjVT5XYqZAMNIyoNYZKom8dUjLuirBBurcUpXuxlu01O8dU9qHdg==
```

{% endtab %}
{% endtabs %}

**Code mẫu tạo Signature**

{% tabs %}
{% tab title="Go" %}

```go
// GenerateSignature util to generate signature form Auth Claim of merchant
func GenerateSignature(claim *dtos.AuthClaims, privKey []byte) (string, error) {
    block, _ := pem.Decode(privKey)
    privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
        fmt.Printf("Error ParsePKCS1PrivateKey: %v", err)
        return "", err
    }
    hash := generateHash(claim.URL, claim.Method, claim.Nonce, claim.Timestamp, claim.KeyCode, claim.Body)
    signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
    if err != nil {
        fmt.Printf("Error from signing: %s\n", err)
        return "", err
    }
    claim.Signature = base64.StdEncoding.EncodeToString(signature)
    return claim.Signature, nil
}

```

{% endtab %}

{% tab title="PHP" %}

```php
function generateSignature($url, $method, $nonce, $timestamp, $apiKey, $requestBody, $privateKey)
{
    $data = $url . ";" . $method . ";" . $nonce . ";" . $timestamp . ";" . $apiKey . ";" . $requestBody;
    $p = openssl_pkey_get_private($privateKey);
    if (!$p) {
        trigger_error("Invalid private key\n".$privateKey, E_USER_WARNING);
        throw new InvalidPrivateKeyException($privateKey);
    }
    $signSuccess = openssl_sign($data, $signature, $p, OPENSSL_ALGO_SHA256);
    $encodedSignature = base64_encode($signature);
    openssl_free_key($p);
    return $encodedSignature;
}
```

{% endtab %}

{% tab title="Python" %}

```python
def generateSign():
    requestBody = json.dumps(params, default=lambda o:o.__dict__, ensure_ascii=False,separators=(',', ':'))
    data = url+";"+method+";"+nonce+";"+str(int(timestamp))+";"+keyCode+";"+requestBody
    f = open("/home/vid/Documents/leo/sb_private.pem", "r")
    pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read())
    sign = OpenSSL.crypto.sign(pkey, data, "sha256")
    encodedSign = base64.b64encode(sign)
    return encodedSign
```

{% endtab %}

{% tab title="Java" %}

```java
import java.io.File;
import java.nio.file.Files;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class VinIDSignature {

    //The method that signs the data using the private key that is stored in keyFile path
    public String generateSign(String url, String method, String nonce, String timestamp, String keyCode, String requestBody, String keyFile) throws Exception {
        String body = requestBody == null || "".equals(requestBody) ? "" : requestBody;
        String data = url + ";" + method + ";" + nonce + ";" + timestamp + ";" + keyCode + ";" + body;
        java.security.Signature rsa = java.security.Signature.getInstance("SHA256withRSA");
        rsa.initSign(getPrivate(keyFile));
        rsa.update(data.getBytes());
        return new String(Base64.getEncoder().encode(rsa.sign()));
    }

    //Method to retrieve the Private Key from a file
    private PrivateKey getPrivate(String filename) throws Exception {
        byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(spec);
    }
}

```

{% endtab %}

{% tab title="C#" %}

```csharp
using System.Text;
using System.IO;
using java.security.spec;
using java.security;
using System;
using System.Security.Cryptography;

namespace OneID
{
    class Security
    {
        public string sign(string url, string method, string nonce, string timestamp, string keycode, string requestBody, string keyfile)
        {
            string data = url + ";" + method + ";" + nonce + ";" + timestamp + ";" + keycode + ";" + requestBody;
            RSACryptoServiceProvider ras = PemKeyUtils.GetRSAProviderFromPemFile(keyfile);
            return System.Convert.ToBase64String(ras.SignData(Encoding.ASCII.GetBytes(data), CryptoConfig.MapNameToOID("SHA256")));
        }
    }
}

```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.vinid.net/tai-lieu-tich-hop/quy-tac-ket-noi/signature-chu-ki-dien-tu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
