HMAC-Signatur erstellen
Anstelle der tokenbasierten Berechtigung können Sie auch HMAC-Berechtigungsnachweise verwenden.
Diese Berechtigungsnachweise werden verwendet, um einen Berechtigungsheader analog zu AWS Signature Version 4 zu erstellen. Die Berechnung von Signaturen bietet Identitätsprüfung und Datenintegrität während der Übertragung. Jede Signatur ist mit der Zeitmarke der Anforderung verknüpft, sodass Berechtigungsheader nicht wiederverwendet werden können. Der Header besteht aus vier Komponenten: Algorithmusdeklaration, Berechtigungsnachweisinformationen, signierte Header und berechnete Signatur.
AWS4-HMAC-SHA256 Credential={access-key}/{date}/{region}/s3/aws4_request,SignedHeaders=host;x-amz-date;{other-required-headers},Signature={signature}
Das Datum wird im Format YYYYMMDD
angegeben und die Region entspricht dem Standort des angegebenen Buckets, zum Beispiel us
. Die Header host
und x-amz-date
sind immer erforderlich. Abhängig von
der Anforderung können auch weitere Header erforderlich sein (z. B. der Header x-amz-content-sha256
in Anforderungen mit Nutzdaten). Die Signatur für jede einzelne Anforderung muss neu berechnet werden. Aus diesem Grund bevorzugen
viele Entwickler ein Tool oder SDK, das den Berechtigungsheader automatisch erzeugt.
Header vom Typ authorization
erstellen
Zuerst muss eine Anforderung in einem Standardformat erstellt werden.
- Deklarieren Sie, welche HTTP-Methode verwendet wird (z. B.
PUT
). - Definieren Sie die Ressource, auf die in standardisierter Weise zugegriffen wird. Dies ist der Teil der Adresse zwischen
http(s)://
und der Abfragezeichenfolge. Für Anforderungen auf Kontoebene (wie zum Beispiel zur Auflistung von Buckets) ist dies einfach das Zeichen/
. - Falls Anforderungsparameter verwendet werden, müssen diese Angaben standardisiert werden, indem sie in Prozentcodierung angegeben (Leerzeichen müssen beispielsweise mit der Zeichenfolge
%20
dargestellt werden) und alphabetisch sortiert werden. - Header müssen standardisiert werden, indem enthaltene Leerzeichen entfernt werden, die alphabetischen Zeichen in Kleinbuchstaben umgesetzt werden und jeweils ein Zeilenumbruch hinzugefügt wird und anschließend eine Sortierung in ASCII-Reihenfolge durchgeführt wird.
- Nachdem sie in Standardformat aufgelistet worden sind, müssen die Header 'signiert' werden. Dabei werden nur die Headernamen und nicht ihre Werte in alphabetischer Reihenfolge aufgelistet, die durch Semikolons getrennt sind.
Host
undx-amz-date
sind für alle Anforderungen erforderlich. - Wenn die Anforderung über einen Hauptteil verfügt, wenn beispielsweise ein Objekt hochgeladen oder eine neue ACL erstellt wird, muss der Anforderungshauptteil unter Verwendung des SHA-256-Algorithmus hashverschlüsselt und als Base16-codierte Kleinbuchstaben dargestellt werden.
- Formulieren Sie durch Verbinden der HTTP-Methode, der standardisierten Ressource, der standardisierten Parameter, der standardisierten Header und des hashverschlüsselten Anforderungshauptteils jeweils unter Angabe eines Zeilenumbruch zur Trennung eine standardisierte Anforderung.
Als Nächstes müssen Sie eine zu signierende Zeichenfolge (String-to-sign) zusammenstellen, die in Verbindung mit dem Signaturschlüssel die endgültige Signatur bildet. Die zu signierende Zeichenfolge (String-to-sign) verwendet das folgende Format:
AWS4-HMAC-SHA256
{time}
{date}/{string}/s3/aws4_request
{hashed-standardized-request}
- Die Zeit muss in der aktuellen koordinierten Weltzeit (Coordinated Universal Time, UTC) angegeben werden und gemäß Spezifikation ISO 8601 formatiert sein (z. B.
20161128T152924Z
). - Das Datum ist im Format
YYYYMMDD
anzugeben. - Die letzte Zeile besteht aus der zuvor erstellten standardisierten Anforderung, die mit dem SHA-256-Algorithmus hashverschlüsselt wurde.
Jetzt müssen Sie die Signatur berechnen.
- Zuerst muss der Signaturschlüssel aus dem geheimen Zugriffsschlüssel des Kontos, dem aktuellen Datum, der Region sowie dem verwendeten API-Typ berechnet werden.
- Dem geheimen Zugriffsschlüssel wird die Zeichenfolge
AWS4
als Präfix vorangestellt und die daraus entstandene neue Zeichenfolge wird als Schüssel zum Umwandeln des Datums in einen Hashwert verwendet. - Der dabei generierte Hashwert wird als Schlüssel für die Umwandlung der Region in einen Hashwert verwendet.
- Der Prozess wird unter Verwendung des neuen Hashwerts als Schlüssel zur Umwandlung des API-Typs in einen Hashwert fortgesetzt.
- Zum Abschluss wird der neueste Hashwert als Schlüssel zur Umwandlung der Zeichenfolge
aws4_request
in einen Hashwert und damit zur Erstellung des Signaturschlüssels verwendet. - Der Signaturschlüssel wird dann als Schlüssel zur Umwandlung der zu signierenden Zeichenfolge (String-to-sign) verwendet, wodurch die endgültige Signatur generiert wird.
Als letzter noch auszuführender Schritt muss nun der Header vom Typ authorization
wie hier gezeigt assembliert werden:
AWS4-HMAC-SHA256 Credential={access-key}/{date}/{region}/s3/aws4_request,SignedHeaders=host;x-amz-date;{other-required-headers},Signature={signature}
Header vom Typ authorization
generieren
Python-Beispiel
import os
import datetime
import hashlib
import hmac
import requests
# please don't store credentials directly in code
access_key = os.environ.get('COS_HMAC_ACCESS_KEY_ID')
secret_key = os.environ.get('COS_HMAC_SECRET_ACCESS_KEY')
# request elements
http_method = 'GET'
host = 's3.us.cloud-object-storage.appdomain.cloud'
region = 'us-standard'
endpoint = 'https://s3.us.cloud-object-storage.appdomain.cloud'
bucket = '' # add a '/' before the bucket name to list buckets
object_key = ''
request_parameters = ''
# hashing and signing methods
def hash(key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
# region is a wildcard value that takes the place of the AWS region value
# as COS doen't use the same conventions for regions, this parameter can accept any string
def createSignatureKey(key, datestamp, region, service):
keyDate = hash(('AWS4' + key).encode('utf-8'), datestamp)
keyString = hash(keyDate, region)
keyService = hash(keyString, service)
keySigning = hash(keyService, 'aws4_request')
return keySigning
# assemble the standardized request
time = datetime.datetime.utcnow()
timestamp = time.strftime('%Y%m%dT%H%M%SZ')
datestamp = time.strftime('%Y%m%d')
standardized_resource = '/' + bucket + '/' + object_key
standardized_querystring = request_parameters
standardized_headers = 'host:' + host + '\n' + 'x-amz-date:' + timestamp + '\n'
signed_headers = 'host;x-amz-date'
payload_hash = hashlib.sha256(''.encode('utf-8')).hexdigest()
standardized_request = (http_method + '\n' +
standardized_resource + '\n' +
standardized_querystring + '\n' +
standardized_headers + '\n' +
signed_headers + '\n' +
payload_hash).encode('utf-8')
# assemble string-to-sign
hashing_algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + 's3' + '/' + 'aws4_request'
sts = (hashing_algorithm + '\n' +
timestamp + '\n' +
credential_scope + '\n' +
hashlib.sha256(standardized_request).hexdigest())
# generate the signature
signature_key = createSignatureKey(secret_key, datestamp, region, 's3')
signature = hmac.new(signature_key,
(sts).encode('utf-8'),
hashlib.sha256).hexdigest()
# assemble all elements into the 'authorization' header
v4auth_header = (hashing_algorithm + ' ' +
'Credential=' + access_key + '/' + credential_scope + ', ' +
'SignedHeaders=' + signed_headers + ', ' +
'Signature=' + signature)
# create and send the request
headers = {'x-amz-date': timestamp, 'Authorization': v4auth_header}
# the 'requests' package autmatically adds the required 'host' header
request_url = endpoint + standardized_resource + standardized_querystring
print('\nSending `%s` request to IBM COS -----------------------' % http_method)
print('Request URL = ' + request_url)
request = requests.get(request_url, headers=headers)
print('\nResponse from IBM COS ----------------------------------')
print('Response code: %d\n' % request.status_code)
print(request.text)
Java-Beispiel
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.format.DateTimeFormatter;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Formatter;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class CosHMAC {
// please don't store credentials directly in code
private static final String accessKey = System.getenv("COS_HMAC_ACCESS_KEY_ID");
private static final String secretKey = System.getenv("COS_HMAC_SECRET_ACCESS_KEY");
// constants
private static final String httpMethod = "GET";
private static final String host = "s3.us.cloud-object-storage.appdomain.cloud";
private static final String region = "us-standard";
private static final String endpoint = "https://s3.us.cloud-object-storage.appdomain.cloud";
private static final String bucket = ""; // add a '/' before the bucket name to list buckets
private static final String objectKey = "";
private static final String requestParameters = "";
public static void main(String[] args) {
try {
// assemble the standardized request
ZonedDateTime time = ZonedDateTime.now(ZoneOffset.UTC);
String datestamp = time.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String timestamp = datestamp + "T" + time.format(DateTimeFormatter.ofPattern("HHmmss")) + "Z";
String standardizedResource = bucket + "/" + objectKey;
String standardizedQuerystring = requestParameters;
String standardizedHeaders = "host:" + host + "\n" + "x-amz-date:" + timestamp + "\n";
String signedHeaders = "host;x-amz-date";
String payloadHash = hashHex("");
String standardizedRequest = httpMethod + "\n" +
standardizedResource + "\n" +
standardizedQuerystring + "\n" +
standardizedHeaders + "\n" +
signedHeaders + "\n" +
payloadHash;
// assemble string-to-sign
String hashingAlgorithm = "AWS4-HMAC-SHA256";
String credentialScope = datestamp + "/" + region + "/" + "s3" + "/" + "aws4_request";
String sts = hashingAlgorithm + "\n" +
timestamp + "\n" +
credentialScope + "\n" +
hashHex(standardizedRequest);
// generate the signature
byte[] signatureKey = createSignatureKey(secretKey, datestamp, region, "s3");
String signature = hmacHex(signatureKey, sts);
// assemble all elements into the "authorization" header
String v4auth_header = hashingAlgorithm + " " +
"Credential=" + accessKey + "/" + credentialScope + ", " +
"SignedHeaders=" + signedHeaders + ", " +
"Signature=" + signature;
// create and send the request
String requestUrl = endpoint + standardizedResource + standardizedQuerystring;
URL urlObj = new URL(requestUrl);
HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();
con.setRequestMethod(httpMethod);
//add request headers
con.setRequestProperty("x-amz-date", timestamp);
con.setRequestProperty("Authorization", v4auth_header);
System.out.printf("\nSending %s request to IBM COS -----------------------", httpMethod);
System.out.println("Request URL = " + requestUrl);
int responseCode = con.getResponseCode();
System.out.println("\nResponse from IBM COS ----------------------------------");
System.out.printf("Response code: %d\n\n", responseCode);
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
//print result
System.out.println(response.toString());
con.disconnect();
}
catch (Exception ex) {
System.out.printf("Error: %s\n", ex.getMessage());
}
}
private static String toHexString(byte[] bytes) {
Formatter formatter = new Formatter();
for (byte b : bytes) {
formatter.format("%02x", b);
}
return formatter.toString();
}
private static byte[] hash(byte[] key, String msg) {
byte[] returnVal = null;
try {
SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
returnVal = mac.doFinal(msg.getBytes("UTF8"));
}
catch (Exception ex) {
throw ex;
}
finally {
return returnVal;
}
}
private static String hmacHex(byte[] key, String msg) {
String returnVal = null;
try {
returnVal = toHexString(hash(key, msg));
}
catch (Exception ex) {
throw ex;
}
finally {
return returnVal;
}
}
private static String hashHex(String msg) {
String returnVal = null;
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(msg.getBytes(StandardCharsets.UTF_8));
returnVal = toHexString(encodedhash);
}
catch (Exception ex) {
throw ex;
}
finally {
return returnVal;
}
}
// region is a wildcard value that takes the place of the AWS region value
// as COS doesn"t use the same conventions for regions, this parameter can accept any string
private static byte[] createSignatureKey(String key, String datestamp, String region, String service) {
byte[] returnVal = null;
try {
byte[] keyDate = hash(("AWS4" + key).getBytes("UTF8"), datestamp);
byte[] keyString = hash(keyDate, region);
byte[] keyService = hash(keyString, service);
byte[] keySigning = hash(keyService, "aws4_request");
returnVal = keySigning;
}
catch (Exception ex) {
throw ex;
}
finally {
return returnVal;
}
}
}
NodeJS-Beispiel
const crypto = require('crypto');
const moment = require('moment');
const https = require('https');
// please don't store credentials directly in code
const accessKey = process.env.COS_HMAC_ACCESS_KEY_ID;
const secretKey = process.env.COS_HMAC_SECRET_ACCESS_KEY;
const httpMethod = 'GET';
const host = 's3.us.cloud-object-storage.appdomain.cloud';
const region = 'us-standard';
const endpoint = 'https://s3.us.cloud-object-storage.appdomain.cloud';
const bucket = ''; // add a '/' before the bucket name to list buckets
const objectKey = '';
const requestParameters = '';
// hashing and signing methods
function hash(key, msg) {
var hmac = crypto.createHmac('sha256', key);
hmac.update(msg, 'utf8');
return hmac.digest();
}
function hmacHex(key, msg) {
var hmac = crypto.createHmac('sha256', key);
hmac.update(msg, 'utf8');
return hmac.digest('hex');
}
function hashHex(msg) {
var hash = crypto.createHash('sha256');
hash.update(msg);
return hash.digest('hex');
}
// region is a wildcard value that takes the place of the AWS region value
// as COS doesn't use the same conventions for regions, this parameter can accept any string
function createSignatureKey(key, datestamp, region, service) {
keyDate = hash(('AWS4' + key), datestamp);
keyString = hash(keyDate, region);
keyService = hash(keyString, service);
keySigning = hash(keyService, 'aws4_request');
return keySigning;
}
// assemble the standardized request
var time = moment().utc();
var timestamp = time.format('YYYYMMDDTHHmmss') + 'Z';
var datestamp = time.format('YYYYMMDD');
var standardizedResource = bucket + '/' + objectKey;
var standardizedQuerystring = requestParameters;
var standardizedHeaders = 'host:' + host + '\n' + 'x-amz-date:' + timestamp + '\n';
var signedHeaders = 'host;x-amz-date';
var payloadHash = hashHex('');
var standardizedRequest = httpMethod + '\n' +
standardizedResource + '\n' +
standardizedQuerystring + '\n' +
standardizedHeaders + '\n' +
signedHeaders + '\n' +
payloadHash;
// assemble string-to-sign
var hashingAlgorithm = 'AWS4-HMAC-SHA256';
var credentialScope = datestamp + '/' + region + '/' + 's3' + '/' + 'aws4_request';
var sts = hashingAlgorithm + '\n' +
timestamp + '\n' +
credentialScope + '\n' +
hashHex(standardizedRequest);
// generate the signature
var signatureKey = createSignatureKey(secretKey, datestamp, region, 's3');
var signature = hmacHex(signatureKey, sts);
// assemble all elements into the 'authorization' header
var v4authHeader = hashingAlgorithm + ' ' +
'Credential=' + accessKey + '/' + credentialScope + ', ' +
'SignedHeaders=' + signedHeaders + ', ' +
'Signature=' + signature;
// create and send the request
var authHeaders = {'x-amz-date': timestamp, 'Authorization': v4authHeader}
// the 'requests' package autmatically adds the required 'host' header
console.log(authHeaders);
var requestUrl = endpoint + standardizedResource + standardizedQuerystring
console.log(`\nSending ${httpMethod} request to IBM COS -----------------------`);
console.log('Request URL = ' + requestUrl);
var options = {
host: host,
port: 443,
path: standardizedResource + standardizedQuerystring,
method: httpMethod,
headers: authHeaders
}
var request = https.request(options, function (response) {
console.log('\nResponse from IBM COS ----------------------------------');
console.log(`Response code: ${response.statusCode}\n`);
response.on('data', function (chunk) {
console.log(chunk.toString());
});
});
request.end();
Nächste Schritte
Sie können die Dokumentation zu Berechtigungsnachweisen als Teil eines Serviceberechtigungsnachweises lesen. Eine Übersicht über die Authentifizierung finden Sie im IBM Cloud Identity and Access Management-Service.