sign

package module
v1.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 18, 2024 License: Apache-2.0 Imports: 22 Imported by: 9

Documentation

Overview

Package sign provides utilities to generate signed URLs for Amazon CloudFront.

More information about signed URLs and their structure can be found at: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html

To sign a URL create a URLSigner with your private key and credential pair key ID. Once you have a URLSigner instance you can call URLSigner.Sign or URLSigner.SignWithPolicy to sign the URLs.

Example:

// Load our key from a PEM block.
privKey, err := sign.LoadPEMPrivKey(block)
if err != nil {
    log.Fatalf("Failed to load private key, err: %s\n", err.Error())
}

// Create our signer. Keys loaded via the LoadPEMPrivKey* family of APIs
// implement crypto.Signer and can be passed to this directly.
signer := sign.NewURLSigner(keyID, privKey)

// Sign URL to be valid for 1 hour from now.
signedURL, err := signer.Sign(rawURL, time.Now().Add(1*time.Hour))
if err != nil {
    log.Fatalf("Failed to sign url, err: %s\n", err.Error())
}

Index

Examples

Constants

View Source
const (
	// CookiePolicyName name of the policy cookie
	CookiePolicyName = "CloudFront-Policy"
	// CookieSignatureName name of the signature cookie
	CookieSignatureName = "CloudFront-Signature"
	// CookieKeyIDName name of the signing Key ID cookie
	CookieKeyIDName = "CloudFront-Key-Pair-Id"
)

Variables

This section is empty.

Functions

func CreateResource

func CreateResource(scheme, u string) (string, error)

CreateResource constructs, validates, and returns a resource URL string. An error will be returned if unable to create the resource string.

func LoadEncryptedPEMPrivKey deprecated

func LoadEncryptedPEMPrivKey(reader io.Reader, password []byte) (*rsa.PrivateKey, error)

LoadEncryptedPEMPrivKey decrypts the PEM encoded private key using the password provided returning a RSA private key. If the PEM data is invalid, or unable to decrypt an error will be returned.

Deprecated: RFC 1423 PEM encryption is insecure. Callers using encrypted keys should instead decrypt that payload externally and pass it to LoadPEMPrivKey.

func LoadPEMPrivKey

func LoadPEMPrivKey(reader io.Reader) (*rsa.PrivateKey, error)

LoadPEMPrivKey reads a PEM encoded RSA private key from the io.Reader. A new RSA private key will be returned if no error.

func LoadPEMPrivKeyFile

func LoadPEMPrivKeyFile(name string) (key *rsa.PrivateKey, err error)

LoadPEMPrivKeyFile reads a PEM encoded RSA private key from the file name. A new RSA private key will be returned if no error.

func LoadPEMPrivKeyPKCS8 added in v1.4.0

func LoadPEMPrivKeyPKCS8(reader io.Reader) (interface{}, error)

LoadPEMPrivKeyPKCS8 reads a PEM-encoded RSA private key in PKCS8 format from the given reader.

x509.ParsePKCS8PrivateKey can return multiple key types and this API does not discern between them. Callers in need of the underlying value must obtain it via type assertion:

key, err := LoadPEMPrivKeyPKCS8(r)
if err != nil { /* ... */ }

switch key.(type) {
case *rsa.PrivateKey:
	// ...
case *ecdsa.PrivateKey:
	// ...
case ed25519.PrivateKey:
	// ...
default:
	panic("unrecognized private key type")
}

See aforementioned API docs for a full list of possible key types.

If calling code can opaquely handle the returned key as a crypto.Signer, use LoadPEMPrivKeyPKCS8AsSigner instead.

func LoadPEMPrivKeyPKCS8AsSigner added in v1.4.0

func LoadPEMPrivKeyPKCS8AsSigner(reader io.Reader) (crypto.Signer, error)

LoadPEMPrivKeyPKCS8AsSigner wraps LoadPEMPrivKeyPKCS8 to expect a crypto.Signer.

Types

type AWSEpochTime

type AWSEpochTime struct {
	time.Time
}

