The content of this page has been automatically translated by AI. If you encounter any problems while reading, you can view the corresponding content in Chinese.

Server-Side Signature Practice

Last updated: 2026-06-23 15:13:12

This document describes how to set up a secure temporary signature service.

Solution strengths

Permission security: It can effectively restrict the permission scope to a secure range and is only used for uploading a specified file path.
Path security: The random COS file path is determined by the server, which can effectively avoid the problem of existing files being overwritten and security risks.
Transmission security: Signatures are generated on the server side to avoid the risk of temporary key leakage during transmission.

Upload Process

1. The client selects a file and sends the original file name to the server.
2. Based on the file name suffix, the server generates a random COS file path with a timestamp, applies for signature information and a COS key with corresponding permissions, and returns them to the frontend.

Building a Temporary Signature Service

A temporary key (temporary access credential) is a permission-restricted key obtained through the interface provided by the CAM TencentCloud API. When you need to initiate a COS API request, you must use the three fields returned by the temporary key obtaining interface: TmpSecretId, TmpSecretKey, and Token, to calculate the signature.
When a signature is requested from the server, the server first obtains a temporary key, then uses it to generate the signature, and finally returns the signature to the client.
The following are sample codes for various languages:
Node.js
Go
PHP
Python
Java
.NET(C#)
For the complete code, see Sample Code. For importing the STS package, see Node.js STS Package Import.
// Example of a temporary key service
var express = require('express');
var crypto = require('crypto');
var STS = require('qcloud-cos-sts');

// Configuration parameters
var config = {
// Obtain Tencent Cloud keys. It is recommended to use keys of sub-users with limited permissions. https://console.cloud.tencent.com/cam/capi
secretId: process.env.COS_SECRET_ID,
secretKey: process.env.COS_SECRET_KEY,
// Key validity period
durationSeconds: 1800,
// Enter the bucket and region here, for example: test-1250000000, ap-guangzhou
bucket: process.env.PERSIST_BUCKET,
region: process.env.PERSIST_BUCKET_REGION
};

// Obtain a temporary key
var getTempCredential = async function(cosKey){
var shortBucketName = config.bucket.substr(0 , config.bucket.lastIndexOf('-'));
var appId = config.bucket.substr(1 + config.bucket.lastIndexOf('-'));
// Start obtaining a temporary key
var policy = {
"version": "2.0",
"statement": [
{
"action": [
// Operations required for upload
"name/cos:PutObject",
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload"
],
"effect": "allow",
"resource": [
// Only for cosKey resources
'qcs::cos:' + config.region + ':uid/' + appId + ':prefix//' + appId + '/' + shortBucketName + '/' + cosKey,
]
}
]
};
let tempKeys = null;
try{
tempKeys = await STS.getCredential({
secretId: config.secretId,
secretKey: config.secretKey,
durationSeconds: config.durationSeconds,
policy: policy,
});
console.log(tempKeys);
return tempKeys;
} catch(err){
console.log(err);
return null;
}
};

// Calculate the signature.
var getSignature = function(tempCredential, httpMethod, cosHost, pathname) {
const signAlgorithm = 'sha1';
const credentials = tempCredential.credentials;
const keyTime = `${tempCredential.startTime};${tempCredential.expiredTime}`;

// Step 1: Generate a SignKey
var signKey = crypto.createHmac(signAlgorithm, credentials.tmpSecretKey).update(keyTime).digest('hex');
console.log("signKey:"+signKey);

// Step 2: Generate a StringToSign
const httpString = `${httpMethod.toLowerCase()}\n/${pathname}\n\nhost=${cosHost}\n`;
console.log("httpString:"+httpString);
const httpStringHash = crypto.createHash(signAlgorithm).update(httpString).digest('hex');
const stringToSign = `${signAlgorithm}\n${keyTime}\n${httpStringHash}\n`;
console.log("stringToSign:"+stringToSign);

// Step 3: Generate a Signature
var signature = crypto.createHmac(signAlgorithm, signKey).update(stringToSign).digest('hex');
console.log("signature:"+signature);

// Step 4: Generate an authorization
let authorization = `q-sign-algorithm=${signAlgorithm}&
q-ak=${credentials.tmpSecretId}&
q-sign-time=${keyTime}&
q-key-time=${keyTime}&
q-header-list=host&q-url-param-list=&q-signature=${signature}`;

// Remove the \n caused by line breaks above.
authorization = authorization.replace(/\n/g, '');
console.log("authorization:"+authorization);
return authorization;
}

// Create a temporary key service and a static service for debugging.
var app = express();
// Direct upload signature API
app.all('/sts-server-sign', async function (req, res, next) {
// Obtain the fields that require signing.
var httpMethod = req.query.httpMethod;
var cosHost = req.query.host;
var cosKey = req.query.cosKey;

console.log(httpMethod + " " + cosHost + " " + cosKey);

cosHost = decodeURIComponent(cosHost);
cosKey = decodeURIComponent(cosKey);

console.log(httpMethod + " " + cosHost + " " + cosKey);

// Determine abnormal conditions.
if (!config.secretId || !config.secretKey) return res.send({ code: '-1', message: 'secretId or secretKey not ready' });
if (!config.bucket || !config.region) return res.send({ code: '-1', message: 'bucket or regions not ready' });
if (!httpMethod || !cosHost || !cosKey) return res.send({ code: '-1', message: 'httpMethod or host or coskey is not empty' });

// Start obtaining a temporary key
var tempCredential = await getTempCredential(cosKey);
if(!tempCredential){
res.send({ code: -1, message: 'get temp credentials fail' });
return;
}

// Calculate the signature using a temporary key.
let authorization = getSignature(tempCredential, httpMethod, cosHost, cosKey);

// Return the domain name, file path, signature, and credential information.
res.send({
code: 0,
data: {
cosHost: cosHost,
cosKey: cosKey,
authorization: authorization,
securityToken: tempCredential.credentials.sessionToken
},
});
});

app.all('*', function (req, res, next) {
res.send({ code: -1, message: '404 Not Found' });
});

// Start the signature service.
app.listen(3000);
console.log('app is listening at http://127.0.0.1:3000');
For the complete code, see Sample Code. For importing the STS package, see Go STS Package Import.
package main

import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/tencentyun/cos-go-sdk-v5"
sts "github.com/tencentyun/qcloud-cos-sts-sdk/go"
"math/rand"
"net/http"
"net/url"
"os"
"reflect"
"strings"
"time"
"unicode"
)

