OpenAPI署名の検証

序文

バックエンド開発として、API インターフェースを提供する場合、またはサードパーティの API インターフェースに接続する場合、インターフェースのスクレイピング防止やリプレイなどのセキュリティ上の問題を考慮する必要があり、厳密には、双方が合意した署名ルールが必要です。追加した。

一般的なアイデア

通常の状況では、有効期限とセキュリティを考慮する限り、署名ルールに厳格なルールはなく、彼のことをあまり高く考える必要はありません。

事前に準備する

API KEY と秘密鍵 API SECRET を用意します。これらは通常 API プロバイダ自身が生成するものであり、漏洩しないことが保証されている限り固定ではありません。

1. タイムスタンプ

Unix タイムスタンプのタイムスタンプを生成します。時間はミリ秒単位 (つまり、1970 年 1 月 1 日 (UTC/GMT 午前 0 時) から経過したミリ秒数) まで正確です。実装方法は次のとおりです。

 String timestamp = Long.toString(System.currentTimeMillis()); 

2. ランダムな文字(nonce)を生成する

乱数ノンスを生成します (注: 現在の定義は 32 ビットで、乱数ツール クラスによって生成できます)。実装方法は次のとおりです。

String nonce = RandomStringUtils.randomAlphanumeric(32);

3.署名署名の生成

1) タイムスタンプ、ノンス、API_KEY の 3 つの文字列を「文字列の先頭文字の ASCII コード」に従って昇順に並べます (ソート処理中に ASCII コード値が同じ場合、次のビットがインクリメントされます)比較のために順次) (この種の並べ替え、「辞書順」とも呼ばれます)、並べ替えられた結果を文字列 join_str に結合します。 2) 次に、API_SECRET を使用して、上記で生成された文字列 join_str に対して hmac
-sha256 署名を行います。暗号化方法も可能ですが、固定されていません)、16 進数でエンコードして署名を取得します。

4. パラメータの結合

上記で取得したタイムスタンプ、ノンス、署名、および API_KEY を #{k}=#{v} に従って結合し、区別として「,」を使用して新しい文字列を形成します。これは、署名認証文字列認証を返します。スプライシングの代わりに、タイムスタンプ、ノンス、署名の 3 つのパラメータを HTTP リクエスト ヘッダーに直接入れるだけでよく、形式は一意ではありません。

キーコードのデモ

タイムスタンプ、ノンス、API_KEYの3つの文字列を「文字列の先頭文字のASCIIコード」に従って昇順に並べます(ソート処理でASCIIコードの値が同じ場合は、次のビットをインクリメントして比較します) 、文字列に結合します

public static String genOriString(String timestamp,String nonce,String API_KEY){
 
        ArrayList<String> beforesort = new ArrayList<String>();
        beforesort.add(API_KEY);
        beforesort.add(timestamp);
        beforesort.add(nonce);
 
        Collections.sort(beforesort, new SpellComparator());
        StringBuffer aftersort = new StringBuffer();
        for (int i = 0; i < beforesort.size(); i++) {
            aftersort.append(beforesort.get(i));
        }
 
        String join_str = aftersort.toString();
        return join_str;
    }

API_SECRET を使用して join_str で hmac-sha256 署名を実行し、16 進数でエンコードし、戻り値を返します。

public static String genEncryptString(String join_str, String API_SECRET){
 
        Key sk = new SecretKeySpec(API_SECRET.getBytes(), "HmacSHA256");
        Mac mac = Mac.getInstance(sk.getAlgorithm());
        mac.init(sk);
        final byte[] hmac = mac.doFinal(join_str.getBytes());//完成hmac-sha256签名
        StringBuilder sb = new StringBuilder(hmac.length * 2);
        Formatter formatter = new Formatter(sb);
            for (byte b : hmac) {
                formatter.format("%02x", b);
            }
        String signature = sb.toString();//完成16进制编码
        return signature;
    }

#{k}=#{v} と ',' に従って上記の値を結合し、署名認証文字列を返します。