An AWSEpochTime wraps a time value providing JSON serialization needed for AWS Policy epoch time fields.

func NewAWSEpochTime

func NewAWSEpochTime(t time.Time) *AWSEpochTime

NewAWSEpochTime returns a new AWSEpochTime pointer wrapping the Go time provided.

func (AWSEpochTime) MarshalJSON

func (t AWSEpochTime) MarshalJSON() ([]byte, error)

MarshalJSON serializes the epoch time as AWS Profile epoch time.

func (*AWSEpochTime) UnmarshalJSON added in v1.2.0

func (t *AWSEpochTime) UnmarshalJSON(data []byte) error

UnmarshalJSON unserializes AWS Profile epoch time.

type Condition

type Condition struct {
	// Optional IP address mask the signed URL must be requested from.
	IPAddress *IPAddress `json:"IpAddress,omitempty"`

	// Optional date that the signed URL cannot be used until. It is invalid
	// to make requests with the signed URL prior to this date.
	DateGreaterThan *AWSEpochTime `json:",omitempty"`

	// Required date that the signed URL will expire. A DateLessThan is required
	// sign cloud front URLs
	DateLessThan *AWSEpochTime `json:",omitempty"`
}

A Condition defines the restrictions for how a signed URL can be used.

type CookieOptions

type CookieOptions struct {
	Path     string
	Domain   string
	Secure   bool
	SameSite http.SameSite
	Expires  time.Time
}

A CookieOptions optional additional options that can be applied to the signed cookies.

Example
privKey := unit.RSAPrivateKey

// Create the CookieSigner with options set. These options can be set
// directly with cookieSigner.Opts. These values can be overridden on
// individual Sign and SignWithProfile calls.
cookieSigner := NewCookieSigner("keyID", privKey, func(o *CookieOptions) {
	//provide an optional struct fields to specify other options
	o.Path = "/"

	// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html
	o.Domain = ".cNameAssociatedWithMyDistribution.com"

	// Make sure your app/site can handle https payloads, otherwise
	// set this to false.
	o.Secure = true
})

// Use the signer to sign the URL
cookies, err := cookieSigner.Sign("http*://*", testSignTime.Add(30*time.Minute), func(o *CookieOptions) {
	o.Path = "/mypath/"
})
if err != nil {
	fmt.Println("failed to sign cookies with policy,", err)
	return
}

printExampleCookies(cookies)
Output:

Cookies:
CloudFront-Policy: eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cCo6Ly8qIiwiQ29uZGl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxMjU3ODk1ODAwfX19XX0_, /mypath/, .cNameAssociatedWithMyDistribution.com, true
CloudFront-Signature: DBXEcU6NoyAelecgEcr6mE1IHCqqlHdGwAC2X1dYn0QOLZ8Ar~oehlMub~hEh~UEMijR15ii-yUYf-3ML0b1SwWkh4rTa-SFURWDVuu~vW3cQzRZ4wQrgDR3DGJINrtGtEsDSzA6zdwtZsfvc1W9IRPn9rnVmwDdUurSrcp9M7CdcjkEw9Au~gULX7aUuW87DI5GI7jLo6emmBB1p4V~xAv8rDqOyxdhBzWKDTvl6ErIXnzHitgMclNZrkn-m27BhTQsJOs2R~gT2VrQw-IWX6NMD8r0TDH4DE2HQ8N7jZ0nf8gezbyFk-OhD1P9FUNb1PlwcZWfXtfgHQmM-BmrSQ__, /mypath/, .cNameAssociatedWithMyDistribution.com, true
CloudFront-Key-Pair-Id: keyID, /mypath/, .cNameAssociatedWithMyDistribution.com, true

type CookieSigner

type CookieSigner struct {
	Opts CookieOptions
	// contains filtered or unexported fields
}

A CookieSigner provides signing utilities to sign Cookies for Amazon CloudFront resources. Using a private key and Credential Key Pair key ID the CookieSigner only needs to be created once per Credential Key Pair key ID and private key.

More information about signed Cookies and their structure can be found at: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html

