Autenticación de la firma transportada en la tabla cargada con un navegador
OBS admite la carga de objetos basada en navegador utilizando el método POST. Las firmas de dichas solicitudes se cargan en tablas. En primer lugar, cree una política de seguridad y especifique los requisitos de la solicitud, por ejemplo, nombre de bucket y prefijo de nombre de objeto. A continuación, cree una firma basada en esta política. El formulario de solicitud que se va a firmar debe contener una firma y una política válidas. Por último, cree una tabla para cargar el objeto en el bucket.
El proceso de cálculo de firmas es el siguiente:
- El contenido de la política está codificado en formato UTF-8.
- Realizar codificación de Base64 sobre el resultado obtenido del paso anterior.
- Utilice la SK para realizar el cálculo de la firma HMAC-SHA1 en el resultado obtenido del paso 2.
- Realice la codificación de Base64 en el resultado del paso 3 para obtener la firma.
StringToSign = Base64( UTF-8-Encoding-Of( policy ) ) Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, StringToSign ) )
{ "expiration": "2017-12-31T12:00:00.000Z", "conditions": [ {"x-obs-acl": "public-read" }, {"x-obs-security-token": "YwkaRTbdY8g7q...." }, {"bucket": "book" }, ["starts-with", "$key", "user/"] ] }
La póliza contiene el período de validez (véase Caducidad) y las condiciones (véase Condiciones)
Caducidad
El campo de expiration describe cuándo caducará la firma, que se expresa en el formato según ISO 8601 UTC. Por ejemplo, expiration: 2017-12-31T12:00:00.000Z en el ejemplo significa que la solicitud se invalida después de las 12:00:00 de 31 de diciembre de 2017. Este campo debe especificarse en una política. Solo puede estar en el formato aaaa-MM-dd'T'HH:mm:ss'Z' o aaaa-MM-dd'T'HH:mm:ss.SSS'Z'.
Condiciones
Un mecanismo utilizado para verificar la validez de una solicitud. Las condiciones se utilizan para definir el contenido que debe estar contenido en una solicitud. En el ejemplo, el nombre del bucket solicitado es book, el nombre del objeto tiene el prefijo user/ y la ACL del objeto es de lectura pública. Todos los elementos del formulario, excepto AccessKeyId, signature, file, policy, token, field names y el prefijo x-ignore-, deben incluirse en la política. En la siguiente tabla se enumeran los elementos que deben incluirse en las condiciones.
Elemento |
Descripción |
---|---|
x-obs-acl |
ACL en la solicitud. Soporta la coincidencia exacta y la coincidencia condicional, como starts-with. |
content-length-range |
Longitud máxima y mínima de un objeto que se va a cargar. El valor puede ser un rango. |
Cache-Control, Content-Type, Content-Disposition, Content-Encoding, Expires |
Encabezados especiales para solicitudes de REST Soporta la coincidencia exacta y la coincidencia condicional, como starts-with. |
key |
Nombre de un objeto que se va a cargar. Soporta la coincidencia exacta y la coincidencia condicional, como starts-with. |
bucket |
Nombre del bucket solicitado. Soporta coincidencia exacta. |
success_action_redirect |
Dirección de redirección después de que la carga se haya realizado correctamente. Para obtener más información, véase Carga de objetos - POST. Soporta la coincidencia exacta y la coincidencia condicional, como starts-with. |
success_action_status |
Si no se especifica success_action_redirect, el código de estado se devuelve al cliente cuando la carga se realiza correctamente. Para obtener más información, véase Carga de objetos - POST. Soporta coincidencia exacta. |
x-obs-meta-* |
Metadatos definidos por el usuario. Las palabras clave de un elemento no pueden contener los caracteres no de ASCII o irreconocibles. Si son necesarios los caracteres no de ASCII o irreconocibles, deben codificarse y decodificarse en el lado del cliente. La codificación URL o la codificación Base64 son aceptables, pero el servidor no realiza la decodificación. Soporta la coincidencia exacta y la coincidencia condicional, como starts-with. |
x-obs-* |
Otros campos de encabezado con prefijo x-obs-. Soporta la coincidencia exacta y la coincidencia condicional, como starts-with. |
x-obs-security-token |
Nombre del campo en el encabezado de solicitud. Campo obligatorio para la autenticación temporal de AK/SK y token de seguridad. |
La tabla siguiente describe cómo se pueden emparejar las condiciones de política.
Método de coincidencia |
Descripción |
---|---|
Exact Matches |
Coincidencia exacta por defecto. El valor de la tabla POST debe ser el mismo que el de la política. Por ejemplo, si el objeto ACL se establece en public-read cuando se carga el objeto, el valor del elemento x-obs-acl en la tabla es public-read. Por lo tanto, las condiciones de la política se pueden establecer en {"x-obs-acl": "public-read"} o ["eq", "$x-obs-acl", "public-read"], que son equivalentes. |
Starts With |
Si se utiliza esta condición, el valor establecido en la tabla POST debe comenzar con una string de caracteres fija. Por ejemplo, si el nombre de los objetos cargados debe tener el prefijo user/, el valor del elemento key en la tabla puede ser user/test1, user/test2 y así sucesivamente. Por lo tanto, las condiciones de la política se pueden establecer en: ["starts-with", "$key", "user/"] |
Matching Any Content |
El elemento correspondiente en la tabla POST puede ser cualquier valor. Por ejemplo, si la dirección de redirección tras el éxito de la solicitud puede ser cualquier dirección, el valor del elemento success_action_redirect en la tabla puede ser cualquier valor. Por lo tanto, las condiciones de la política se pueden establecer en: ["starts-with", "$success_action_redirect", ""] |
Specifying Ranges |
La longitud de contenido del elemento file en la tabla POST puede ser un rango especificado y se utiliza solo para limitar el tamaño del objeto. Por ejemplo, si el tamaño del objeto cargado está entre 1 MB y 10 MB, la longitud de contenido del elemento file de la tabla puede ser de 1048576 a 10485760. Por lo tanto, las condiciones de la política se pueden establecer en (el valor no contiene comillas) ["content-length-range", 1048576, 10485760] |
Una política está en formato JSON. Las condiciones se pueden poner entre corchetes rizados {} y corchetes cuadrados []. Los elementos clave y valor de la tabla se escriben entre corchetes rizados {}, que están separados por dos puntos (:). Los corchetes [] contienen el tipo de condición, la clave y el valor. Estos tres elementos están separados por comas (,). El signo de dólar ($) delante de la clave indica que la clave es una variable.
La siguiente tabla muestra los caracteres que deben escaparse en una política.
Personaje después de escapar |
Carácter real |
---|---|
\\ |
Barra diagonal inversa (\) |
\$ |
Símbolo del dólar ($) |
\b |
Tecla retroceso |
\f |
Página arriba y abajo |
\n |
Saltos de línea |
\r |
Ingresar |
\t |
Tabla horizontal |
\v |
Tabla vertical |
\uxxxx |
Todos los caracteres de Unicode |
Ejemplos de solicitud y política
En las tablas siguientes se proporcionan ejemplos de solicitudes y políticas.
Ejemplo 1: Subir el objeto testfile.txt al bucket examplebucket y establecer el objeto ACL a public-read.
Solicitud |
Política |
---|---|
POST / HTTP/1.1 Host: examplebucket.obs.region.myhuaweicloud.com Content-Type: multipart/form-data; boundary=7e32233530b26 Content-Length: 1250 --7e32233530b26 Content-Disposition: form-data; name="key" testfile.txt --7e32233530b26 Content-Disposition: form-data; name="x-obs-acl" public-read --7e32233530b26 Content-Disposition: form-data; name="content-type" text/plain --7e32233530b26 Content-Disposition: form-data; name="AccessKeyId" UDSIAMSTUBTEST000002 --7e32233530b26 Content-Disposition: form-data; name="policy" ewogICJleHBpcmF0aW9uIjogIjIwMTktMDctMDFUMTI6MDA6MDAuMDAwWiIsCiAgImNvbmRpdGlvbnMiOiBbCiAgICB7ImJ1Y2tldCI6ICJleGFtcGxlYnVja2V0IiB9LAogICAgWyJlcSIsICIka2V5IiwgInRlc3RmaWxlLnR4dCJdLAoJeyJ4LW9icy1hY2wiOiAicHVibGljLXJlYWQiIH0sCiAgICBbImVxIiwgIiRDb250ZW50LVR5cGUiLCAidGV4dC9wbGFpbiJdLAogICAgWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsIDYsIDEwXQogIF0KfQo= --7e32233530b26 Content-Disposition: form-data; name="signature" xxl7bZs/5FgtBUggOdQ88DPZUo0= --7e32233530b26 Content-Disposition: form-data; name="file"; filename="E:\TEST_FILE\TEST.txt" Content-Type: text/plain 123456 --7e32233530b26 Content-Disposition: form-data; name="submit" Upload --7e32233530b26-- |
{ "expiration": "2019-07-01T12:00:00.000Z", "conditions": [ {"bucket": "examplebucket" }, ["eq", "$key", "testfile.txt"], {"x-obs-acl": "public-read" }, ["eq", "$Content-Type", "text/plain"] ] } |
Ejemplo 2: Sube el objeto file/obj1 al bucket examplebucket y configura los cuatro elementos de metadatos personalizados del objeto.
Solicitud |
Política |
---|---|
POST / HTTP/1.1 Host: examplebucket.obs.region.myhuaweicloud.com Content-Type: multipart/form-data; boundary=7e329d630b26 Content-Length: 1597 --7e3542930b26 Content-Disposition: form-data; name="key" file/obj1 --7e3542930b26 Content-Disposition: form-data; name="AccessKeyId" UDSIAMSTUBTEST000002 --7e3542930b26 Content-Disposition: form-data; name="policy" ewogICJleHBpcmF0aW9uIjogIjIwMTktMDctMDFUMTI6MDA6MDAuMDAwWiIsCiAgImNvbmRpdGlvbnMiOiBbCiAgICB7ImJ1Y2tldCI6ICJleGFtcGxlYnVja2V0IiB9LAogICAgWyJzdGFydHMtd2l0aCIsICIka2V5IiwgImZpbGUvIl0sCiAgICB7Ingtb2JzLW1ldGEtdGVzdDEiOiJ2YWx1ZTEifSwKICAgIFsiZXEiLCAiJHgtb2JzLW1ldGEtdGVzdDIiLCAidmFsdWUyIl0sCiAgICBbInN0YXJ0cy13aXRoIiwgIiR4LW9icy1tZXRhLXRlc3QzIiwgImRvYyJdLAogICAgWyJzdGFydHMtd2l0aCIsICIkeC1vYnMtbWV0YS10ZXN0NCIsICIiXQogIF0KfQo= --7e3542930b26 Content-Disposition: form-data; name="signature" HTId8hcaisn6FfdWKqSJP9RN4Oo= --7e3542930b26 Content-Disposition: form-data; name="x-obs-meta-test1" value1 --7e3542930b26 Content-Disposition: form-data; name="x-obs-meta-test2" value2 --7e3542930b26 Content-Disposition: form-data; name="x-obs-meta-test3" doc123 --7e3542930b26 Content-Disposition: form-data; name="x-obs-meta-test4" my --7e3542930b26 Content-Disposition: form-data; name="file"; filename="E:\TEST_FILE\TEST.txt" Content-Type: text/plain 123456 --7e3542930b26 Content-Disposition: form-data; name="submit" Upload --7e3542930b26-- |
{ "expiration": "2019-07-01T12:00:00.000Z", "conditions": [ {"bucket": "examplebucket" }, ["starts-with", "$key", "file/"], {"x-obs-meta-test1":"value1"}, ["eq", "$x-obs-meta-test2", "value2"], ["starts-with", "$x-obs-meta-test3", "doc"], ["starts-with", "$x-obs-meta-test4", ""] ] } |
Cálculo de signatura en Java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Base64; import java.util.Date; import java.util.List; import java.util.TimeZone; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class SignDemo { private static final String DEFAULT_ENCODING = "UTF-8"; private static final String EXPIRATION_DATE_FORMATTER = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; private static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT"); private static final long DEFAULT_EXPIRE_SECONDS = 300; private String ak; private String sk; private String join(List<?> items, String delimiter) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < items.size(); i++) { String item = items.get(i).toString(); sb.append(item); if (i < items.size() - 1) { sb.append(delimiter); } } return sb.toString(); } public String hmacSha1(String input) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException { SecretKeySpec signingKey = new SecretKeySpec(this.sk.getBytes(DEFAULT_ENCODING), "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(signingKey); return Base64.getEncoder() .encodeToString(mac.doFinal(Base64.getEncoder().encode(input.getBytes(DEFAULT_ENCODING)))); } private String stringToSign(List<String> conditions, String expiration) { StringBuilder policy = new StringBuilder(); policy.append("{\"expiration\":").append("\"").append(expiration).append("\",") .append("\"conditions\":["); policy.append(join(conditions, ",")); policy.append("]}"); return policy.toString(); } private String getFormatExpiration(Date requestDate, long expires) { requestDate = requestDate != null ? requestDate : new Date(); SimpleDateFormat expirationDateFormat = new SimpleDateFormat(EXPIRATION_DATE_FORMATTER); expirationDateFormat.setTimeZone(GMT_TIMEZONE); Date expiryDate = new Date(requestDate.getTime() + (expires <= 0 ? DEFAULT_EXPIRE_SECONDS : expires) * 1000); String expiration = expirationDateFormat.format(expiryDate); return expiration; } public String postSignature(List<String> conditions, String expiration) throws Exception { return hmacSha1(this.stringToSign(conditions, expiration)); } public static void main(String[] args) throws Exception { SignDemo demo = new SignDemo(); /* Hard-coded or plaintext AK and SK are risky. For security purposes, encrypt your AK and SK and store them in the configuration file or environment variables. In this example, the AK and SK are stored in environment variables for identity authentication. Before running the code in this example, configure environment variables HUAWEICLOUD_SDK_AK and HUAWEICLOUD_SDK_SK. */ demo.ak = System.getenv("HUAWEICLOUD_SDK_AK"); demo.sk = System.getenv("HUAWEICLOUD_SDK_SK"); String expiration = demo.getFormatExpiration(null, 0); List<String> conditions = new ArrayList<>(); String[] tmpConditions = { "{\"x-obs-acl\": \"public-read\" }", "{\"x-obs-security-token\": \"YwkaRTbdY8g7q....\" }", "{\"bucket\": \"book\" }", "[\"starts-with\", \"$key\", \"user/\"]" }; for (String condition : tmpConditions) { conditions.add(condition); } System.out.println(expiration); System.out.println(demo.postSignature(conditions, expiration)); } } |
Cálculo de signatura en Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# coding=utf-8 import hashlib import hmac import time import pytz import binascii from datetime import datetime class SignatureDemo: EXPIRATION_DATE_FORMATTER = "%Y-%m-%dT%H:%M:%S.%f" DEFAULT_ENCODING = "UTF-8" # The default expiration time is 5 minutes. DEFAULT_EXPIRE_SECONDS = 300 GMT_TIMEZONE = "GMT" def __init__(self, ak=None, sk=None): self.ak = ak self.__sk = sk def set_secret_access_key(self, sk): self.__sk = sk # request_date and expires should be set to timestamps, for example, 1675651495.979. def get_format_expiration(self, request_date, expires): request_date = request_date if request_date else time.time() expiry_date = request_date + (expires if expires > 0 else self.DEFAULT_EXPIRE_SECONDS) expiration = datetime.fromtimestamp(expiry_date, pytz.timezone(self.GMT_TIMEZONE)).strftime( self.EXPIRATION_DATE_FORMATTER)[:-3] + "Z" return expiration def hmac_sha1(self, ipt): # If binascii or encode("base64") is used, newline characters must be removed. policy_base64 = binascii.b2a_base64(ipt.encode(self.DEFAULT_ENCODING)).rstrip() hashed = hmac.new(self.__sk.encode(self.DEFAULT_ENCODING), policy_base64, hashlib.sha1) return binascii.b2a_base64(hashed.digest()).rstrip() def post_signature(self, conditions, expiration): return self.hmac_sha1(self.string_to_sign(conditions, expiration)) @staticmethod def string_to_sign(conditions, expiration): policy = "" policy += "{\"expiration\":" + "\"" + expiration + "\"," + "\"conditions\":[" policy += ",".join(conditions) + "]}" return policy if __name__ == "__main__": demo = SignatureDemo() # Hard-coded or plaintext AK and SK are risky. For security purposes, encrypt your AK and SK and store them in the configuration file or environment variables. # In this example, the AK and SK are stored in environment variables for identity authentication. Before running the code in this example, configure environment variables HUAWEICLOUD_SDK_AK and HUAWEICLOUD_SDK_SK. demo.ak = os.getenv('HUAWEICLOUD_SDK_AK') demo.set_secret_access_key(os.getenv('HUAWEICLOUD_SDK_SK')) exp = demo.get_format_expiration(None, 0) conditions_example = [ "{\"x-obs-acl\": \"public-read\" }", "{\"x-obs-security-token\": \"YwkaRTbdY8g7q....\" }", "{\"bucket\": \"book\" }", "[\"starts-with\", \"$key\", \"user/\"]" ] print(exp) print(demo.post_signature(conditions_example, exp)) |