type Config struct {
filename string
appId string
SecretId string
SecretKey string
Proxy string
DurationSeconds int
Bucket string
Region string
AllowActions []string
}

type Permission struct {
LimitExt bool `json:"limitExt"`
ExtWhiteList []string `json:"extWhiteList"`
LimitContentType bool `json:"limitContentType"`
LimitContentLength bool `json:"limitContentLength"`
}

func generateCosKey(ext string) string {
date := time.Now()
ymd := fmt.Sprintf("%d%02d%d", date.Year(), int(date.Month()), date.Day())
r := fmt.Sprintf("%06d", rand.Intn(1000000))
cosKey := fmt.Sprintf("file/%s/%s_%s.%s", ymd, ymd, r, ext)
return cosKey
}

func getPermission() Permission {
permission := Permission{
LimitExt: true,
ExtWhiteList: []string{"jpg", "jpeg", "png", "gif", "bmp"},
LimitContentType: false,
LimitContentLength: false,
}
return permission
}

func getConfig() Config {
config := Config{
filename: "test.jpg",
appId: "1250000000",
SecretId: os.Getenv("SECRETID"), // The user's SecretId. It is recommended to use sub-account keys, follow the principle of least privilege for authorization, and reduce usage risks. For obtaining sub-account keys, see https://cloud.tencent.com/document/product/598/37140.
SecretKey: os.Getenv("SECRETKEY"), // The user's SecretKey. It is recommended to use sub-account keys, follow the principle of least privilege for authorization, and reduce usage risks. For obtaining sub-account keys, see https://cloud.tencent.com/document/product/598/37140.
Proxy: os.Getenv("Proxy"),
DurationSeconds: 1800,
Bucket: "test-1250000000",
Region: "ap-guangzhou",
AllowActions: []string{
"name/cos:PutObject",
},
}
return config
}

func stringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}

func StructToCamelMap(input interface{}) map[string]interface{} {
v := reflect.ValueOf(input)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}

result := make(map[string]interface{})
typ := v.Type()

for i := 0; i < v.NumField(); i++ {
field := typ.Field(i)
fieldValue := v.Field(i)

// Convert the field names to camelCase.
key := toLowerCamel(field.Name)

// Process nested structs.
if fieldValue.Kind() == reflect.Struct ||
(fieldValue.Kind() == reflect.Ptr && fieldValue.Elem().Kind() == reflect.Struct) {

if fieldValue.IsNil() && fieldValue.Kind() == reflect.Ptr {
result[key] = nil
continue
}

result[key] = StructToCamelMap(fieldValue.Interface())
} else {
// Process primitive types.
result[key] = fieldValue.Interface()
}
}

return result
}

// Convert to camelCase format (first letter lowercase).
func toLowerCamel(s string) string {
if s == "" {
return s
}

// Process uppercase words (e.g., ID).
if strings.ToUpper(s) == s {
return strings.ToLower(s)
}

// Regular camelCase conversion.
runes := []rune(s)
runes[0] = unicode.ToLower(runes[0])
return string(runes)
}