To sign a Cookie, create a CookieSigner with your private key and credential pair key ID. Once you have a CookieSigner instance you can call Sign or SignWithPolicy to sign the URLs.

The signer is safe to use concurrently, but the optional cookies options are not safe to modify concurrently.

func NewCookieSigner

func NewCookieSigner(keyID string, privKey *rsa.PrivateKey, opts ...func(*CookieOptions)) *CookieSigner

NewCookieSigner constructs and returns a new CookieSigner to be used to for signing Amazon CloudFront URL resources with.

func (CookieSigner) Sign

func (s CookieSigner) Sign(u string, expires time.Time, opts ...func(*CookieOptions)) ([]*http.Cookie, error)

Sign returns the cookies needed to allow user agents to make arbetrary requests to cloudfront for the resource(s) defined by the policy.

Sign will create a CloudFront policy with only a resource and condition of DateLessThan equal to the expires time provided.

The returned slice cookies should all be added to the Client's cookies or server's response.

Example:

s := sign.NewCookieSigner(keyID, privKey)

// Get Signed cookies for a resource that will expire in 1 hour
cookies, err := s.Sign("*", time.Now().Add(1 * time.Hour))
if err != nil {
    fmt.Println("failed to create signed cookies", err)
    return
}

// Or get Signed cookies for a resource that will expire in 1 hour
// and set path and domain of cookies
cookies, err := s.Sign("*", time.Now().Add(1 * time.Hour), func(o *sign.CookieOptions) {
    o.Path = "/"
    o.Domain = ".example.com"
})
if err != nil {
    fmt.Println("failed to create signed cookies", err)
    return
}

// Server Response via http.ResponseWriter
for _, c := range cookies {
    http.SetCookie(w, c)
}

// Client request via the cookie jar
if client.CookieJar != nil {
    for _, c := range cookies {
       client.Cookie(w, c)
    }
}
Example
privKey := unit.RSAPrivateKey
cookieSigner := NewCookieSigner("keyID", privKey)

// Use the signer to sign the URL
cookies, err := cookieSigner.Sign("http://example.com/somepath/*", testSignTime.Add(30*time.Minute))
if err != nil {
	fmt.Println("failed to sign cookies with policy,", err)
	return
}

printExampleCookies(cookies)
Output:

Cookies:
CloudFront-Policy: eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2V4YW1wbGUuY29tL3NvbWVwYXRoLyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjEyNTc4OTU4MDB9fX1dfQ__, , , false
CloudFront-Signature: Gx67J8t1VanOFWN84BQlpN064aGCicJv916esnPr9Rdb2RKEzl7VoDOsh9Uez7SY5blWATkN5F3xNicTpOupdN-ywrTf5zCTLz5RmvLrIyEDS3Y1knTGoWvp6nnIb9FOuI1rSyBaJ8VKuNVQGmvqzXGXsnipgSBPjpkL6Ja3dBXeKIbUeaLKQBZrtMWv9nS5VyG4nOP-CRcTgQ5DA3-h~WP2ZzhONb6yoYXeOSvBu8HBl0IZI27InLpxiKlkWUchNncnkZ32Md0CwLLrA4wxFl0fYsxxg6Us2XBYRGmudugJHgkkopem9Cc4eOiDGMABcJGAuZprVXT0WuOBYJngTA__, , , false
CloudFront-Key-Pair-Id: keyID, , , false

func (CookieSigner) SignWithPolicy

func (s CookieSigner) SignWithPolicy(p *Policy, opts ...func(*CookieOptions)) ([]*http.Cookie, error)

SignWithPolicy returns the cookies needed to allow user agents to make arbetrairy requets to cloudfront for the resource(s) defined by the policy.

The returned slice cookies should all be added to the Client's cookies or server's response.

Example:

s := sign.NewCookieSigner(keyID, privKey)

