首页 > 其他分享 >AWS V4加密

AWS V4加密

时间:2024-09-19 10:24:01浏览次数:8  
标签:return String AWS private awsHeaders V4 key put 加密

1.加密工具类

package com.ota.V7;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * AWS V4 签名处理工具
 *
 * 参考链接:https://docs.aws.amazon.com/zh_cn/general/latest/gr/sigv4_signing.html
 */
public class AWSV4Auth {

    private AWSV4Auth() {
    }

    public static class Builder {

        private String accessKeyID;
        private String secretAccessKey;
        private String regionName;
        private String serviceName;
        private String httpMethodName;
        private String canonicalURI;
        private TreeMap<String, String> queryParametes;
        private TreeMap<String, String> awsHeaders;
        private String payload;
        private boolean debug = false;

        public Builder(String accessKeyID, String secretAccessKey) {
            this.accessKeyID = accessKeyID;
            this.secretAccessKey = secretAccessKey;
        }

        public Builder regionName(String regionName) {
            this.regionName = regionName;
            return this;
        }

        public Builder serviceName(String serviceName) {
            this.serviceName = serviceName;
            return this;
        }

        public Builder httpMethodName(String httpMethodName) {
            this.httpMethodName = httpMethodName;
            return this;
        }

        public Builder canonicalURI(String canonicalURI) {
            this.canonicalURI = canonicalURI;
            return this;
        }

        public Builder queryParametes(TreeMap<String, String> queryParametes) {
            this.queryParametes = queryParametes;
            return this;
        }

        public Builder awsHeaders(TreeMap<String, String> awsHeaders) {
            this.awsHeaders = awsHeaders;
            return this;
        }

        public Builder payload(String payload) {
            this.payload = payload;
            return this;
        }


        public Builder debug() {
            this.debug = true;
            return this;
        }

        public AWSV4Auth build() {
            return new AWSV4Auth(this);
        }
    }

    private String accessKeyID;
    private String secretAccessKey;
    private String regionName;
    private String serviceName;
    private String httpMethodName;
    private String canonicalURI;
    private TreeMap<String, String> queryParametes;
    private TreeMap<String, String> awsHeaders;
    private String payload;
    private boolean debug = false;

    /* Other variables */
    private final String HMACAlgorithm = "AWS4-HMAC-SHA256";
    private final String aws4Request = "aws4_request";
    private String strSignedHeader;
    private String xAmzDate;
    private String currentDate;

    private AWSV4Auth(Builder builder) {
        accessKeyID = builder.accessKeyID;
        secretAccessKey = builder.secretAccessKey;
        regionName = builder.regionName;
        serviceName = builder.serviceName;
        httpMethodName = builder.httpMethodName;
        canonicalURI = builder.canonicalURI;
        queryParametes = builder.queryParametes;
        awsHeaders = builder.awsHeaders;
        payload = builder.payload;
        debug = builder.debug;

        /* Get current timestamp value.(UTC) */
        xAmzDate = getTimeStamp();
        currentDate = getDate();
    }

    /**
     * 任务 1:针对签名版本 4 创建规范请求
     *
     * @return
     */
    private String prepareCanonicalRequest() {
        StringBuilder canonicalURL = new StringBuilder("");

        /* Step 1.1 以HTTP方法(GET, PUT, POST, etc.)开头, 然后换行. */
        canonicalURL.append(httpMethodName).append("\n");

        /* Step 1.2 添加URI参数,换行. */
        canonicalURI = canonicalURI == null || canonicalURI.trim().isEmpty() ? "/" : canonicalURI;
        canonicalURL.append(canonicalURI).append("\n");

        /* Step 1.3 添加查询参数,换行. */
        StringBuilder queryString = new StringBuilder("");
        if (queryParametes != null && !queryParametes.isEmpty()) {
            for (Map.Entry<String, String> entrySet : queryParametes.entrySet()) {
                String key = entrySet.getKey();
                String value = entrySet.getValue();
                queryString.append(key).append("=").append(encodeParameter(value)).append("&");
            }

            queryString.deleteCharAt(queryString.lastIndexOf("&"));

            queryString.append("\n");
        } else {
            queryString.append("\n");
        }
        canonicalURL.append(queryString);

        /* Step 1.4 添加headers, 每个header都需要换行. */
        StringBuilder signedHeaders = new StringBuilder("");
        if (awsHeaders != null && !awsHeaders.isEmpty()) {
            for (Map.Entry<String, String> entrySet : awsHeaders.entrySet()) {
                String key = entrySet.getKey();
                String value = entrySet.getValue();
                signedHeaders.append(key).append(";");
                canonicalURL.append(key).append(":").append(value).append("\n");
            }
            canonicalURL.append("\n");
        } else {
            canonicalURL.append("\n");
        }

        /* Step 1.5 添加签名的headers并换行. */
        strSignedHeader = signedHeaders.substring(0, signedHeaders.length() - 1); // 删掉最后的 ";"
        canonicalURL.append(strSignedHeader).append("\n");

        /* Step 1.6 对HTTP或HTTPS的body进行SHA256处理. */
        payload = payload == null ? "" : payload;
        canonicalURL.append(generateHex(payload));

        if (debug) {
            System.out.println("##Canonical Request:\n" + canonicalURL.toString());
        }

        return canonicalURL.toString();
    }