func getStsCredential() (map[string]interface{}, error) {
config := getConfig()

permission := getPermission()

c := sts.NewClient(
// Obtain the key through environment variables. The os.Getenv method retrieves environment variables.
config.SecretId, //os.Getenv("SECRETID"), // The user's SecretId. It is recommended to use sub-account keys, follow the principle of least privilege for authorization, and reduce usage risks. For obtaining sub-account keys, see https://cloud.tencent.com/document/product/598/37140.
config.SecretKey, //os.Getenv("SECRETKEY"), // The user's SecretKey. It is recommended to use sub-account keys, follow the principle of least privilege for authorization, and reduce usage risks. For obtaining sub-account keys, see https://cloud.tencent.com/document/product/598/37140.
nil,
// sts.Host("sts.internal.tencentcloudapi.com"), // Sets the domain name. The default domain name is sts.tencentcloudapi.com.
// sts.Scheme("http"), // Sets the protocol. The default is https. Obtaining temporary sts keys on the public cloud does not allow http. Set http only for special scenarios.
)

condition := make(map[string]map[string]interface{})

segments := strings.Split(config.filename, ".")
if len(segments) == 0 {
//ext := ""
}
ext := segments[len(segments)-1]

if permission.LimitExt {
extInvalid := ext == "" || !stringInSlice(ext, permission.ExtWhiteList)
if extInvalid {
return nil, errors.New("Illegal file. Upload is prohibited.")
}
}

if permission.LimitContentType {
condition["string_like_if_exist"] = map[string]interface{}{
// Only uploads with a content-type of image are allowed.
"cos:content-type": "image/*",
}
}

// 3. Limit the upload file size.
if permission.LimitContentLength {
condition["numeric_less_than_equal"] = map[string]interface{}{
// The upload size limit cannot exceed 5 MB (applies only to simple uploads).
"cos:content-length": 5 * 1024 * 1024,
}
}

key := generateCosKey(ext)
// Policy Overview https://cloud.tencent.com/document/product/436/18023
opt := &sts.CredentialOptions{
DurationSeconds: int64(config.DurationSeconds),
Region: config.Region,
Policy: &sts.CredentialPolicy{
Version: "2.0",
Statement: []sts.CredentialPolicyStatement{
{
// List of key permissions. Simple uploads and multipart uploads require the following permissions. For other permission lists, see https://cloud.tencent.com/document/product/436/31923.
Action: config.AllowActions,
Effect: "allow",
Resource: []string{
// Change this to the allowed path prefix. You can determine the specific upload path based on your website's user login status. Examples: a.jpg, a/*, or * (Using the wildcard * poses significant security risks. Evaluate its use carefully.)
// The bucket naming format is BucketName-APPID. The bucket entered here must follow this format.
"qcs::cos:ap-guangzhou:uid/" + config.appId + ":" + config.Bucket + "/" + key,
},
// Start building the effective condition.
// For detailed configuration rules for conditions and the condition types supported by COS, refer to https://cloud.tencent.com/document/product/436/71306.
Condition: condition,
},
},
},
}

// case 1: Request a temporary key
res, err := c.GetCredential(opt)
if err != nil {
return nil, err
}
// Convert to a camelCase map.
resultMap := StructToCamelMap(res)
resultMap["bucket"] = config.Bucket
resultMap["region"] = config.Region
resultMap["key"] = key

return resultMap, nil
}

func main() {
result, err := getStsCredential()
if err != nil {
fmt.Printf("Failed to request temporary key: %v\n", err)
return // Determines whether to exit based on the context.
}

// Type-assert credentials as map[string]interface{}.
credentials, ok := result["credentials"].(map[string]interface{})
if !ok {
fmt.Println("Invalid credential format")
return
}
// Obtain fields with type checking.
tak, tok := credentials["tmpSecretID"].(string)
tsk, tskok := credentials["tmpSecretKey"].(string)
token, tokenok := credentials["sessionToken"].(string)
if !tok || !tskok || !tokenok {
fmt.Println("Temporary credential fields are missing or have incorrect types")
return
}
host := "https://" + result["bucket"].(string) + ".cos." + result["region"].(string) + ".myqcloud.com"
u, _ := url.Parse(host)
b := &cos.BaseURL{BucketURL: u}
c := cos.NewClient(b, &http.Client{})
name := result["key"].(string)
ctx := context.Background()
opt := &cos.PresignedURLOptions{
Query: &url.Values{},
Header: &http.Header{},
}
// The token must be placed in the header when making a request.
opt.Header.Add("x-cos-security-token", token)
signature := c.Object.GetSignature(ctx, http.MethodPut, name, tak, tsk, time.Hour, opt, true)
data := make(map[string]string)

// Add the token and sign.
data["securityToken"] = token
data["authorization"] = signature
data["cosHost"] = host
data["cosKey"] = name
// Convert to JSON.
jsonData, err := json.MarshalIndent(data, "", " ")
if err != nil {
fmt.Println("JSON encoding failed:", err)
return
}
fmt.Println(string(jsonData))
}