policy := &sign.Policy{
    Statements: []sign.Statement{
        {
            // Read the provided documentation on how to set this
            // correctly, you'll probably want to use wildcards.
            Resource: rawCloudFrontURL,
            Condition: sign.Condition{
                // Optional IP source address range
                IPAddress: &sign.IPAddress{SourceIP: "192.0.2.0/24"},
                // Optional date URL is not valid until
                DateGreaterThan: &sign.AWSEpochTime{time.Now().Add(30 * time.Minute)},
                // Required date the URL will expire after
                DateLessThan: &sign.AWSEpochTime{time.Now().Add(1 * time.Hour)},
            },
        },
    },
}

// Get Signed cookies for a resource that will expire in 1 hour
cookies, err := s.SignWithPolicy(policy)
if err != nil {
    fmt.Println("failed to create signed cookies", err)
    return
}

// Or get Signed cookies for a resource that will expire in 1 hour
// and set path and domain of cookies
cookies, err := s.SignWithPolicy(policy, func(o *sign.CookieOptions) {
    o.Path = "/"
    o.Domain = ".example.com"
})
if err != nil {
    fmt.Println("failed to create signed cookies", err)
    return
}

// Server Response via http.ResponseWriter
for _, c := range cookies {
    http.SetCookie(w, c)
}

// Client request via the cookie jar
if client.CookieJar != nil {
    for _, c := range cookies {
       client.Cookie(w, c)
    }
}
Example
// Sign cookie to be valid for 30 minutes from now, expires one hour
// from now, and restricted to the 192.0.2.0/24 IP address range.
// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-setting-signed-cookie-custom-policy.html
p := &Policy{
	// Only a single policy statement can be used with CloudFront
	// cookie signatures.
	Statements: []Statement{{
		// Read the provided documentation on how to set this correctly,
		// you'll probably want to use wildcards
		Resource: "http://sub.cloudfront.com",
		Condition: Condition{
			// Optional IP source address range
			IPAddress: &IPAddress{SourceIP: "192.0.2.0/24"},
			// Optional date URL is not valid until
			DateGreaterThan: &AWSEpochTime{testSignTime.Add(30 * time.Minute)},
			// Required date the URL will expire after
			DateLessThan: &AWSEpochTime{testSignTime.Add(1 * time.Hour)},
		},
	},
	},
}

privKey := unit.RSAPrivateKey

// Key ID that represents the key pair associated with the private key
keyID := "privateKeyID"

// Set credentials to the CookieSigner.
cookieSigner := NewCookieSigner(keyID, privKey)

// Avoid adding an Expire or MaxAge. See provided AWS Documentation for
// more info.
cookies, err := cookieSigner.SignWithPolicy(p)
if err != nil {
	fmt.Println("failed to sign cookies with policy,", err)
	return
}

printExampleCookies(cookies)
Output:

Cookies:
CloudFront-Policy: eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL3N1Yi5jbG91ZGZyb250LmNvbSIsIkNvbmRpdGlvbiI6eyJJcEFkZHJlc3MiOnsiQVdTOlNvdXJjZUlwIjoiMTkyLjAuMi4wLzI0In0sIkRhdGVHcmVhdGVyVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxMjU3ODk1ODAwfSwiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjEyNTc4OTc2MDB9fX1dfQ__, , , false
CloudFront-Signature: Ixn4bF1LLrLcB8XG-t5bZbIB0vfwSF2s4gkef~PcNBdx73MVvZD3v8DZ5GzcqNrybMiqdYJY5KqK6vTsf5JXDgwFFz-h98wdsbV-izcuonPdzMHp4Ay4qyXM6Ed5jB9dUWYGwMkA6rsWXpftfX8xmk4tG1LwFuJV6nAsx4cfpuKwo4vU2Hyr2-fkA7MZG8AHkpDdVUnjm1q-Re9HdG0nCq-2lnBAdOchBpJt37narOj-Zg6cbx~6rzQLVQd8XIv-Bn7VTc1tkBAJVtGOHb0q~PLzSRmtNGYTnpL0z~gp3tq8lhZc2HuvJW5-tZaYP9yufeIzk5bqsT6DT4iDuclKKw__, , , false
CloudFront-Key-Pair-Id: privateKeyID, , , false

type IPAddress

type IPAddress struct {
	SourceIP string `json:"AWS:SourceIp"`
}