    /**
     * 任务 2:创建签名版本 4 的待签字符串
     *
     * @param canonicalURL
     * @return
     */
    private String prepareStringToSign(String canonicalURL) {
        String stringToSign = "";

        /* Step 2.1 以算法名称开头,并换行. */
        stringToSign = HMACAlgorithm + "\n";

        /* Step 2.2 添加日期,并换行. */
        stringToSign += xAmzDate + "\n";

        /* Step 2.3 添加认证范围,并换行. */
        stringToSign += currentDate + "/" + regionName + "/" + serviceName + "/" + aws4Request + "\n";

        /* Step 2.4 添加任务1返回的规范URL哈希处理结果,然后换行. */
        stringToSign += generateHex(canonicalURL);

        if (debug) {
            System.out.println("##String to sign:\n" + stringToSign);
        }

        return stringToSign;
    }

    /**
     * 任务 3:为 AWS Signature 版本 4 计算签名
     *
     * @param stringToSign
     * @return
     */
    private String calculateSignature(String stringToSign) {
        try {
            /* Step 3.1 生成签名的key */
            byte[] signatureKey = getSignatureKey(secretAccessKey, currentDate, regionName, serviceName);

            /* Step 3.2 计算签名. */
            byte[] signature = HmacSHA256(signatureKey, stringToSign);

            /* Step 3.2.1 对签名编码处理 */
            String strHexSignature = bytesToHex(signature);
            return strHexSignature;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     *任务 4:将签名信息添加到请求并返回headers
     *
     * @return
     */
    public Map<String, String> getHeaders() {
        awsHeaders.put("x-amz-date", xAmzDate);

        /* 执行任务 1: 创建aws v4签名的规范请求字符串. */
        String canonicalURL = prepareCanonicalRequest();

        /* 执行任务 2: 创建用来认证的字符串 4. */
        String stringToSign = prepareStringToSign(canonicalURL);

        /* 执行任务 3: 计算签名. */
        String signature = calculateSignature(stringToSign);

        if (signature != null) {
            Map<String, String> header = new HashMap<String, String>(0);
            header.put("x-amz-date", xAmzDate);
            header.put("Authorization", buildAuthorizationString(signature));

            if (debug) {
                System.out.println("##Signature:\n" + signature);
                System.out.println("##Header:");
                for (Map.Entry<String, String> entrySet : header.entrySet()) {
                    System.out.println(entrySet.getKey() + " = " + entrySet.getValue());
                }
                System.out.println("================================");
            }
            return header;
        } else {
            if (debug) {
                System.out.println("##Signature:\n" + signature);
            }
            return null;
        }
    }

    /**
     * 连接前几步处理的字符串生成Authorization header值.
     *
     * @param strSignature
     * @return
     */
    private String buildAuthorizationString(String strSignature) {
        return HMACAlgorithm + " "
                + "Credential=" + accessKeyID + "/" + getDate() + "/" + regionName + "/" + serviceName + "/" + aws4Request + ", "
                + "SignedHeaders=" + strSignedHeader + ", "
                + "Signature=" + strSignature;
    }
    public static Map<String, String> getauth(String uri, String method, TreeMap<String, String> params, String data,String session_id,String access_id,String secret_key,String region,String service){
        TreeMap<String, String> awsHeaders = new TreeMap<String, String>();
        awsHeaders.put("cache-control", "no-cache");
//        awsHeaders.put("content-type", "application/json; charset=utf-8");
        awsHeaders.put("host", "api.volotea.com");
        awsHeaders.put("x-api-key", "9797bcb6b2f5439ba68d021328-mobile");
        awsHeaders.put("x-backend-version","6.33.1");
        //awsHeaders.put("x-app-version","3.91.1");
        awsHeaders.put("x-client-session", session_id);
        awsHeaders.put("x-client-type", "xamarin-android");
        return new Builder(access_id, secret_key)
                .regionName(region)
                .serviceName(service)
                .httpMethodName(method)
                .canonicalURI(uri)
                .queryParametes(params)
                .awsHeaders(awsHeaders)
//                .payload(data)
//                .debug()
                .build()
                .getHeaders();
    }




    public static Map<String, String> webgetauth(String uri, String method, TreeMap<String, String> params, String data,String session_id,String access_id,String secret_key,String region,String service){
        TreeMap<String, String> awsHeaders = new TreeMap<String, String>();
        awsHeaders.put("accept", "application/json");
        awsHeaders.put("host","api.volotea.com");
//        awsHeaders.put("content-type", "application/json; charset=utf-8");
        awsHeaders.put("x-api-key", "e94ab0cb5c614fc3b1ce49d89f6a-spa");

        return new Builder(access_id, secret_key)
                .regionName(region)
                .serviceName(service)
                .httpMethodName(method)
                .canonicalURI(uri)
                .queryParametes(params)
                .awsHeaders(awsHeaders)
//                .payload(data)
//                .debug()
                .build()
                .getHeaders();
    }

    public static Map<String, String> webauth(String uri, String method, TreeMap<String, String> params, String data,String session_id,String access_id,String secret_key,String region,String service){
        TreeMap<String, String> awsHeaders = new TreeMap<String, String>();
        awsHeaders.put("accept", "application/json");
        awsHeaders.put("x-api-key", "e94ab0cb5c614fc3b1ce49d89f6a-spa");
        awsHeaders.put("host","api.volotea.com");
//        awsHeaders.put("Content-Length",data.length()+"");
//        awsHeaders.put("User-Agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36");
//        awsHeaders.put("content-Type","application/json");
//        awsHeaders.put("Origin","https://book.volotea.com");
//        awsHeaders.put("Referer","https://book.volotea.com/");
//        awsHeaders.put("Accept-Encoding","gzip, deflate, br");
//        awsHeaders.put("Accept-Language","zh-CN,zh;q=0.9");



//        awsHeaders.put("x-backend-version", "5.126.19");
//        awsHeaders.put("x-client-session", session_id);
//        awsHeaders.put("x-client-type", "xamarin-iphone");
        return new Builder(access_id, secret_key)
                .regionName(region)
                .serviceName(service)
                .httpMethodName(method)
                .canonicalURI(uri)
                .queryParametes(params)
                .awsHeaders(awsHeaders)
                .payload(data)
//                .debug()
                .build()
                .getHeaders();
    }

    public static Map<String, String> auth(String uri, String method, TreeMap<String, String> params, String data,String session_id,String access_id,String secret_key,String region,String service){
        TreeMap<String, String> awsHeaders = new TreeMap<String, String>();
//        awsHeaders.put("cache-control", "no-cache");
//        awsHeaders.put("content-length", data.length()+"");
//        awsHeaders.put("content-type", "application/json");
        String amzDate = ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
        awsHeaders.put("accept", "application/json");
        awsHeaders.put("host", "api.volotea.com");
        awsHeaders.put("x-amz-date", amzDate);
        awsHeaders.put("x-api-key", "e94ab0cb5c614fc3b1ce49d89f6a-spa");
        System.out.println(amzDate);
//        awsHeaders.put("x-backend-version","6.33.1");
//        awsHeaders.put("x-app-version","3.91.1");
//        awsHeaders.put("x-client-session", session_id);

//        awsHeaders.put("x-client-type","xamarin-android");
        return new Builder(access_id, secret_key)
                .regionName(region)
                .serviceName(service)
                .httpMethodName(method)
                .canonicalURI(uri)
                .queryParametes(params)
                .awsHeaders(awsHeaders)
                .payload(data)
//                .debug()
                .build()
                .getHeaders();
    }

    /**
     * 将字符串16进制化.
     *
     * @param data
     * @return
     */
    private String generateHex(String data) {
        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(data.getBytes("UTF-8"));
            byte[] digest = messageDigest.digest();
            return String.format("%064x", new java.math.BigInteger(1, digest));
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 以给定的key应用HmacSHA256算法处理数据.
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     * @reference:
     * http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java
     */
    private byte[] HmacSHA256(byte[] key, String data) throws Exception {
        String algorithm = "HmacSHA256";
        Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(key, algorithm));
        return mac.doFinal(data.getBytes("UTF8"));
    }

    /**
     * 生成AWS 签名
     *
     * @param key
     * @param date
     * @param regionName
     * @param serviceName
     * @return
     * @throws Exception
     * @reference
     * http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java
     */
    private byte[] getSignatureKey(String key, String date, String regionName, String serviceName) throws Exception {
        byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
        byte[] kDate = HmacSHA256(kSecret, date);
        byte[] kRegion = HmacSHA256(kDate, regionName);
        byte[] kService = HmacSHA256(kRegion, serviceName);
        byte[] kSigning = HmacSHA256(kService, aws4Request);
        return kSigning;
    }

    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();

    /**
     * 将字节数组转换为16进制字符串
     *
     * @param bytes
     * @return
     */
    private String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars).toLowerCase();
    }