For the complete code, see Sample Code. For importing the STS package, see PHP STS Package Import.
<?php
require_once __DIR__ . '/vendor/autoload.php';

use QCloud\COSSTS\Sts;

// Generate the COS file path and name for upload.
function generateCosKey($ext) {
$ymd = date('Ymd');
$r = substr('000000' . rand(), -6);
$cosKey = 'file/' . $ymd. '/' . $ymd . '_' . $r;
if ($ext) {
$cosKey = $cosKey . '.' . $ext;
}
return $cosKey;
};

// Obtain a temporary key for single-file upload permission.
function getKeyAndCredentials($filename) {
// The business side must implement user login status verification, for example, by verifying the token.
// $canUpload = checkUserRole($userToken);
// if (!$canUpload) {
// return 'The current user does not have upload permission.';
// }

// You can control the type and size of uploaded files and enable this feature as needed.
$permission = array(
'limitExt' => false, // Restrict uploaded file extensions
'extWhiteList' => ['jpg', 'jpeg', 'png', 'gif', 'bmp'], // Allowed file extensions for upload
'limitContentType' => false, // Restrict the Content-Type for uploads
'limitContentLength' => false, // Restrict uploaded file size
);
$condition = array();

// The client passes in the original file name. Here, a random Key is generated based on the file extension.
$ext = pathinfo($filename, PATHINFO_EXTENSION);

// 1. Restrict uploaded file extensions
if ($permission['limitExt']) {
if ($ext === '' || array_key_exists($ext, $permission['extWhiteList'])) {
return 'Illegal file. Upload is prohibited.';
}
}

// 2. Restrict the content-type for uploads
if ($permission['limitContentType']) {
// Only uploads with a content-type of image are allowed.
$condition['string_like_if_exist'] = array('cos:content-type' => 'image/*');
}

// 3. Limit the upload file size.
if ($permission['limitContentLength']) {
// The upload size is limited to 5 MB (applies only to simple uploads).
$condition['numeric_less_than_equal'] = array('cos:content-length' => 5 * 1024 * 1024);
}

$cosKey = generateCosKey($ext);
$bucket = 'test-125000000'; // Replace with your bucket
$region = 'ap-guangzhou'; // Replace with the region where your bucket resides
$config = array(
'url' => 'https://sts.tencentcloudapi.com/', // The url must match the domain name.
'domain' => 'sts.tencentcloudapi.com', // Domain name. This is optional and defaults to sts.tencentcloudapi.com.
'proxy' => '',
'secretId' => "",//getenv('GROUP_SECRET_ID'), // Static key. If it is a plaintext key, enter it directly in the format 'xxx'. Do not place it within the getenv() function.
'secretKey' => "",//getenv('GROUP_SECRET_KEY'), // Static key. If it is a plaintext key, enter it directly in the format 'xxx'. Do not place it within the getenv() function.
'bucket' => $bucket, // Replace with your bucket
'region' => $region, // Replace with the region where your bucket resides
'durationSeconds' => 1800, // Key validity period
'allowPrefix' => array($cosKey), // Grants permissions only for the path of the current key.
// List of key permissions. Simple uploads and multipart uploads require the following permissions. For other permission lists, see https://cloud.tencent.com/document/product/436/31923.
'allowActions' => array (
// Simple upload
'name/cos:PutObject'
),
);

if (!empty($condition)) {
$config['condition'] = $condition;
}

$sts = new Sts();
$tempKeys = $sts->getTempKeys($config);
$resTemp = array_merge(
$tempKeys,
[
'startTime' => time(),
'bucket' => $bucket,
'region' => $region,
'key' => $cosKey,
]
);
return $resTemp;
}

