全部产品
Search
文档中心

Simple Message Queue (formerly MNS):Verifikasi tanda tangan pada server HTTP

更新时间:Jul 02, 2025

Dalam permintaan untuk mendorong pesan dari Simple Message Queue (sebelumnya MNS) ke titik akhir, tanda tangan di header Authorization dihasilkan menggunakan algoritma RSA-SHA1 pada string-to-sign. Topik ini menjelaskan cara memverifikasi tanda tangan pada server HTTP menggunakan kunci publik.

Langkah 1: Ambil Sertifikat X509

Dalam permintaan HTTP yang dikirim dari SMQ ke titik akhir, header x-mns-signing-cert-url menentukan string yang menunjukkan URL sertifikat tanda tangan. Anda harus mendekode Base64 pada string tersebut untuk mengambil URL sertifikat tanda tangan dan mendapatkan kunci publik dari sertifikat.

Catatan

Sertifikat tanda tangan hanya valid jika awalan URL sertifikat tanda tangan adalah https://mnstest.oss-cn-hangzhou.aliyuncs.com/. Jika tidak, sertifikat tersebut tidak valid. Untuk informasi lebih lanjut, lihat Bagaimana cara memeriksa apakah URL sertifikat kunci publik disediakan oleh Alibaba Cloud?

Langkah 2: Hitung nilai string-to-sign

Buat string-to-sign berdasarkan pseudocode berikut:

StringToSign = HttpMethod + "\n" 
         + CONTENT-MD5 + "\n"     
         + CONTENT-TYPE + "\n" 
         + DATE + "\n" 
         + CanonicalizedMNSHeaders
         + CanonicalizedResource;

Tabel berikut menjelaskan parameter.

Parameter

Deskripsi

HttpMethod

Metode HTTP dalam huruf besar, seperti PUT, GET, POST, atau DELETE.

Content-MD5

Nilai hash MD5 dari badan permintaan. Jika header Content-MD5 tidak ditentukan, biarkan parameter ini kosong.

CONTENT-TYPE

Jenis badan permintaan. Jika header Content-Type tidak ditentukan, biarkan parameter ini kosong.

DATE

Waktu ketika permintaan dikirim.

  • Contoh: Thu, 07 Mar 2012 18:49:58 GMT. Jika Anda menggunakan header x-mns-date sebagai pengganti header Date, atur parameter ini ke nilai header x-mns-date.

  • Parameter ini diperlukan dan nilainya harus dalam format GMT.

  • Jika SMQ tidak menerima permintaan dalam waktu 15 menit setelah permintaan dikirim, SMQ menganggap permintaan tersebut tidak valid dan mengembalikan kode kesalahan 400. Untuk informasi lebih lanjut, lihat Kode kesalahan.

CanonicalizedMNSHeaders

Kombinasi header HTTP yang diawali oleh x-mns-. Nilai parameter ini harus memenuhi persyaratan berikut:

  • Nama header harus dalam huruf kecil.

  • Header harus diurutkan secara alfabetis.

  • Header digabungkan.

    // Header permintaan asli.
    Map<String, String> httpHeaders = request.getHeaders();
    // Urutkan header dan ubah header menjadi huruf kecil.
    sortHeadersKeyAndToLowerCase(httpHeaders);
    // Gabungkan header.
    Set<String> keySet = httpHeaders.keySet();
    for (String key : keySet) {
        if (key.startsWith("x-mns-")) {
            CanonicalizedMNSHeaders.append(key).append(":")
                .append(httpHeaders.get(key)).append("\n");
        }
    }

CanonicalizedResource

Pengenal Sumber Daya Seragam (URI) dari sumber daya yang diminta oleh permintaan HTTP. Anda dapat memperoleh URI dengan menghapus nama domain dan nomor port dari alamat IP server HTTP yang merespons permintaan. Sebagai contoh, jika Anda menggunakan titik akhir http://123.123.XX.XX:8080/api/test?code=200, URI-nya adalah /api/test?code=200; URI-nya adalah /mns/help untuk http://www.aliyun.com/mns/help.

Penting

Dalam skenario di mana gateway atau gateway transit lainnya dikonfigurasikan di sistem Anda, URI sumber daya yang diminta oleh permintaan HTTP akan berlaku jika berbeda dari URI permintaan API yang diverifikasi oleh SMQ.

Contoh:

