Authentifizierung
Jede Anfrage an unser REST-API muss authentifiziert werden, um unserem System Ihre Identität zu bestätigen. Der Ablauf der Authentifizierung lehnt sich eng an die Authentifizierung von REST-Anfragen in Amazon S3 an.
Der Authentication-Header
Anfragen an unser REST-API verwenden den standard HTTP-Header Authorization
um Authentifizierungsinformationen zu
transportieren. Der Authentifizierungsheader hat die folgenden Form:
Authorization: COB CobaiWebServicesAccessKeyID:Signatur
Die Access-Key-Id und Ihren geheimen Schlüssel können Sie im jeweiligen Zielsystem selbst erzeugen. Über die Access-Key-Id kann das System erkennen, wer die Anfrage signiert hat und welcher geheime Schlüssel verwendet wurde um die Anfrage zu signieren.
Die Signatur ist eine Auswahl von Elementen aus der REST-Anfrage, die mit RFC 2104 HMAC-SHA1 verschlüsselt werden, die Signatur ändert sich daher mit jeder Anfrage. Bei einer eingehenden REST-Anfrage erzeugt das System die selbe Auswahl der Anfrageelemente und verschlüsselt sie mit dem geheimen Schlüssel der zur übermittelten Access-Key-Id gehört. Stimmen die übermittelte Signatur und die vom System erzeugte Signatur überein gilt die Anfrage als authentifiziert und wird mit der Authorisierung des Schlüsselinhabers ausgeführt.
Der Authorisation-Header wird wie folgt erzeugt:
Authorization = "COB" + " " + CobaiWebServiceAccessKeyId + ":" + Signatur;
Signatur = Base64( HMAC-SHA1 ( UTF8-Encoding-von ( IhrGeheimerSchüssel, ZuSignierenderString ) ) );
ZuSignierenderString = HTTPVerb + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Date + "\n" +
Header-Normalform = <siehe weiter unten> +
URL-Normalform = <siehe weiter unten>
In Java würden Sie dazu etwas in dieser Art machen, wobei RequestDescription das Erzeugen der Normalform kapselt:
package net.lacho.remoting.api;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class RequestSignature {
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
private String signature;
public RequestSignature(final String privateKey, final RequestDescription requestDescription) {
try {
SecretKeySpec signingKey = new SecretKeySpec(privateKey.getBytes(), HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[]rawHmac = mac.doFinal(requestDescription.getBytes());
signature = new Base64(1000, new byte[] {}).encodeToString(rawHmac);
}
catch (Exception e) {
signature = "";
}
}
@Override
public String toString() {
return signature;
}
}
Erzeugung der Anfrage-Normalform
Wie weiter oben ausgeführt erzeugt das System bei einer Anfrage eine Signatur für die Anfrage und vergleicht sie mit der übermittelten Signatur. Sie müssen daher die Signatur mit der selben Methode erzeugen wie das System. Der zu signierenden String heißt auch Anfrage-Normalform. Um die Anfrage-Normalform erzeugen zu können müssen sie die URL-Normalform und die Header-Normalform erzeugen.
Header-Normalform
Um die Header-Normalform zu erzeugen verwenden Sie alle HTTP-Header aus Ihrer Anfrage die mit 'x-cob-' beginnen und wenden den folgenden Prozess darauf an:
- Wandeln Sie alle Headernamen in Kleinbuchstaben um, aus 'X-Cob-Date' wird also 'x-cob-date'.
- Sortieren Sie die Headerfelder lexikografisch nach den Headernamen.
- Kombinieren Sie mehrere Header mit identischem Namen in einen neuen Header, wie in
RFC 2616, Abschnitt 4.2 beschrieben.
Zwischen den einzelnen kommagetrennten Werten ist kein Whitespace erlaubt. Zum Bespiel wird aus den beiden
Headern
x-cob-username:user1
undx-cob-username:user2
der Headerx-cob-username:user1,user2
. - Wandeln Sie Header deren Werte über mehrere Zeilen gehen in eine einzeilige Form um (siehe RFC 2616, Abschnitt 4.2).
- Entfernen Sie eventuell vorhanden Leerraum um den Doppelpunkt in den Headern.
- Fügen Sie an jeden Header ein Newline an (U+000A). Verbinden Sie anschließend alle Header zu einem einzelnen String. Dieser String ist die Header-Normalform.
URL-Normalform
Erzeugung der URL-Normalform:
- Entfernen Sie den Domänenteil (FQDN) aus der Anfrage-URL.
- Entfernen Sie den Query-String, sofern vorhanden, inklusive des "?" aus der Anfrage URL.
- URL-encoden Sie alle Meta-Characters im verbliebenen String. Verwenden Sie dabei UTF-8.
Damit entsteht zum Beispiel aus
api.cobai.com/v2/orders/pending?sort=desc
die Normalform
/v2/orders/pending
Benannte Headerelemente und Headerelemente an festen Positionen
Die ersten vier Headerelemente unterscheiden sich von den anderen dadurch das sie an einer festen Position stehen. Von ihnen wird daher in den zu signierenden String nur der Wert, nicht aber der Headername aufgenommen.
Wenn einer dieser Header in Ihrer Anfrage nicht vorkommt (manche sind optional, z.B. Content-Type bei einer PUT-Anfrage) verwenden sie an der entsprechenden Stelle den leeren String ("").
Anforderungen an Zeitstempel
Für jede Anfrage ist ein gültiger Zeitstempel, entweder als HTTP Date Header oder alternativ als x-cob-date Header
notwendig. Der Zeitstempel darf maximal 15 Minuten (vor oder zurück) von unserer aktuellen Systemzeit abweichen wenn die
Anfrage abgesendet wird um Replay-Angriffe
zu erschweren. Anfragen die ausserhalb der erlaubten Zeitfensters eingehen
werden mit einem RequestTimeTooSkewed
-Fehler abgelehnt.
Wenn Ihre HTTP-Clientlibrary das Setzen des Date Headers nicht erlaubt können Sie alternativ den x-cob-date
Header
verwenden. Das Datum muss in einem der von RFC 2616, Abschnitt 3.3 vorgegebenen Formate übergeben werden. Wenn Sie
einen x-cob-date Header
übergeben ignoriert das System bei der erzeugung des zu signierenden Strings den Date Header.
Denken Sie also in diesem Fall daran bei der Erzeugung Ihres zu signierenden Strings für den Date Header den leeren String
("") zu verwenden.
Signaturprobleme
Wenn die Authentifizierung fehlschlägt antwortet das System mit einem SignatureDoesNotMatch
-Fehler
(ein XML-Dokument). Dieses Dokument soll Ihnen helfen mögliche Fehlergründe zu erkennen und enthält dazu unter anderem
im Element requestDescription
den kompletten zu signierenden String basierend auf der von Ihnen übermittelten
Anfrage.