    /**
     * 获取yyyyMMdd'T'HHmmss'Z'格式的当前时间
     *
     * @return
     */
    public static String getTimeStamp() {
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));//server timezone
        return dateFormat.format(new Date());
    }

    /**
     * 获取yyyyMMdd格式的当前日期
     *
     * @return
     */
    public static String getDate() {
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));//server timezone
        return dateFormat.format(new Date());
    }

    /**
     * UTF-8编码
     * @param param
     * @return
     */
    private String encodeParameter(String param){
        try {
            return URLEncoder.encode(param, "UTF-8");
        } catch (Exception e) {
            return URLEncoder.encode(param);
        }
    }
}

2.使用

这里我们使用一个post请求来做示例,首先分析Authorization的结构,

AWS4-HMAC-SHA256 Credential=AKIATLNGK7P7OIQKOYM4/20240919/eu-west-1/execute-api/aws4_request, SignedHeaders=accept;host;x-amz-date;x-api-key, Signature=b4ecf8deb72cbf54acabfd072f4e614adb3469e253d8ca7200c129b70372e072
  • AKIATLNGK7P7OIQKOYM4 代表access_id
  • eu-west-1 代表区域
  • execute-api 代表service
  • SignedHeaders 代表签名请求头
  • Signature 是通过 请求路径、方法、请求头、请求体、密钥等参数计算出来的