public static String genauthorization(String API_KEY, String timestamp, String nonce, String signature){
 
        String authorization = "key=" + API_KEY
                     +",timestamp=" + timestamp
                         +",nonce=" + nonce
                     +",signature=" + signature;
        return authorization;
    }

SpringBoot の pom 依存関係

     <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
     </dependency>
 
     <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
     </dependency>

SpellComparator クラス

 
import java.util.Comparator;
 
 
public class SpellComparator implements Comparator<Object> {
    public int compare(Object o1, Object o2) {
        try{
            String s1 = new String(o1.toString().getBytes("GB2312"), "ISO-8859-1");
            String s2 = new String(o2.toString().getBytes("GB2312"), "ISO-8859-1");
            return s1.compareTo(s2);
        }catch (Exception e){
            e.printStackTrace();
        }
        return 0;
     }
}

完全に生成されたデモ

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Formatter;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.client.ClientProtocolException;
 
 
public class GenerateString {
    // 开放平台注册获取id(API KEY)
    public static final String id = "22bfe9745135";
    // 开放平台注册获取密钥(API SECRET)
    public static final String secret = "19fbdc10";
 
    private static final String HASH_ALGORITHM = "HmacSHA256";
    static String timestamp = Long.toString(System.currentTimeMillis());
    static String nonce = RandomStringUtils.randomAlphanumeric(32);
 
    public static String genOriString(String api_key){
 
        ArrayList<String> beforesort = new ArrayList<String>();
        beforesort.add(api_key);
        beforesort.add(timestamp);
        beforesort.add(nonce);
 
        Collections.sort(beforesort, new SpellComparator());
        StringBuffer aftersort = new StringBuffer();
        for (int i = 0; i < beforesort.size(); i++) {
            aftersort.append(beforesort.get(i));
        }
 
        String OriString = aftersort.toString();
        return OriString;
    }
 
    public static String genEncryptString(String genOriString, String api_secret)throws SignatureException {
        try{
            Key sk = new SecretKeySpec(api_secret.getBytes(), HASH_ALGORITHM);
            Mac mac = Mac.getInstance(sk.getAlgorithm());
            mac.init(sk);
            final byte[] hmac = mac.doFinal(genOriString.getBytes());
            StringBuilder sb = new StringBuilder(hmac.length * 2);
 
            @SuppressWarnings("resource")
            Formatter formatter = new Formatter(sb);
            for (byte b : hmac) {
                formatter.format("%02x", b);
            }
            String EncryptedString = sb.toString();
            return EncryptedString;
        }catch (NoSuchAlgorithmException e1){
            throw new SignatureException("error building signature, no such algorithm in device "+ HASH_ALGORITHM);
        }catch (InvalidKeyException e){
            throw new SignatureException("error building signature, invalid key " + HASH_ALGORITHM);
        }
    }
 
    public static String genHeaderParam(String api_key, String api_secret) throws SignatureException{
 
        String GenOriString = genOriString(api_key);
        String EncryptedString = genEncryptString(GenOriString, api_secret);
 
        String HeaderParam = "key=" + api_key
                +",timestamp=" + timestamp
                +",nonce=" + nonce
                +",signature=" + EncryptedString;
        System.out.println(HeaderParam);
        return HeaderParam;
    }
 
    public static void main(String[] args) throws ClientProtocolException, IOException, SignatureException{
        String s = genHeaderParam(id, secret);
        System.out.println(s);
    }
}

最後に書きます

これは兄弟のブログ記事を参考にまとめたものです。非常に優れており、シンプルで実用的です。インターフェイスの署名検証に関しては、呼び出し元のいくつかのパラメーターを検証して署名を生成し、2 つの署名を比較します。署名が一致しているかどうかだけで十分ですが、送信されたタイムスタンプ (有効期限は 10 分間に設定可能) を確認するのが最善です。
さて、今日の共有はこれで終わりです、兄弟姉妹、幸せな520、仕事と乾物に焦点を当てたアカウント「Anqianmahou」に引き続き注目してください。

おすすめ

転載: blog.csdn.net/weixin_42329623/article/details/130782710