function getSign()
{
$filename = "test.jpg";
$method = "putObject";
$result = getKeyAndCredentials($filename);
$credentials = $result["credentials"];
$sessionToken = $credentials["sessionToken"];
$tmpSecretId = $credentials["tmpSecretId"];
$tmpSecretKey = $credentials["tmpSecretKey"];
$expiredTime = $result["expiredTime"];
$startTime = $result["startTime"];
$bucket = $result["bucket"];
$region = $result["region"];
$key = $result["key"];

$cosClient = new Qcloud\Cos\Client(
array(
'region' => $region,
'scheme' => 'https', // Protocol header. The default is http.
'signHost' => true, // By default, the Header Host is signed. You can also choose not to sign the Header Host, but this may cause request failures or security vulnerabilities. If you choose not to sign the host, set this to false.
'credentials'=> array(
'secretId' => $tmpSecretId,
'secretKey' => $tmpSecretKey,
'token' => $sessionToken)));
### Upload pre-signiture by using the simple upload.
try {
$url = $cosClient->getPresignedUrl($method,array(
'Bucket' => $bucket,
'Key' => $key,
'Body' => "",
'Params'=> array('x-cos-security-token' => $sessionToken),
'Headers'=> array(),
), $expiredTime - $startTime); // Signature validity period

$parsedUrl = parse_url($url);
$host = 'https://' . $parsedUrl['host']; // Automatically includes the port (if present).
$queryString = isset($parsedUrl['query']) ? $parsedUrl['query'] : '';
$queryParts = explode('&', $queryString);
$signParts = array_filter($queryParts, function($part) {
return strpos($part, 'x-cos-security-token=') !== 0;
});
$sign = implode('&', $signParts);
$result = [
'cosHost' => $host,
'cosKey' => $key,
'authorization' => $sign,
'securityToken' => $sessionToken
];

echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

} catch (\Exception $e) {
//Request failed
echo($e);
}
}
getSign();
For the complete code, see Sample Code. For importing the STS package, see Python STS Package Import.
#!/usr/bin/env python
# coding=utf-8
import json
import os
import datetime
import random
from urllib.parse import urlencode

from qcloud_cos import CosConfig, CosS3Client

from sts.sts import Sts


if __name__ == '__main__':

# Configuration parameters
config = {
"filename":"test.jpg",
"appId": "1250000000",
"secretId": os.getenv("SecretId"),
"secretKey": os.getenv("SecretKey"),
"proxy": os.getenv("Proxy"),
"durationSeconds": 1800,
"bucket": "examplebucket-1250000000",
"region": "ap-guangzhou",
# List of permissions for key upload operations
"allowActions": [
# Simple upload
"name/cos:PutObject"
],
}

permission = {
"limitExt": True, # Restrict uploaded file extensions
"extWhiteList": ["jpg", "jpeg", "png", "gif", "bmp"], # Allowed file extensions for upload
"limitContentType": False, # Restrict the Content-Type for uploads
"limitContentLength": False, # Restrict uploaded file size
}


# Generate the COS file path and name for upload
def generate_cos_key(ext=None):
date = datetime.datetime.now()
ymd = date.strftime('%Y%m%d')
r = str(int(random.random() * 1000000)).zfill(6)
cos_key = f"file/{ymd}/{ymd}_{r}.{ext if ext else ''}"
return cos_key


segments = config['filename'].split(".")
ext = segments[-1] if segments else ""
key = generate_cos_key(ext)
resource = f"qcs::cos:{config['region']}:uid/{config['appId']}:{config['bucket']}/{key}"

condition = {}

# 1. Restrict uploaded file extensions
if permission["limitExt"]:
ext_invalid = not ext or ext not in permission["extWhiteList"]
if ext_invalid:
print('Illegal file. Upload is prohibited.')

# 2. Restrict the content-type for uploads
if permission["limitContentType"]:
condition.update({
"string_like_if_exist": {
# Only image content-types are allowed for upload.
"cos:content-type": "image/*"
}
})

# 3. Limit the upload file size.
if permission["limitContentLength"]:
condition.update({
"numeric_less_than_equal": {
# The upload size limit cannot exceed 5 MB (applies only to simple uploads).
"cos:content-length": 5 * 1024 * 1024
}
})


def get_credential_demo():
credentialOption = {
# Temporary key validity period, in seconds
'duration_seconds': config.get('durationSeconds'),
'secret_id': config.get("secretId"),
# Fixed key
'secret_key': config.get("secretKey"),
# Replace with your bucket
'bucket': config.get("bucket"),
'proxy': config.get("proxy"),
# Replace with the region where your bucket resides
'region': config.get("region"),
"policy": {
"version": '2.0',
"statement": [
{
"action": config.get("allowActions"),
"effect": "allow",
"resource": [
resource
],
"condition": condition
}
],
},
}

try:

sts = Sts(credentialOption)
response = sts.get_credential()
credential_dic = dict(response)
credential_info = credential_dic.get("credentials")
credential = {
"bucket": config.get("bucket"),
"region": config.get("region"),
"key": key,
"startTime": credential_dic.get("startTime"),
"expiredTime": credential_dic.get("expiredTime") - credential_dic.get("startTime"),
"requestId": credential_dic.get("requestId"),
"expiration": credential_dic.get("expiration"),
"credentials": {
"tmpSecretId": credential_info.get("tmpSecretId"),
"tmpSecretKey": credential_info.get("tmpSecretKey"),
"sessionToken": credential_info.get("sessionToken"),
},
}
return credential
except Exception as e:
print(e)


