2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Java で Azure BLOB オブジェクト を 30秒間 だけ Read 可能な SAS トークン を ライブラリを使わないで生成する

Last updated at Posted at 2021-01-30

tl;dr

SAS トークンの構造を学習するために、ライブラリを使わないで、 Java で生成しました。
( Node.js 版は こちら )

公式ドキュメント : サービス SAS を作成する

コード

BLOB オブジェクト を 30秒 だけ Read 可能なURL

Main.java
public class Main {
    public static void main(String[] args) {
        String name = "ストレージアカウント名";
        String key = "キー";
        String container = "コンテナ名";
        String blob = "ブロブ名";
        int expiry = 30; // 30 秒間有効
        String filename = "ファイル名";

        SAS sas = new SAS(name, key);
        String url = sas.getUrl(container, blob, expiry, filename);
        System.out.println(url);
    }
}
SAS.java
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.StringJoiner;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class SAS {
    String account_name;
    String account_key;

    // コンストラクタ
    // @param name ストレージアカウント名
    // @param key ストレージアカウントキー
    public SAS(String name, String key) {
        account_name = name;
        account_key = key;
    }

    // トークン付きURLを生成する
    // @param container コンテナ名
    // @param blob ブロブ名
    // @param expiry_second トークン有効期間(秒)
    // @param filename ファイル名
    public String getUrl(String container, String blob, int expiry_second, String filename) {
        String token = getToken(container, blob, expiry_second, filename);
        String url = "https://" + account_name + ".blob.core.windows.net/" + container + "/" + blob;
        return url + "?" + token;
    }

    // トークンを生成する
    // @param container コンテナ名
    // @param blob ブロブ名
    // @param expiry_second トークン有効期間(秒)
    // @param filename ファイル名
    String getToken(String container, String blob, int expiry_second, String filename) {

        // パラメータ
        Date now = new Date();
        String start = ""; // isoDate(now, start_second);
        String expiry = isoDate(now, expiry_second);

        String version = "2018-11-09";
        String resource = "b";
        String permission = "r";
        String ip = ""; // "0.0.0.0/0";
        String protocol = ""; // "https";

        String canonicalizedResource = "/blob/" + account_name + "/" + container + "/" + blob;

        String rscc = "";
        String rscd = "attachment; filename=\"" + filename + "\"";
        String rsce = "";
        String rscl = "";
        String rsct = "";

        String identifier = "";
        String snapshot = "";

        // シグネチャ (順番は厳守)
        String[] arr = { //
                permission, //
                start, //
                expiry, //
                canonicalizedResource, //
                identifier, //
                ip, //
                protocol, //
                version, //
                resource, //
                snapshot, //
                rscc, // Cache-Control
                rscd, // Content-Disposition
                rsce, // Content-Encoding
                rscl, // Content-Language
                rsct, // Content-Type
        };
        String stringToSign = String.join("\n", arr);

        // HMAC256
        String signature = getHMAC256(stringToSign);

        // クエリパラメータ (順番は任意)
        String params[][] = { //
                { "sp", permission }, //
                { "sr", resource }, //
                // { "st", start }, //
                { "se", expiry }, //
                // { "sip", ip }, //
                // { "spr", protocol }, //
                { "sv", version }, //
                { "sig", signature }, //
                { "rscd", rscd }, //
        };
        StringJoiner sas = new StringJoiner("&");
        Arrays.asList(params).forEach(v -> sas.add(v[0] + "=" + encode(v[1])));

        return sas.toString();
    }

    String encode(String str) {
        try {
            return URLEncoder.encode(str, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new InternalError(e.toString());
        }
    }

    String isoDate(Date date, int second) {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.SECOND, second);
        return fmt.format(cal.getTime());
    }

    String getHMAC256(String input) {
        try {
            String algo = "HmacSHA256";
            // 1. キー は バイト形式 (Base64 で デコード)
            byte[] key = Base64.getDecoder().decode(account_key);
            SecretKeySpec secretKey = new SecretKeySpec(key, algo);
            Mac sha256HMAC = Mac.getInstance(algo);
            sha256HMAC.init(secretKey);
            // 2. 入力 は バイト形式 (UTF8 で デコード)
            byte[] inp = input.getBytes("UTF-8");
            // 3. 出力 は バイト形式 を Base64 で エンコード
            byte[] out = sha256HMAC.doFinal(inp);
            return Base64.getEncoder().encodeToString(out);
        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
            throw new InternalError(e.toString());
        }
    }
}
2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?