SignedHeaders的顺序要保持一致,如下图,我对accept;``host;``x-amz-date;``x-api-key进行签名

image

测试

    String entity="[{\"passengerKey\":\"MCFBRFQ-\",\"name\":{\"first\":\"SHUAIYIN\",\"last\":\"LAI\",\"title\":\"MR\"},\"passengerType\":\"ADT\",\"addAsFriend\":false,\"contactDetails\":{\"email\":\"[email protected]\",\"phone\":{\"prefix\":\"+44\",\"nationalNumber\":\"13525659648\"}},\"hasPRM\":false}]";
        Map<String, String> auth = AWSV4Auth.auth("/api/spa/voe/v1/passengers", "POST", null, entity, "19202f7517c.4d619202f7517c.315-19202f7517b.27d19202f7517e.112", "AKIATLNGK7P7OIQKOYM4", "Aen88bM77IeAx2Llv/vsb+qdt8fm6eSm5aAv4SUF", "eu-west-1", "execute-api");
        System.out.println(auth.get("Authorization"));

标签:return,String,AWS,private,awsHeaders,V4,key,put,加密
From: https://www.cnblogs.com/wlstudy09/p/18420075

相关文章

  • 当前标识(IIS APPPOOL\.NET v4.5)没有对“C:\Windows\Microsoft.NET\Framework64
    当前标识(IISAPPPOOL\.NETv4.5)没有对“C:\Windows\Microsoft.NET\Framework64\v4.0.30319\TemporaryASP.NETFiles”的写访问权限。初学者在使用ISS创建网站时是不是也遇到过类似的问题,这可能是执行当前Web请求期间生成了未经处理的异常,主要就是设置对TemporaryASP.NE......
  • 【加密算法基础——AES CBC模式代码解密实践】
    AES解密实践之代码实现AES解密使用python脚本比较灵活,但是一定要保证脚本是调试过的,才能在找到正确的密文,密钥,初始向量的情况下,解出正确的明文。但是对于AES解密,命令行无法处理key截断的问题。实际测试了一下,CBC模式,对于key截断的问题可以解决,但是CFB模式,目前还无法实验......
  • PlayReady DRM 是微软开发的一种数字版权管理技术,旨在保护和管理数字内容的版权。它提
    PlayReadyDRM是由微软公司(Microsoft)开发的数字版权管理(DRM)技术。其起源可以追溯到2000年代初期,微软意识到需要一种强大的解决方案来保护其数字内容的版权,尤其是在不断增长的在线媒体和流媒体服务领域。发展背景:初期DRM技术:在PlayReady出现之前,微软已经推出了其他D......
  • 对称加密和非对称加密的区别
    原文:TechCPP/problems/对称加密和非对称加密的区别都有那些?.md参考:密码学笔记密钥对称加密/单钥加密(privatekeycryptography):使用同一个密钥进行加密和解密。这意味着加密方和解密方必须事先共享同一个密钥,并且保证这个密钥的安全。非对称加密/双钥加密(publickeycryptog......
  • C#生态园:数据安全从我做起——C#加密库应用指南
    C#生态园:数据安全从我做起——C#加密库应用指南在当今数字化时代,数据安全已经成为企业和个人不可忽视的重要议题。随着数据量的不断增长和数据价值的不断提升,保护数据免受未经授权的访问和恶意变得尤为重要。C#作为一种广泛使用的编程语言,提供了丰富的加密库和工具,帮助开发者构建安......
  • 【加密社】Solidity 中的事件机制及其应用
    加密社引言在Solidity合约开发过程中,事件(Events)是一种非常重要的机制。它们不仅能够让开发者记录智能合约的重要状态变更,还能够让外部系统(如前端应用)监听这些状态的变化。本文将详细介绍Solidity中的事件机制以及如何利用不同的手段来触发、监听和获取这些事件。事件......
  • php AES/ECB/PKCS7Padding 加密
    在PHP中,直接使用 openssl_encrypt 函数时,它并不直接支持PKCS7Padding,特别是当使用ECB模式时,因为ECB模式本身并不关心数据填充(每个块都是独立加密的,且块大小固定为AES的块大小,即128位或16字节)。然而,由于AES的块大小与PKCS#7填充的最小单位(1字节)相同,在ECB模式......
  • QUIC握手加密过程详解
    一、术语解释1.公钥:公钥主要用于加密数据。数据一旦用公钥加密,只有对应的私钥才能解密。公钥还用于验证使用相应私钥生成的数字签名,确保数据的完整性和来源的真实性。公钥是可以公开分享的密钥,任何人都可以使用它。2.私钥:私钥用于解密用公钥加密的数据。私钥用于生成数字......
  • HTTPS的加密流程:保护你的数据传输
    目录1.什么是HTTPS?2.HTTPS的加密流程2.1 客户端发起HTTPS请求2.2 服务器响应并发送证书2.3 客户端验证证书2.4 客户端生成加密密钥2.5 客户端使用服务器的公钥加密密钥2.6 服务器解密密钥2.7 建立加密通道2.8 数据传输2.9 会话结束3.总结在当今这个......
  • python+flask计算机毕业设计基于数据加密的高校奖学金评定系统的设计与实现(程序+开题+
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着高校规模的不断扩大和学生数量的激增,奖学金评定工作逐渐成为一项复杂而繁重的任务。传统的奖学金评定方式往往依赖于人工收集、整理和......