/******************************************************************************* * Copyright 2008-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"). You may not use * this file except in compliance with the License. A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * ***************************************************************************** * __ _ _ ___ * ( )( \/\/ )/ __) * /__\ \ / \__ \ * (_)(_) \/\/ (___/ * * AWS SDK for .NET * */ using System; using System.IO; using System.Text; using System.Xml.Serialization; using Amazon.Runtime; using Amazon.Util; using ThirdParty.Json.LitJson; using System.Globalization; using System.Collections.Generic; namespace Amazon.S3.Util { /// /// Utility class for managing and exchanging HTTP POST uploads of objects to Amazon S3. /// /// /// /// This object supports creating, marshalling, and unmarshalling of the information needed to build /// an authenticated HTTP POST request to S3 for uploading objects according to a policy. /// /// For more information, /// [XmlRootAttribute(IsNullable = false)] public class S3PostUploadSignedPolicy { /// /// Given a policy and AWS credentials, produce a S3PostUploadSignedPolicy. /// /// JSON string representing the policy to sign /// Credentials to sign the policy with /// A signed policy object for use with an S3PostUploadRequest. public static S3PostUploadSignedPolicy GetSignedPolicy(string policy, AWSCredentials credentials) { ImmutableCredentials iCreds = credentials.GetCredentials(); var policyBytes = iCreds.UseToken ? addTokenToPolicy(policy, iCreds.Token) : Encoding.UTF8.GetBytes(policy.Trim()); var base64Policy = Convert.ToBase64String(policyBytes); string signature = CryptoUtilFactory.CryptoInstance.HMACSign(Encoding.UTF8.GetBytes(base64Policy), iCreds.SecretKey, SigningAlgorithm.HmacSHA1); return new S3PostUploadSignedPolicy { Policy = base64Policy, Signature = signature, AccessKeyId = iCreds.AccessKey, SecurityToken = iCreds.Token, SignatureVersion = "2" }; } /// /// Given a policy and AWS credentials, produce a S3PostUploadSignedPolicy. /// /// JSON string representing the policy to sign /// Credentials to sign the policy with /// Service region endpoint. /// A signed policy object for use with an S3PostUploadRequest. public static S3PostUploadSignedPolicy GetSignedPolicyV4(string policy, AWSCredentials credentials, RegionEndpoint region) { var signedAt = AWSSDKUtils.CorrectedUtcNow; ImmutableCredentials iCreds = credentials.GetCredentials(); var algorithm = "AWS4-HMAC-SHA256"; var dateStamp = Runtime.Internal.Auth.AWS4Signer.FormatDateTime(signedAt, AWSSDKUtils.ISO8601BasicDateFormat); var dateTimeStamp = Runtime.Internal.Auth.AWS4Signer.FormatDateTime(signedAt, AWSSDKUtils.ISO8601BasicDateTimeFormat); var credentialString = string.Format(CultureInfo.InvariantCulture, "{0}/{1}/{2}/{3}/{4}/", iCreds.AccessKey, dateStamp, region.SystemName, "s3", Runtime.Internal.Auth.AWS4Signer.Terminator); Dictionary extraConditions = new Dictionary { { S3Constants.PostFormDataXAmzCredential, credentialString }, { S3Constants.PostFormDataXAmzAlgorithm, algorithm }, { S3Constants.PostFormDataXAmzDate, dateTimeStamp } }; if (iCreds.UseToken) { extraConditions[S3Constants.PostFormDataSecurityToken] = iCreds.Token; } var policyBytes = addConditionsToPolicy(policy, extraConditions); var base64Policy = Convert.ToBase64String(policyBytes); var signingKey = Runtime.Internal.Auth.AWS4Signer.ComposeSigningKey(iCreds.SecretKey, region.SystemName, dateStamp, "s3"); var signature = AWSSDKUtils.ToHex(Runtime.Internal.Auth.AWS4Signer.ComputeKeyedHash(SigningAlgorithm.HmacSHA256, signingKey, base64Policy), true); return new S3PostUploadSignedPolicy { Policy = base64Policy, Signature = signature, AccessKeyId = iCreds.AccessKey, SecurityToken = iCreds.Token, SignatureVersion = "4", Algorithm = algorithm, Date = dateTimeStamp, Credential = credentialString }; } private static byte[] addConditionsToPolicy(string policy, Dictionary newConditions) { var json = JsonMapper.ToObject(new JsonReader(policy)); var jsonConditions = json["conditions"]; if (jsonConditions != null && jsonConditions.IsArray) { foreach (var newCond in newConditions) { bool found = false; for (int i = 0; i < jsonConditions.Count; i++) { JsonData jsonCond = jsonConditions[i]; if (jsonCond.IsObject && jsonCond[newCond.Key] != null) { jsonCond[newCond.Key] = newCond.Value; found = true; } } if (!found) { var jsonCond = new JsonData(); jsonCond.SetJsonType(ThirdParty.Json.LitJson.JsonType.Object); jsonCond[newCond.Key] = newCond.Value; jsonConditions.Add(jsonCond); } } } return Encoding.UTF8.GetBytes(JsonMapper.ToJson(json).Trim()); } private static byte[] addTokenToPolicy(string policy, string token) { var json = JsonMapper.ToObject(new JsonReader(policy)); var found = false; var conditions = json["conditions"]; if (conditions != null && conditions.IsArray) { for (int i = 0; i < conditions.Count; i++) { JsonData cond = conditions[i]; if (cond.IsObject && cond[S3Constants.PostFormDataSecurityToken] != null) { cond[S3Constants.PostFormDataSecurityToken] = token; found = true; } } if (!found) { var tokenCondition = new JsonData(); tokenCondition.SetJsonType(ThirdParty.Json.LitJson.JsonType.Object); tokenCondition[S3Constants.PostFormDataSecurityToken] = token; conditions.Add(tokenCondition); } } return Encoding.UTF8.GetBytes(JsonMapper.ToJson(json).Trim()); } /// /// The policy document which governs what uploads can be done. /// public string Policy { get; set; } /// /// The signature for the policy. /// public string Signature { get; set; } /// /// The AWS Access Key Id for the credential pair that produced the signature. /// public string AccessKeyId { get; set; } /// /// The security token from session or instance credentials. /// public string SecurityToken { get; set; } /// /// The signature version usedd. Either "2" or "4". /// public string SignatureVersion { get; set; } /// /// The signing algorithm used. Required as a field in the post Amazon /// S3 can re-calculate the signature. /// public string Algorithm { get; set; } /// /// The date value in ISO8601 format. It is the same date used in /// creating the signing key. /// public string Date { get; set; } /// /// In addition to the access key ID, this provides scope information /// used in calculating the signing key for signature calculation. /// public string Credential { get; set; } /// /// Get the policy document as a human readable string. /// /// Human readable policy document. public string GetReadablePolicy() { return Encoding.UTF8.GetString(Convert.FromBase64String(this.Policy)); } private static string KEY_POLICY = "policy", KEY_SIGNATURE = "signature", KEY_ACCESSKEY = "access_key"; /// /// JSON representation of this object /// /// JSON string public string ToJson() { var json = new JsonData(); json[KEY_POLICY] = this.Policy; json[KEY_SIGNATURE] = this.Signature; json[KEY_ACCESSKEY] = this.AccessKeyId; return JsonMapper.ToJson(json); } /// /// XML Representation of this object /// /// XML String public string ToXml() { StringBuilder xml = new StringBuilder(1024); var serializer = new XmlSerializer(this.GetType()); using (StringWriter sw = new StringWriter(xml, CultureInfo.InvariantCulture)) { serializer.Serialize(sw, this); } return xml.ToString(); } /// /// Create an instance of this class from a JSON string. /// /// JSON string /// Instance of S3PostUploadSignedPolicy public static S3PostUploadSignedPolicy GetSignedPolicyFromJson(string policyJson) { JsonData json; try { json = JsonMapper.ToObject(policyJson); } catch (Exception e) { throw new ArgumentException("Invalid JSON document", e); } if (null == json[KEY_POLICY] || !json[KEY_POLICY].IsString) throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "JSON document requires '{0}' field"), KEY_POLICY); if (null == json[KEY_SIGNATURE] || !json[KEY_SIGNATURE].IsString) throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "JSON document requires '{0}' field"), KEY_SIGNATURE); if (null == json[KEY_ACCESSKEY] || !json[KEY_ACCESSKEY].IsString) throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "JSON document requires '{0}' field"), KEY_ACCESSKEY); return new S3PostUploadSignedPolicy { Policy = json[KEY_POLICY].ToString(), Signature = json[KEY_SIGNATURE].ToString(), AccessKeyId = json[KEY_ACCESSKEY].ToString() }; } /// /// Create an instance of this class from an XML string. /// /// XML string generated by ToXml() /// Instance of S3PostUploadSignedPolicy public static S3PostUploadSignedPolicy GetSignedPolicyFromXml(string policyXml) { var reader = new StringReader(policyXml); XmlSerializer serializer = new XmlSerializer(typeof(S3PostUploadSignedPolicy)); S3PostUploadSignedPolicy policy; try { policy = serializer.Deserialize(reader) as S3PostUploadSignedPolicy; } catch (Exception e) { throw new ArgumentException("Could not parse XML", e); } if (String.IsNullOrEmpty(policy.AccessKeyId)) throw new ArgumentException("XML Document requries 'AccessKeyId' field"); if (String.IsNullOrEmpty(policy.Policy)) throw new ArgumentException("XML Document requries 'Policy' field"); if (String.IsNullOrEmpty(policy.Signature)) throw new ArgumentException("XML Document requries 'Signature' field"); return policy; } } }