result = get_credential_demo()
credentials = result["credentials"]
secret_id = credentials["tmpSecretId"]
secret_key = credentials["tmpSecretKey"]
token = credentials["sessionToken"]
bucket = result["bucket"]
region = result["region"]
key = result["key"]
expired = result["expiredTime"]

config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token)
client = CosS3Client(config)
sign = client.get_auth(Method='put',Bucket=bucket, Key=key, Expired=expired, Params={
'x-cos-security-token': token
},SignHost=True)
sign = urlencode(dict([item.split('=', 1) for item in sign.split('&')]))
host = "https://" + result["bucket"] + ".cos." + result["region"] + ".myqcloud.com"
response = {
"cosHost":host,
"cosKey":key,
"authorization":sign,
"securityToken":token
}
print('get data : ' + json.dumps(response, indent=4))
To import the Java STS package, see Java STS Package Import. To import the Java COS package, see Java COS Package Import.
package com.tencent.cloud;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicSessionCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.region.Region;
import com.tencent.cloud.cos.util.Jackson;
import org.junit.Test;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

public class ServerSignTest {

public static String generateCosKey(String ext) {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String ymd = dateFormat.format(date);

Random random = new Random();
int r = random.nextInt(1000000);
String rStr = String.format("%06d", r);

String cosKey = String.format("file/%s/%s_%s.%s", ymd, ymd, rStr, ext != null ? ext : "");
return cosKey;
}

// Obtain configuration information
public TreeMap<String,Object> getConfig(){

String bucket = "0-1250000000";
String appId = "1250000000";
String filename = "test.jpg";
String region = "ap-guangzhou";
String secretId = "";
String secretKey = "";
String proxy = "";
int durationSeconds = 1800;

String[] segments = filename.split("\\.");
String ext = segments.length > 0 ? segments[segments.length - 1] : "";

// Temporary key restrictions
Boolean limitExt = false; // Restrict uploaded file extensions
List extWhiteList = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp"); // Allowed file extensions for upload
Boolean limitContentType = false; // Restrict the Content-Type for uploads
Boolean limitContentLength = false; // Restrict uploaded file size


Map<String, Object> condition = new HashMap();

// 1. Restrict uploaded file extensions
if (limitExt) {
boolean extInvalid = ext == null || !extWhiteList.contains(ext);
if (extInvalid) {
System.out.println("Illegal file. Upload is prohibited.");
return null;
}
}

// 2. Restrict the content-type for uploads
if (limitContentType) {
condition.put("string_like_if_exist", new HashMap<String, String>() {{
put("cos:content-type", "image/*");
}});
}

// 3. Limit the upload file size (applies only to simple uploads).
if (limitContentLength) {
condition.put("numeric_less_than_equal", new HashMap<String, Long>() {{
put("cos:content-length", 5L * 1024 * 1024);
}});
}
String key = generateCosKey(ext);
String resource = "qcs::cos:" + region + ":uid/" + appId + ':' + bucket + '/' + key;
List allowActions = Arrays.asList(
// Simple upload
"name/cos:PutObject"
);

// Build policy
Map<String, Object> policy = new HashMap();
policy.put("version", "2.0");
Map<String, Object> statement = new HashMap();
statement.put("action", allowActions);
statement.put("effect", "allow");
List<String> resources = Arrays.asList(
resource
);
statement.put("resource", resources);
statement.put("condition", condition);
policy.put("statement", Arrays.asList(statement));


// Build config
TreeMap <String,Object> config = new TreeMap<String, Object>();
config.put("secretId",secretId);
config.put("secretKey",secretKey);
config.put("proxy",proxy);
config.put("duration",durationSeconds);
config.put("bucket",bucket);
config.put("region",region);
config.put("key",key);
config.put("policy",Jackson.toJsonPrettyString(policy));
return config;
}

public TreeMap <String,Object> getKeyAndCredentials() {
TreeMap config = this.getConfig();
try {
Response response = CosStsClient.getCredential(config);
TreeMap <String,Object> credential = new TreeMap<String, Object>();
TreeMap <String,Object> credentials = new TreeMap<String, Object>();
credentials.put("tmpSecretId",response.credentials.tmpSecretId);
credentials.put("tmpSecretKey",response.credentials.tmpSecretKey);
credentials.put("sessionToken",response.credentials.sessionToken);
credential.put("startTime",response.startTime);
credential.put("expiredTime",response.expiredTime);
credential.put("requestId",response.requestId);
credential.put("expiration",response.expiration);
credential.put("credentials",credentials);
credential.put("bucket",config.get("bucket"));
credential.put("region",config.get("region"));
credential.put("key",config.get("key"));
return credential;
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("no valid secret !");
}
}
/**
* This basic example for requesting a temporary key is suitable for uniformly granting a set of operation permissions to a batch of object paths within a bucket.
*/
@Test
public void testGetKeyAndCredentials() {
TreeMap <String,Object> credential = this.getKeyAndCredentials();
TreeMap <String,Object> credentials = (TreeMap<String, Object>) credential.get("credentials");
try {


String tmpSecretId = (String) credentials.get("tmpSecretId");
String tmpSecretKey = (String) credentials.get("tmpSecretKey");
String sessionToken = (String) credentials.get("sessionToken");
Date expiredTime = new Date((Long) credential.get("expiredTime"));
String key = (String) credential.get("key");
String bucket = (String) credential.get("bucket");
String region = (String) credential.get("region");

COSCredentials cred = new BasicSessionCredentials(tmpSecretId, tmpSecretKey, sessionToken);
ClientConfig clientConfig = new ClientConfig();
clientConfig.setRegion(new Region(region));

COSClient cosClient = new COSClient(cred, clientConfig);


Map<String, String> headers = new HashMap<String,String>();
Map<String, String> params = new HashMap<String,String>();
params.put("x-cos-security-token",sessionToken);
URL url = cosClient.generatePresignedUrl(bucket, key, expiredTime, HttpMethodName.PUT, headers, params);
String host = "https://" + url.getHost();
String query = url.toString().split("\\?")[1];
String sign = query.split("&x-cos-security-token")[0];
TreeMap <String,Object> result = new TreeMap<String, Object>();
result.put("cosHost",host);
result.put("cosKey",key);
result.put("authorization",sign);
result.put("securityToken",sessionToken);
System.out.println(Jackson.toJsonPrettyString(result));
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("no valid sign !");
}
}
}
For the complete code, see Sample Code. To import the STS package, see .NET STS Package Import.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Mail;
using COSSTS;
using COSXML;
using COSXML.Auth;
using COSXML.Model.Tag;
using Newtonsoft.Json;
using Formatting = System.Xml.Formatting;


