目次
- 加解密の基礎
- 暗号化方式の分類
-
- 対称暗号技術
- 非対称暗号技術(RSAアルゴリズム)(デジタル証明書)
- ```
* シナリオ1:公開鍵で暗号化、秘密鍵で復号
- シナリオ2:秘密鍵で署名:デジタル署名、公開鍵で検証
- ハッシュアルゴリズム(MD5)
-
* APIテストツール
- Postman
- Jmeter
- API認証署名の原理
- ```
* 認証署名とは何か
- なぜ認証署名が必要か
- 認証署名の実装方法
-
- パラメータをキーでASCII順にソート
- パラメータ名と値を連結
- APIキーとシークレットを文字列の先頭に連結
- タイムスタンプを文字列の末尾に追加して有効期限検証を実装
- リプレイ攻撃を防ぐためのランダム値nonceの追加
- 完成した文字列をMD5でハッシュ化
- 認証署名の実装例
- ```
* 例1
加解密の基礎
暗号化:ネットワーク上で送信される生データ(平文)が暗号化アルゴリズムによって暗号化され(暗号文)送信され、盗難を防ぐ 復号:暗号文を元のデータに復元する
暗号化方式の分類
対称暗号:暗号化と復号に同じ鍵を使用 非対称暗号:非対称暗号には2つの鍵(公開鍵と秘密鍵)が必要で、それぞれが互いに暗号化と復号が可能です。公開鍵は公開されますが、秘密鍵は秘匿されます。
対称暗号技術
- DES暗号化アルゴリズム
- AES暗号化アルゴリズム
- Base64アルゴリズム
https://www.bejson.com/enc/aesdes/ で体験できます DESとAESは毎回暗号化すると暗号文が異なりますが、Base64は暗号化後の暗号文が固定されます
非対称暗号技術(RSAアルゴリズム)(デジタル証明書)
https://www.bejson.com/enc/rsa/ で公開鍵と秘密鍵をオンライン生成し、公開鍵での暗号化と秘密鍵での復号をテストできます
シナリオ1:公開鍵で暗号化、秘密鍵で復号
2人のユーザーAとBがおり、Bが鍵ペアを持っています。Aはデータを暗号化してBに送信したい
シナリオ2:秘密鍵で署名:デジタル署名、公開鍵で検証:署名検証
デジタル証明書の由来:公開鍵は公開されているため安全ではないため、第三者のCA(認証局)が公開鍵を暗号化し、暗号化されたものがデジタル証明書です。 デジタル証明書には、Bユーザーの基本情報とBの公開鍵情報が含まれます。X509標準
CA:鍵ペアを持ち、秘密鍵で暗号化
FiddlerはHTTPSプロトコルのデータパケットを直接キャプチャできず、デジタル証明書のインストールが必要です https = http + ssl安全転送プロトコル
ssl安全転送プロトコル:安全ソケットレイヤー、NetScapeが開発
ハッシュアルゴリズム(MD5)(復号を全く考慮せず、ハッシュアルゴリズムとも呼ばれる)
Postman
Jmeter
${__digest(MD5,admin,)}
API認証署名の原理
認証署名とは何か
認証署名:ユーザー名、パスワード、タイムスタンプ、およびソートされたすべてのパラメータを組み合わせ、暗号化して生成される文字列です。この文字列がAPIにアクセスするための一意の認証コードです ユーザー名:APIキー パスワード:APIシークレット
なぜ認証署名が必要か
- 偽装攻撃の防止
- 改ざん攻撃の防止
- リプレイ攻撃の防止
- 情報漏洩の防止
アクセス者の正当性を保証し、パラメータが変更されていないことを確認し、リクエストの一意性を確保します
認証署名の実装方法
パラメータをキーでASCII順にソート
例えばパラメータが?c=1&b=2&a=3の場合 ソート後はa、b、cになります
パラメータ名と値を連結した文字列を作成
a=1&b=2&c=3
APIキーとシークレットを文字列の先頭に連結
api_key=admin&api_secret=123&a=1&b=2&c=3
タイムスタンプを文字列の末尾に追加して有効期限検証を実装
例えば1分以内に有効
api_key=admin&api_secret=123&a=1&b=2&c=3×tamp=1666757432136
リプレイ攻撃を防ぐためのランダム値nonceを追加
nonceはランダム数で、UUIDが望ましい
api_key=admin&api_secret=123&a=1&b=2&c=3×tamp=1666757432136&nonce=123123
完成した文字列をMD5でハッシュ化
String str = "api_key=admin&api_secret=123&a=1&b=2&c=3×tamp=1666757432136&nonce=123123";
String signature = md5(str);
認証署名の実装例
例1
署名アルゴリズム 署名アルゴリズムの説明: 1.リクエストパラメータをパラメータ名で昇順ソートします; 2.リクエストパラメータ名とパラメータ値を相互に連結して文字列を作成します; 3.アプリケーションシークレットをリクエストパラメータ文字列の先頭と末尾に追加します; 4.この文字列をMD5(すべて大文字)でハッシュ化し、その文字列がリクエストパラメータに対応する署名です; 5.この署名値はsignパラメータと共に、他のリクエストパラメータと一緒にサービス提供プラットフォームに送信されます。
疑似コード:
Map<String,Object> requestParams = new ...; // パラメータ
Set<String> keys = requestParams.keySet();
List<String> paramNames = new ArrayList<String>(keys);
// 1.
Collections.sort(paramNames);
StringBuilder paramBuilder = new StringBuilder();
// 2.
for (String paramName : paramNames) {
paramBuilder.append(paramName).append(requestParams.get(paramName));
}
// 3.
String source = secret + paramBuilder.toString() + secret;
// 4.
String signature = md5(source);
// 5.
requestParams.put("signature",signature);
コード例
import java.io.IOException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import com.alibaba.fastjson.JSON;
import junit.framework.TestCase;
public class ApiSecurityTest extends TestCase {
@Test
public void testApiRequest() throws IOException {
String apiKey = "xxx";
String apiSecret = "xxx";
// ビジネスパラメータ
Map businessParams = new HashMap();
businessParams.put("dicCode", "terminalType");
String jsonParams = JSON.toJSONString(businessParams);
jsonParams = URLEncoder.encode(jsonParams, "utf-8");
// システムパラメータ
Map systemParams = new HashMap();
systemParams.put("methodName", "dictionaryItem.list");
systemParams.put("apiVersion", "1.0");
systemParams.put("apiKey", apiKey);
systemParams.put("requestData", jsonParams);
systemParams.put("timestamp", getCurrentTime());
systemParams.put("responseFormat", "json");
String signature = createSignature(systemParams, apiSecret);
systemParams.put("signature", signature);
/*
// 最終リクエストデータ
{
"methodName":"dictionaryItem.list",
"apiVersion":"1.0",
"apiKey":"test",
"requestData":"%7B%22dicCode%22%3A%22terminalType%22%7D",
"timestamp":"2021-12-15 10:25:02",
"responseFormat":"json",
"signature":"4B291FFFFDD6F0E3FB9708AC0F7AC334"
}
*/
System.out.println("=====リクエストデータ=====");
String requestJson = JSON.toJSONString(systemParams);
System.out.println(requestJson);
// contentType:application/json
// requestJsonをリクエストボディに設定
// リクエスト送信
String response = HttpRequest.post("https://xxx.net/api").body(requestJson).execute().body();
System.out.println(response);
/*
レスポンス結果:
{
"code":"0",
"data":[
{"dicCode":"terminalType","isplay":1,"itemId":120,"itemName":"音柱/音箱","itemValue":"1","sort":1},
{"dicCode":"terminalType","isplay":1,"itemId":121,"itemName":"AIO报警箱","itemValue":"2","sort":2},
{"dicCode":"terminalType","isplay":1,"itemId":122,"itemName":"融媒体客服主机","itemValue":"3","sort":3},
{"dicCode":"terminalType","isplay":1,"itemId":123,"itemName":"网络调音台","itemValue":"4","sort":4},
{"dicCode":"terminalType","isplay":1,"itemId":124,"itemName":"云广播适配器","itemValue":"5","sort":5},
{"dicCode":"terminalType","isplay":1,"itemId":125,"itemName":"音频编码器","itemValue":"6","sort":6},
{"dicCode":"terminalType","isplay":1,"itemId":126,"itemName":"村级播控主机","itemValue":"7","sort":7},
{"dicCode":"terminalType","isplay":1,"itemId":127,"itemName":"云话筒","itemValue":"8"},
{"dicCode":"terminalType","isplay":1,"itemId":128,"itemName":"安卓手机客户端","itemValue":"9"},
{"dicCode":"terminalType","isplay":1,"itemId":129,"itemName":"收扩机","itemValue":"10","sort":8}
]
}
*/
}
/**
* 署名の作成
*
* @param params
* パラメータマップ
* @param secret
* シークレットキー
* @return
* @throws IOException
*/
public static String createSignature(Map<String, ?> params, String secret) throws IOException {
Set<String> keySet = params.keySet();
List<String> sortedKeys = new ArrayList<String>(keySet);
Collections.sort(sortedKeys);
StringBuilder paramBuilder = new StringBuilder();
for (String key : sortedKeys) {
paramBuilder.append(key).append(params.get(key));
}
String source = secret + paramBuilder.toString() + secret;
return generateMD5(source);
}
/**
* MD5を生成(すべて大文字)
*
* @param input
* @return
*/
public static String generateMD5(String input) {
try {
// 1 メッセージダイジェストアルゴリズムオブジェクトを作成し、MD5アルゴリズムオブジェクトとして初期化
MessageDigest md = MessageDigest.getInstance("MD5");
// 2 メッセージをバイト配列に変換
byte[] messageBytes = input.getBytes();
// 3 計算してバイト配列を取得(128ビット)
byte[] digestBytes = md.digest(messageBytes);
// 4 配列の各バイト(1バイトは8ビット)を16進数に変換してMD5文字列に連結
return bytesToHex(digestBytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* バイト配列を16進数文字列に変換
*
* @param bytes
* @return
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
hexString.append("0");
}
hexString.append(hex.toUpperCase());
}
return hexString.toString();
}
/**
* 16進数文字列をバイト配列に変換
*
* @param hexString
* @return
*/
private static byte[] hexToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] data = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
data[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return data;
}
/**
* バイト配列からオーディオファイルを生成
*
* @param audioData バイト配列
* @param filePath ファイルパス
* @return
*/
public static String createAudioFile(byte[] audioData, String filePath) throws IOException {
// 方法1:直接ファイルを生成
String fileUrl = filePath + ".mp3";
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream outputStream = new FileOutputStream(file);
outputStream.write(audioData);
// 方法2:バイト配列をクラウドストレージにアップロードしてファイルを生成
// String fileUrl = "http://" + OSSFactory.build().uploadSuffix(audioData, ".mp3");
return fileUrl;
}
public String getCurrentTime() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
}
セキュリティエンジニア(ホワイトハット)向け企業レベル学習ロードマップ
第一段階:セキュリティ基礎(入門)
第二段階:Webペネトレーションテスト(ジュニアセキュリティエンジニア)
第三段階:中級部分(中級ネットワークセキュリティエンジニア)
ネットワークセキュリティ入門に興味がある場合、必要であればここをクリックしてください:ネットワークセキュリティ特別特典:入門&中級向け282G学習リソースパックを無料で共有!