An IPAddress wraps an IPAddress source IP providing JSON serialization information

type Policy

type Policy struct {
	// List of resource and condition statements.
	// Signed URLs should only provide a single statement.
	Statements []Statement `json:"Statement"`
}

A Policy defines the resources that a signed will be signed for.

See the following page for more information on how policies are constructed. http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html#private-content-custom-policy-statement

func NewCannedPolicy

func NewCannedPolicy(resource string, expires time.Time) *Policy

NewCannedPolicy returns a new Canned Policy constructed using the resource and expires time. This can be used to generate the basic model for a Policy that can be then augmented with additional conditions.

See the following page for more information on how policies are constructed. http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html#private-content-custom-policy-statement

func (*Policy) Sign

func (p *Policy) Sign(signer crypto.Signer) (b64Signature, b64Policy []byte, err error)

Sign will sign a policy using an RSA private key. It will return a base 64 encoded signature and policy if no error is encountered.

The signature and policy should be added to the signed URL following the guidelines in: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html

func (*Policy) Validate

func (p *Policy) Validate() error

Validate verifies that the policy is valid and usable, and returns an error if there is a problem.

type Statement

type Statement struct {
	// The Web or RTMP resource the URL will be signed for
	Resource string

	// The set of conditions for this resource
	Condition Condition
}

A Statement is a collection of conditions for resources

type URLSigner

type URLSigner struct {
	// contains filtered or unexported fields
}

An URLSigner provides URL signing utilities to sign URLs for Amazon CloudFront resources. Using a private key and Credential Key Pair key ID the URLSigner only needs to be created once per Credential Key Pair key ID and private key.

The signer is safe to use concurrently.

func NewURLSigner

func NewURLSigner(keyID string, signer crypto.Signer) *URLSigner

NewURLSigner constructs and returns a new URLSigner to be used to for signing Amazon CloudFront URL resources with.

func (URLSigner) Sign

func (s URLSigner) Sign(url string, expires time.Time) (string, error)

Sign will sign a single URL to expire at the time of expires sign using the Amazon CloudFront default Canned Policy. The URL will be signed with the private key and Credential Key Pair Key ID previously provided to URLSigner.

This is the default method of signing Amazon CloudFront URLs. If extra policy conditions are need other than URL expiry use SignWithPolicy instead.

Example:

// Sign URL to be valid for 1 hour from now.
signer := sign.NewURLSigner(keyID, privKey)
signedURL, err := signer.Sign(rawURL, time.Now().Add(1*time.Hour))
if err != nil {
    log.Fatalf("Failed to sign url, err: %s\n", err.Error())
}

func (URLSigner) SignWithPolicy

func (s URLSigner) SignWithPolicy(url string, p *Policy) (string, error)

SignWithPolicy will sign a URL with the Policy provided. The URL will be signed with the private key and Credential Key Pair Key ID previously provided to URLSigner.

Use this signing method if you are looking to sign a URL with more than just the URL's expiry time, or reusing Policies between multiple URL signings. If only the expiry time is needed you can use Sign and provide just the URL's expiry time. A minimum of at least one policy statement is required for a signed URL.

Note: It is not safe to use Polices between multiple signers concurrently

Example:

// Sign URL to be valid for 30 minutes from now, expires one hour from now, and
// restricted to the 192.0.2.0/24 IP address range.
policy := &sign.Policy{
    Statements: []sign.Statement{
        {
            Resource: rawURL,
            Condition: sign.Condition{
                // Optional IP source address range
                IPAddress: &sign.IPAddress{SourceIP: "192.0.2.0/24"},
                // Optional date URL is not valid until
                DateGreaterThan: &sign.AWSEpochTime{time.Now().Add(30 * time.Minute)},
                // Required date the URL will expire after
                DateLessThan: &sign.AWSEpochTime{time.Now().Add(1 * time.Hour)},
            },
        },
    },
}

signer := sign.NewURLSigner(keyID, privKey)
signedURL, err := signer.SignWithPolicy(rawURL, policy)
if err != nil {
    log.Fatalf("Failed to sign url, err: %s\n", err.Error())
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL