/* * Copyright 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. */ using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; using Amazon.CloudFront.Model; using Amazon.Runtime; using Amazon.Util; using ThirdParty.BouncyCastle.OpenSsl; using System.Globalization; namespace Amazon.CloudFront { /// /// This utility class provides methods for creating signed cookies for /// Amazon CloudFront distributions using canned or custom policies. /// public static class AmazonCloudFrontCookieSigner { private const string ExpiresKey = "CloudFront-Expires"; private const string SignatureKey = "CloudFront-Signature"; private const string PolicyKey = "CloudFront-Policy"; private const string KeyPairIdKey = "CloudFront-Key-Pair-Id"; /// /// The supported protocols for accessing restricted content /// using signed cookies. You may combine the enums for specifying /// multiple protocols. /// [Flags] public enum Protocols { /// /// Http Protocol /// Http = 1, /// /// Https Protocol /// Https = 2 } /// /// Returns signed cookies that grants universal access to private content until a given date. /// /// The protocol used to access content using signed cookies. /// The domain name of the distribution. /// The path for the resource. /// The private key file. RSA private key (.pem) are supported. /// The key pair id corresponding to the private key file given. /// The expiration date till which content can be accessed using the generated cookies. /// The signed cookies. public static CookiesForCannedPolicy GetCookiesForCannedPolicy(Protocols protocol, string distributionDomain, FileSystemInfo privateKey, string resourcePath, string keyPairId, DateTime expiresOn) { using (var reader = new StreamReader(File.OpenRead(privateKey.FullName))) { return GetCookiesForCannedPolicy(protocol, distributionDomain, reader, resourcePath, keyPairId, expiresOn); } } /// /// Returns signed cookies that grants universal access to private content until a given date. /// /// The protocol used to access content using signed cookies. /// The domain name of the distribution. /// The path for the resource. /// The private key file. RSA private key (.pem) are supported. /// The key pair id corresponding to the private key file given. /// The expiration date till which content can be accessed using the generated cookies. /// The signed cookies. public static CookiesForCannedPolicy GetCookiesForCannedPolicy(Protocols protocol, string distributionDomain, TextReader privateKey, string resourcePath, string keyPairId, DateTime expiresOn) { string url = GenerateResourcePath(protocol, distributionDomain, resourcePath); return GetCookiesForCannedPolicy(url, keyPairId, privateKey, expiresOn); } /// /// Generate signed cookies that allows access to a specific distribution and /// resource path by applying a access restrictions from a "canned" (simplified) /// policy document. /// /// /// The URL or path that uniquely identifies a resource within a /// distribution. For standard distributions the resource URL will /// be "http://" + distributionName + "/" + path /// (may also include URL parameters. For distributions with the /// HTTPS required protocol, the resource URL must start with /// "https://". RTMP resources do not take the form of a /// URL, and instead the resource path is nothing but the stream's /// name. /// /// Identifier of a public/private certificate keypair already configured in your Amazon Web Services account. /// The RSA private key data that corresponding to the certificate keypair identified by keyPairId. /// The expiration date till which content can be accessed using the generated cookies. /// The signed cookies. public static CookiesForCannedPolicy GetCookiesForCannedPolicy(string resourceUrlOrPath, string keyPairId, FileSystemInfo privateKey, DateTime expiresOn) { using (var reader = new StreamReader(File.OpenRead(privateKey.FullName))) { return GetCookiesForCannedPolicy(resourceUrlOrPath, keyPairId, reader, expiresOn); } } /// /// Generate signed cookies that allows access to a specific distribution and /// resource path by applying a access restrictions from a "canned" (simplified) /// policy document. /// /// /// The URL or path that uniquely identifies a resource within a /// distribution. For standard distributions the resource URL will /// be "http://" + distributionName + "/" + path /// (may also include URL parameters. For distributions with the /// HTTPS required protocol, the resource URL must start with /// "https://". RTMP resources do not take the form of a /// URL, and instead the resource path is nothing but the stream's /// name. /// /// Identifier of a public/private certificate keypair already configured in your Amazon Web Services account. /// The RSA private key data that corresponding to the certificate keypair identified by keyPairId. /// The expiration date till which content can be accessed using the generated cookies. /// The signed cookies. public static CookiesForCannedPolicy GetCookiesForCannedPolicy(string resourceUrlOrPath, string keyPairId, TextReader privateKey, DateTime expiresOn) { var cookies = new CookiesForCannedPolicy(); string epochSeconds = AWSSDKUtils.ConvertToUnixEpochSecondsString(expiresOn.ToUniversalTime()); cookies.Expires = new KeyValuePair( ExpiresKey, epochSeconds); RSAParameters rsaParameters = AmazonCloudFrontUrlSigner.ConvertPEMToRSAParameters(privateKey); string cannedPolicy = "{\"Statement\":[{\"Resource\":\"" + resourceUrlOrPath + "\",\"Condition\":{\"DateLessThan\":{\"AWS:EpochTime\":" + epochSeconds + "}}}]}"; byte[] signatureBytes = AmazonCloudFrontUrlSigner.SignWithSha1RSA( UTF8Encoding.UTF8.GetBytes(cannedPolicy), rsaParameters); string urlSafeSignature = AmazonCloudFrontUrlSigner.MakeBytesUrlSafe(signatureBytes); cookies.Signature = new KeyValuePair(SignatureKey, urlSafeSignature); cookies.KeyPairId = new KeyValuePair(KeyPairIdKey, keyPairId); return cookies; } /// /// Returns signed cookies that provides tailored access to private content based on an access time window and an ip range. /// /// The protocol used to access content using signed cookies. /// The domain name of the distribution. /// Your private key file. RSA private key (.pem) are supported. /// The path for the resource. /// The key pair id corresponding to the private key file given. /// The expiration date till which content can be accessed using the generated cookies. /// The date from which content can be accessed using the generated cookies. /// The allowed IP address range of the client making the GET request, in CIDR form (e.g. 192.168.0.1/24). /// The signed cookies. public static CookiesForCustomPolicy GetCookiesForCustomPolicy(Protocols protocol, string distributionDomain, FileSystemInfo privateKey, string resourcePath, string keyPairId, DateTime expiresOn, DateTime activeFrom, string ipRange) { using (var reader = new StreamReader(File.OpenRead(privateKey.FullName))) { return GetCookiesForCustomPolicy(protocol, distributionDomain, reader, resourcePath, keyPairId, expiresOn, activeFrom, ipRange); } } /// /// Returns signed cookies that provides tailored access to private content based on an access time window and an ip range. /// /// The protocol used to access content using signed cookies. /// The domain name of the distribution. /// Your private key file. RSA private key (.pem) are supported. /// The path for the resource. /// The key pair id corresponding to the private key file given. /// The expiration date till which content can be accessed using the generated cookies. /// The date from which content can be accessed using the generated cookies. /// The allowed IP address range of the client making the GET request, in CIDR form (e.g. 192.168.0.1/24). /// The signed cookies. public static CookiesForCustomPolicy GetCookiesForCustomPolicy(Protocols protocol, string distributionDomain, TextReader privateKey, string resourcePath, string keyPairId, DateTime expiresOn, DateTime activeFrom, string ipRange) { var url = GenerateResourcePath(protocol, distributionDomain, resourcePath); return GetCookiesForCustomPolicy(url, privateKey, keyPairId, expiresOn, activeFrom, ipRange); } /// /// Returns signed cookies that provides tailored access to private content based on an access time window and an ip range. /// /// /// The URL or path for resource within a distribution. /// /// Your private key file. RSA private key (.pem) are supported. /// The key pair id corresponding to the private key file given. /// The expiration date till which content can be accessed using the generated cookies. /// The date from which content can be accessed using the generated cookies. /// The allowed IP address range of the client making the GET request, in CIDR form (e.g. 192.168.0.1/24). /// The signed cookies. public static CookiesForCustomPolicy GetCookiesForCustomPolicy(string resourceUrlOrPath, TextReader privateKey, string keyPairId, DateTime expiresOn, DateTime activeFrom, string ipRange) { var cookies = new CookiesForCustomPolicy(); var policy = AmazonCloudFrontUrlSigner.BuildPolicyForSignedUrl(resourceUrlOrPath, expiresOn, ipRange, activeFrom); var base64EncodedPolicy = AmazonCloudFrontUrlSigner.MakeStringUrlSafe(policy); cookies.Policy = new KeyValuePair(PolicyKey, base64EncodedPolicy); RSAParameters rsaParameters = AmazonCloudFrontUrlSigner.ConvertPEMToRSAParameters(privateKey); byte[] signatureBytes = AmazonCloudFrontUrlSigner.SignWithSha1RSA( UTF8Encoding.UTF8.GetBytes(policy), rsaParameters); string urlSafeSignature = AmazonCloudFrontUrlSigner.MakeBytesUrlSafe(signatureBytes); cookies.Signature = new KeyValuePair(SignatureKey, urlSafeSignature); cookies.KeyPairId = new KeyValuePair(KeyPairIdKey, keyPairId); return cookies; } /// /// Returns signed cookies that provides tailored access to private content based on an access time window and an ip range. /// /// The protocol used to access content using signed cookies. /// The domain name of the distribution. /// Your private key file. RSA private key (.pem) are supported. /// The path for the resource. /// The key pair id corresponding to the private key file given. /// The expiration date till which content can be accessed using the generated cookies. /// The allowed IP address range of the client making the GET request, in CIDR form (e.g. 192.168.0.1/24). /// The signed cookies. public static CookiesForCustomPolicy GetCookiesForCustomPolicy(Protocols protocol, string distributionDomain, TextReader privateKey, string resourcePath, string keyPairId, DateTime expiresOn, string ipRange) { return GetCookiesForCustomPolicy(protocol, distributionDomain, privateKey, resourcePath, keyPairId, expiresOn, DateTime.MinValue, ipRange); } /// /// Returns the resource path for the given distribution, object, /// and protocol. /// private static string GenerateResourcePath(Protocols protocol, string distributionDomain, string path) { if (protocol == 0) { // Uninitialized protocol value. throw new ArgumentException("Invalid value for AmazonCloudFrontCookieSigner.Protocols enum." + " Valid values are AmazonCloudFrontCookieSigner.Protocols.Http or AmazonCloudFrontCookieSigner.Protocols.Https .", "protocol"); } if (protocol == (Protocols.Http | Protocols.Https)) { return "http*://" + distributionDomain + "/" + path; } else { return protocol.ToString().ToLowerInvariant() + "://" + distributionDomain + "/" + path; } } } /// /// Contains common cookies used by Amazon CloudFront. /// public abstract class SignedCookies { /// /// The active CloudFront key pair Id for the key pair (Trusted Signer) that you are using to generate the signature. /// /// For more information, see /// Specifying the AWS Accounts That Can Create Signed URLs and Signed Cookies (Trusted Signers) /// in the Amazon CloudFront User Guide. /// /// public KeyValuePair KeyPairId { get; internal set; } /// /// The hashed and signed version of the policy. /// public KeyValuePair Signature { get; internal set; } } /// /// Contains the cookies used to access restricted content from /// CloudFront using a canned policy. /// public class CookiesForCannedPolicy : SignedCookies { /// /// Date and time in Unix time format (in seconds) and Coordinated Universal Time (UTC). /// public KeyValuePair Expires { get; internal set; } } /// /// Contains the cookies used to access restricted content from /// CloudFront using a custom policy. /// public class CookiesForCustomPolicy : SignedCookies { /// /// Base64 encoded version of the custom policy. /// public KeyValuePair Policy { get; internal set; } } }