namespace COSSnippet
{
public class ServerSign
{
// Permanent key. Replace the relevant parameters and then execute to generate the required information.
string secretId = "";
string secretKey = "";
string bucket = "bucket-1250000000";
string appId = "1250000000";
string region = "ap-guangzhou";
string filename = "test.jpg";
string method = "put";
int time = 1800;
// Restrictions
Boolean limitExt = false; // Restrict uploaded file extensions
List<string> extWhiteList = new List<String> { "jpg", "jpeg", "png", "gif", "bmp" }; // Allowed file extensions for upload
Boolean limitContentType = false; // Restrict the Content-Type for uploads
Boolean limitContentLength = false; // Restrict uploaded file size

public string generateCosKey(string ext)
{
DateTime date = DateTime.Now;
int m = date.Month;
string ymd = $"{date.Year}{(m < 10 ? $"0{m}" : m.ToString())}{date.Day}";
Random random = new Random();
string r = random.Next(0, 1000000).ToString("D6"); // Generates a 6-digit random number, padding with leading zeros if necessary.
string cosKey = $"file/{ymd}/{ymd}_{r}.{(string.IsNullOrEmpty(ext) ? "" : ext)}";
return cosKey;
}
public Dictionary<string, object> getConfig()
{
Dictionary<string, object> config = new Dictionary<string, object>();
string[] allowActions = new string[] { // Allowed operation scope, using upload as an example here
"name/cos:PutObject"
};
string[] segments = filename.Split(".");
string ext = segments.Length > 0 ? segments[segments.Length - 1] : string.Empty;
string key = generateCosKey(ext);
string resource = $"qcs::cos:{region}:uid/{appId}:{bucket}/{key}";

var condition = new Dictionary<string, object>();
// 1. Restrict uploaded file extensions
if (limitExt)
{
var extInvalid = string.IsNullOrEmpty(ext) || !extWhiteList.Contains(ext);
if (extInvalid)
{
Console.WriteLine("Illegal file. Upload is prohibited.");
return null;
}
}

// 2. Restrict the content-type for uploaded files
if (limitContentType)
{
condition["string_like_if_exist"] = new Dictionary<string, string>
{
{ "cos:content-type", "image/*" } // Only image content-types are allowed for upload.
};
}

// 3. Limit the upload file size (applies only to simple uploads).
if (limitContentLength)
{
condition["numeric_less_than_equal"] = new Dictionary<string, long>
{
{ "cos:content-length", 5 * 1024 * 1024 } // The upload size limit cannot exceed 5 MB.
};
}

var policy = new Dictionary<string, object>
{
{ "version", "2.0" },
{ "statement", new List<Dictionary<string, object>>
{
new Dictionary<string, object>
{
{ "action", allowActions },
{ "effect", "allow" },
{ "resource", new List<string>
{
resource,
}
},
{ "condition", condition }
}
}
}
};

// Serialize to JSON and output.
string jsonPolicy = JsonConvert.SerializeObject(policy);
config.Add("bucket", bucket);
config.Add("region", region);
config.Add("durationSeconds", time);

config.Add("secretId", secretId);
config.Add("secretKey", secretKey);
config.Add("key", key);
config.Add("policy", jsonPolicy);
return config;
}
// Obtain a temporary access credential for federated identity. https://cloud.tencent.com/document/product/1312/48195
public Dictionary<string, object> GetCredential()
{
var config = getConfig();
// Obtain a temporary key
Dictionary<string, object> credential = STSClient.genCredential(config);
Dictionary<string, object> credentials = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject((object) credential["Credentials"]));
Dictionary<string, object> credentials1 = new Dictionary<string, object>();
credentials1.Add("tmpSecretId",credentials["TmpSecretId"]);
credentials1.Add("tmpSecretKey",credentials["TmpSecretKey"]);
credentials1.Add("sessionToken",credentials["Token"]);
Dictionary<string, object> dictionary1 = new Dictionary<string, object>();
dictionary1.Add("credentials",credentials1);
dictionary1.Add("startTime",credential["StartTime"]);
dictionary1.Add("requestId",credential["RequestId"]);
dictionary1.Add("expiration",credential["Expiration"]);
dictionary1.Add("expiredTime",credential["ExpiredTime"]);
dictionary1.Add("bucket",config["bucket"]);
dictionary1.Add("region",config["region"]);
dictionary1.Add("key",config["key"]);
return dictionary1;
}
static void Main(string[] args)
{
ServerSign m = new ServerSign();
Dictionary<string, object> result = m.GetCredential();
Dictionary<string, object> credentials = (Dictionary<string, object>)result["credentials"];
string tmpSecretId = (string)credentials["tmpSecretId"];
string tmpSecretKey = (string)credentials["tmpSecretKey"];
string sessionToken = (string)credentials["sessionToken"];
string bucket = (string)result["bucket"];
string region = (string)result["region"];
string key = (string)result["key"];
long expiredTime = (long)result["expiredTime"];
int startTime = (int)result["startTime"];
QCloudCredentialProvider cosCredentialProvider = new DefaultSessionQCloudCredentialProvider(
tmpSecretId, tmpSecretKey, expiredTime, sessionToken);
CosXmlConfig config = new CosXmlConfig.Builder()
.IsHttps(true) // Set the default HTTPS request.
.SetRegion(region) // Set a default bucket region.
.SetDebugLog(true) // Display log.
.Build(); // Create CosXmlConfig object.
CosXml cosXml = new CosXmlServer(config, cosCredentialProvider);
PreSignatureStruct preSignatureStruct = new PreSignatureStruct();
preSignatureStruct.appid = m.appId;//"1250000000";
preSignatureStruct.region = region;//"COS_REGION";
preSignatureStruct.bucket = bucket;//"examplebucket-1250000000";
preSignatureStruct.key = "exampleObject"; // Object key
preSignatureStruct.httpMethod = "PUT"; // HTTP request method
preSignatureStruct.isHttps = true; // Generate an HTTPS request URL.
preSignatureStruct.signDurationSecond = 600; // The request signature is valid for 600 seconds.
preSignatureStruct.headers = null; // Headers to be validated in the signature.
preSignatureStruct.queryParameters = null; // Request parameters in the URL to be validated in the signature.
// Upload a pre-signed URL (a signature URL calculated using the permanent key method).
Dictionary<string, string> queryParameters = new Dictionary<string, string>();
queryParameters.Add("x-cos-security-token",sessionToken);
Dictionary<string, string> headers = new Dictionary<string, string>();
string authorization = cosXml.GenerateSign(m.method,key,queryParameters,headers,expiredTime - startTime,expiredTime - startTime);

string host = "https://" + bucket + ".cos." + region + ".myqcloud.com";
Dictionary<string, object> response = new Dictionary<string, object>();
response.Add("cosHost",host);
response.Add("cosKey",key);
response.Add("authorization",authorization);
response.Add("securityToken",sessionToken);
Console.WriteLine($"{JsonConvert.SerializeObject(response)}");
}
}
}