/*
* 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; }
}
}