POST
ZDgxNjY5ZjFlMDQ5MGM0YWMwMWE5ODlmZDVlYmQxYjI=
text/xml;charset=utf-8
Wed, 25 May 2016 10:46:14 GMT
x-mns-request-id:57458276F0E3D56D7C00****
x-mns-signing-cert-url:aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5w****
x-mns-version:2015-06-06
/notifications       

Langkah 3: Dekripsi header Authorization

Setelah melakukan dekode Base64 pada header Authorization, Anda dapat mendekripsi nilai yang didekode menggunakan kunci publik yang diambil di Langkah 1.

Langkah 4: Verifikasi tanda tangan

Bandingkan string-to-sign yang dihasilkan di Langkah 2 dengan string yang didekripsi yang dihasilkan di Langkah 3. Jika kedua string sama, permintaan dari SMQ valid. Jika tidak, tolak permintaan.

Contoh kode Java

import org.apache.commons.codec.binary.Base64;

import java.io.DataInputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class SignDemo {
    private Boolean authenticate(String method, String uri, Map<String, String> headers) {
        try {
            // Ambil URL sertifikat tanda tangan. 
            if (!headers.containsKey("x-mns-signing-cert-url")) {
                System.out.println("Header x-mns-signing-cert-url tidak ditemukan");
                return false;
            }
            String cert = headers.get("x-mns-signing-cert-url");
            if (cert.isEmpty()) {
                System.out.println("x-mns-signing-cert-url kosong");
                return false;
            }
            cert = new String(Base64.decodeBase64(cert));
            System.out.println("x-mns-signing-cert-url:\t" + cert);

            // Gunakan URL untuk mengambil sertifikat dan ambil kunci publik dari sertifikat. 
            URL url = new URL(cert);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            DataInputStream in = new DataInputStream(conn.getInputStream());
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            Certificate c = cf.generateCertificate(in);
            PublicKey pk = c.getPublicKey();

            // Ambil string-to-sign. 
            String str2sign = getSignStr(method, uri, headers);
            System.out.println("String2Sign:\t" + str2sign);

            // Dekode header Authorization dalam Base64. 
            String signature = headers.get("Authorization");
            byte[] decodedSign = Base64.decodeBase64(signature);

            // Verifikasi tanda tangan. 
            java.security.Signature signetcheck = java.security.Signature.getInstance("SHA1withRSA");
            signetcheck.initVerify(pk);
            signetcheck.update(str2sign.getBytes());
            Boolean res = signetcheck.verify(decodedSign);
            return res;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private String getSignStr(String method, String uri, Map<String, String> headers) {
        StringBuilder sb = new StringBuilder();
        sb.append(method);
        sb.append("\n");
        sb.append(safeGetHeader(headers, "Content-md5"));
        sb.append("\n");
        sb.append(safeGetHeader(headers, "Content-Type"));
        sb.append("\n");
        sb.append(safeGetHeader(headers, "Date"));
        sb.append("\n");

        List<String> tmp = new ArrayList<String>();
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            if (entry.getKey().startsWith("x-mns-")) {
                tmp.add(entry.getKey() + ":" + entry.getValue());
            }
        }
        Collections.sort(tmp);

        for (String kv : tmp) {
            sb.append(kv);
            sb.append("\n");
        }

        sb.append(uri);
        return sb.toString();
    }

    private String safeGetHeader(Map<String, String> headers, String name) {
        if (headers.containsKey(name)) {
            return headers.get(name);
        } else {
            return "";
        }
    }

    public static void main(String[] args) {
        SignDemo sd = new SignDemo();
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Authorization", "Mko2Azg9fhCw8qR6G7AeAFMyzjO9qn7LDA5/t9E+6X5XURXTqBUuhpK+K55UNhrnlE2UdDkRrwDxsaDP5ajQ****");
        headers.put("Content-md5", "M2ViOTE2ZDEyOTlkODBjMjVkNzM4YjNhNWI3ZWQ1****");
        headers.put("Content-Type", "text/xml;charset=utf-8");
        headers.put("Date", "Tue, 23 Feb 2016 09:41:06 GMT");
        headers.put("x-mns-request-id", "56CC2932F0E3D5BD5306****");
        headers.put("x-mns-signing-cert-url", "aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5w****");
        headers.put("x-mns-version", "2015-06-06");
        Boolean res = sd.authenticate("POST", "/notifications", headers);
        System.out.println("Hasil autentikasi:" + res);
    }
}