github.com/aliyun/aliyun-oss-go-sdk@v3.0.2+incompatible/oss/crypto/crypto_bucket.go (about)

     1  package osscrypto
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"hash"
     8  	"hash/crc64"
     9  	"io"
    10  	"net/http"
    11  	"os"
    12  	"strconv"
    13  
    14  	kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
    15  	"github.com/aliyun/aliyun-oss-go-sdk/oss"
    16  )
    17  
    18  // MasterCipherManager is interface for getting master key with MatDesc(material desc)
    19  // If you may use different master keys for encrypting and decrypting objects,each master
    20  // key must have a unique, non-emtpy, unalterable MatDesc(json string format) and you must provide this interface
    21  // If you always use the same master key for encrypting and decrypting objects, MatDesc
    22  // can be empty and you don't need to provide this interface
    23  //
    24  // matDesc map[string]string:is converted by matDesc json string
    25  // return: []string  the secret key information,such as {"rsa-public-key","rsa-private-key"} or {"non-rsa-key"}
    26  type MasterCipherManager interface {
    27  	GetMasterKey(matDesc map[string]string) ([]string, error)
    28  }
    29  
    30  // ExtraCipherBuilder is interface for creating a decrypt ContentCipher with Envelope
    31  // If the objects you need to decrypt are neither encrypted with ContentCipherBuilder
    32  // you provided, nor encrypted with rsa and ali kms master keys, you must provide this interface
    33  //
    34  // ContentCipher  the interface used to decrypt objects
    35  type ExtraCipherBuilder interface {
    36  	GetDecryptCipher(envelope Envelope, cm MasterCipherManager) (ContentCipher, error)
    37  }
    38  
    39  // CryptoBucketOption CryptoBucket option such as SetAliKmsClient, SetMasterCipherManager, SetDecryptCipherManager.
    40  type CryptoBucketOption func(*CryptoBucket)
    41  
    42  // SetAliKmsClient set field AliKmsClient of CryptoBucket
    43  // If the objects you need to decrypt are encrypted with ali kms master key,but not with ContentCipherBuilder
    44  // you provided, you must provide this interface
    45  func SetAliKmsClient(client *kms.Client) CryptoBucketOption {
    46  	return func(bucket *CryptoBucket) {
    47  		bucket.AliKmsClient = client
    48  	}
    49  }
    50  
    51  // SetMasterCipherManager set field MasterCipherManager of CryptoBucket
    52  func SetMasterCipherManager(manager MasterCipherManager) CryptoBucketOption {
    53  	return func(bucket *CryptoBucket) {
    54  		bucket.MasterCipherManager = manager
    55  	}
    56  }
    57  
    58  // SetExtraCipherBuilder set field ExtraCipherBuilder of CryptoBucket
    59  func SetExtraCipherBuilder(extraBuilder ExtraCipherBuilder) CryptoBucketOption {
    60  	return func(bucket *CryptoBucket) {
    61  		bucket.ExtraCipherBuilder = extraBuilder
    62  	}
    63  }
    64  
    65  // DefaultExtraCipherBuilder is Default implementation of the ExtraCipherBuilder for rsa and kms master keys
    66  type DefaultExtraCipherBuilder struct {
    67  	AliKmsClient *kms.Client
    68  }
    69  
    70  // GetDecryptCipher is used to get ContentCipher for decrypt object
    71  func (decb *DefaultExtraCipherBuilder) GetDecryptCipher(envelope Envelope, cm MasterCipherManager) (ContentCipher, error) {
    72  	if cm == nil {
    73  		return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,MasterCipherManager is nil")
    74  	}
    75  
    76  	if envelope.CEKAlg != AesCtrAlgorithm {
    77  		return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,not supported content algorithm %s", envelope.CEKAlg)
    78  	}
    79  
    80  	if envelope.WrapAlg != RsaCryptoWrap && envelope.WrapAlg != KmsAliCryptoWrap {
    81  		return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,not supported envelope wrap algorithm %s", envelope.WrapAlg)
    82  	}
    83  
    84  	matDesc := make(map[string]string)
    85  	if envelope.MatDesc != "" {
    86  		err := json.Unmarshal([]byte(envelope.MatDesc), &matDesc)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  	}
    91  
    92  	masterKeys, err := cm.GetMasterKey(matDesc)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	var contentCipher ContentCipher
    98  	if envelope.WrapAlg == RsaCryptoWrap {
    99  		// for rsa master key
   100  		if len(masterKeys) != 2 {
   101  			return nil, fmt.Errorf("rsa keys count must be 2,now is %d", len(masterKeys))
   102  		}
   103  		rsaCipher, err := CreateMasterRsa(matDesc, masterKeys[0], masterKeys[1])
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  		aesCtrBuilder := CreateAesCtrCipher(rsaCipher)
   108  		contentCipher, err = aesCtrBuilder.ContentCipherEnv(envelope)
   109  
   110  	} else if envelope.WrapAlg == KmsAliCryptoWrap {
   111  		// for kms master key
   112  		if len(masterKeys) != 1 {
   113  			return nil, fmt.Errorf("non-rsa keys count must be 1,now is %d", len(masterKeys))
   114  		}
   115  
   116  		if decb.AliKmsClient == nil {
   117  			return nil, fmt.Errorf("aliyun kms client is nil")
   118  		}
   119  
   120  		kmsCipher, err := CreateMasterAliKms(matDesc, masterKeys[0], decb.AliKmsClient)
   121  		if err != nil {
   122  			return nil, err
   123  		}
   124  		aesCtrBuilder := CreateAesCtrCipher(kmsCipher)
   125  		contentCipher, err = aesCtrBuilder.ContentCipherEnv(envelope)
   126  	} else {
   127  		// to do
   128  		// for master keys which are neither rsa nor kms
   129  	}
   130  
   131  	return contentCipher, err
   132  }
   133  
   134  // CryptoBucket implements the operations for encrypting and decrypting objects
   135  // ContentCipherBuilder is used to encrypt and decrypt objects by default
   136  // when the object's MatDesc which you want to decrypt is emtpy or same to the
   137  // master key's MatDesc you provided in ContentCipherBuilder, sdk try to
   138  // use ContentCipherBuilder to decrypt
   139  type CryptoBucket struct {
   140  	oss.Bucket
   141  	ContentCipherBuilder ContentCipherBuilder
   142  	ExtraCipherBuilder   ExtraCipherBuilder
   143  	MasterCipherManager  MasterCipherManager
   144  	AliKmsClient         *kms.Client
   145  }
   146  
   147  // GetCryptoBucket create a client encyrption bucket
   148  func GetCryptoBucket(client *oss.Client, bucketName string, builder ContentCipherBuilder,
   149  	options ...CryptoBucketOption) (*CryptoBucket, error) {
   150  	var cryptoBucket CryptoBucket
   151  	cryptoBucket.Client = *client
   152  	cryptoBucket.BucketName = bucketName
   153  	cryptoBucket.ContentCipherBuilder = builder
   154  
   155  	for _, option := range options {
   156  		option(&cryptoBucket)
   157  	}
   158  
   159  	if cryptoBucket.ExtraCipherBuilder == nil {
   160  		cryptoBucket.ExtraCipherBuilder = &DefaultExtraCipherBuilder{AliKmsClient: cryptoBucket.AliKmsClient}
   161  	}
   162  
   163  	return &cryptoBucket, nil
   164  }
   165  
   166  // PutObject creates a new object and encyrpt it on client side when uploading to oss
   167  func (bucket CryptoBucket) PutObject(objectKey string, reader io.Reader, options ...oss.Option) error {
   168  	options = bucket.AddEncryptionUaSuffix(options)
   169  	cc, err := bucket.ContentCipherBuilder.ContentCipher()
   170  	if err != nil {
   171  		return err
   172  	}
   173  
   174  	cryptoReader, err := cc.EncryptContent(reader)
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	var request *oss.PutObjectRequest
   180  	srcLen, err := oss.GetReaderLen(reader)
   181  	if err != nil {
   182  		request = &oss.PutObjectRequest{
   183  			ObjectKey: objectKey,
   184  			Reader:    cryptoReader,
   185  		}
   186  	} else {
   187  		encryptedLen := cc.GetEncryptedLen(srcLen)
   188  		request = &oss.PutObjectRequest{
   189  			ObjectKey: objectKey,
   190  			Reader:    oss.LimitReadCloser(cryptoReader, encryptedLen),
   191  		}
   192  	}
   193  
   194  	opts := addCryptoHeaders(options, cc.GetCipherData())
   195  	resp, err := bucket.DoPutObject(request, opts)
   196  	if err != nil {
   197  		return err
   198  	}
   199  	defer resp.Body.Close()
   200  
   201  	return err
   202  }
   203  
   204  // GetObject downloads the object from oss
   205  // If the object is encrypted, sdk decrypt it automaticly
   206  func (bucket CryptoBucket) GetObject(objectKey string, options ...oss.Option) (io.ReadCloser, error) {
   207  	options = bucket.AddEncryptionUaSuffix(options)
   208  	result, err := bucket.DoGetObject(&oss.GetObjectRequest{ObjectKey: objectKey}, options)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  	return result.Response, nil
   213  }
   214  
   215  // GetObjectToFile downloads the object from oss to local file
   216  // If the object is encrypted, sdk decrypt it automaticly
   217  func (bucket CryptoBucket) GetObjectToFile(objectKey, filePath string, options ...oss.Option) error {
   218  	options = bucket.AddEncryptionUaSuffix(options)
   219  	tempFilePath := filePath + oss.TempFileSuffix
   220  
   221  	// Calls the API to actually download the object. Returns the result instance.
   222  	result, err := bucket.DoGetObject(&oss.GetObjectRequest{ObjectKey: objectKey}, options)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	defer result.Response.Close()
   227  
   228  	// If the local file does not exist, create a new one. If it exists, overwrite it.
   229  	fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, oss.FilePermMode)
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	// Copy the data to the local file path.
   235  	_, err = io.Copy(fd, result.Response.Body)
   236  	fd.Close()
   237  	if err != nil {
   238  		return err
   239  	}
   240  
   241  	// Compares the CRC value
   242  	hasRange, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderRange)
   243  	encodeOpt, _ := oss.FindOption(options, oss.HTTPHeaderAcceptEncoding, nil)
   244  	acceptEncoding := ""
   245  	if encodeOpt != nil {
   246  		acceptEncoding = encodeOpt.(string)
   247  	}
   248  	if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
   249  		result.Response.ClientCRC = result.ClientCRC.Sum64()
   250  		err = oss.CheckCRC(result.Response, "GetObjectToFile")
   251  		if err != nil {
   252  			os.Remove(tempFilePath)
   253  			return err
   254  		}
   255  	}
   256  
   257  	return os.Rename(tempFilePath, filePath)
   258  }
   259  
   260  // DoGetObject is the actual API that gets the encrypted or not encrypted object.
   261  // It's the internal function called by other public APIs.
   262  func (bucket CryptoBucket) DoGetObject(request *oss.GetObjectRequest, options []oss.Option) (*oss.GetObjectResult, error) {
   263  	options = bucket.AddEncryptionUaSuffix(options)
   264  
   265  	// first,we must head object
   266  	metaInfo, err := bucket.GetObjectDetailedMeta(request.ObjectKey)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  
   271  	isEncryptedObj := isEncryptedObject(metaInfo)
   272  	if !isEncryptedObj {
   273  		return bucket.Bucket.DoGetObject(request, options)
   274  	}
   275  
   276  	envelope, err := getEnvelopeFromHeader(metaInfo)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  
   281  	if !isValidContentAlg(envelope.CEKAlg) {
   282  		return nil, fmt.Errorf("not supported content algorithm %s,object:%s", envelope.CEKAlg, request.ObjectKey)
   283  	}
   284  
   285  	if !envelope.IsValid() {
   286  		return nil, fmt.Errorf("getEnvelopeFromHeader error,object:%s", request.ObjectKey)
   287  	}
   288  
   289  	// use ContentCipherBuilder to decrpt object by default
   290  	encryptMatDesc := bucket.ContentCipherBuilder.GetMatDesc()
   291  	var cc ContentCipher
   292  	err = nil
   293  	if envelope.MatDesc == encryptMatDesc {
   294  		cc, err = bucket.ContentCipherBuilder.ContentCipherEnv(envelope)
   295  	} else {
   296  		cc, err = bucket.ExtraCipherBuilder.GetDecryptCipher(envelope, bucket.MasterCipherManager)
   297  	}
   298  
   299  	if err != nil {
   300  		return nil, fmt.Errorf("%s,object:%s", err.Error(), request.ObjectKey)
   301  	}
   302  
   303  	discardFrontAlignLen := int64(0)
   304  	uRange, err := oss.GetRangeConfig(options)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  
   309  	if uRange != nil && uRange.HasStart {
   310  		// process range to align key size
   311  		adjustStart := adjustRangeStart(uRange.Start, cc)
   312  		discardFrontAlignLen = uRange.Start - adjustStart
   313  		if discardFrontAlignLen > 0 {
   314  			uRange.Start = adjustStart
   315  			options = oss.DeleteOption(options, oss.HTTPHeaderRange)
   316  			options = append(options, oss.NormalizedRange(oss.GetRangeString(*uRange)))
   317  		}
   318  
   319  		// seek iv
   320  		cipherData := cc.GetCipherData().Clone()
   321  		cipherData.SeekIV(uint64(adjustStart))
   322  		cc, _ = cc.Clone(cipherData)
   323  	}
   324  
   325  	params, _ := oss.GetRawParams(options)
   326  	resp, err := bucket.Do("GET", request.ObjectKey, params, options, nil, nil)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  
   331  	result := &oss.GetObjectResult{
   332  		Response: resp,
   333  	}
   334  
   335  	// CRC
   336  	var crcCalc hash.Hash64
   337  	hasRange, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderRange)
   338  	if bucket.GetConfig().IsEnableCRC && !hasRange {
   339  		crcCalc = crc64.New(oss.CrcTable())
   340  		result.ServerCRC = resp.ServerCRC
   341  		result.ClientCRC = crcCalc
   342  	}
   343  
   344  	// Progress
   345  	listener := oss.GetProgressListener(options)
   346  	contentLen, _ := strconv.ParseInt(resp.Headers.Get(oss.HTTPHeaderContentLength), 10, 64)
   347  	resp.Body = oss.TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
   348  	resp.Body, err = cc.DecryptContent(resp.Body)
   349  	if err == nil && discardFrontAlignLen > 0 {
   350  		resp.Body = &oss.DiscardReadCloser{
   351  			RC:      resp.Body,
   352  			Discard: int(discardFrontAlignLen)}
   353  	}
   354  	return result, err
   355  }
   356  
   357  // PutObjectFromFile creates a new object from the local file
   358  // the object will be encrypted automaticly on client side when uploaded to oss
   359  func (bucket CryptoBucket) PutObjectFromFile(objectKey, filePath string, options ...oss.Option) error {
   360  	options = bucket.AddEncryptionUaSuffix(options)
   361  	fd, err := os.Open(filePath)
   362  	if err != nil {
   363  		return err
   364  	}
   365  	defer fd.Close()
   366  
   367  	opts := oss.AddContentType(options, filePath, objectKey)
   368  	cc, err := bucket.ContentCipherBuilder.ContentCipher()
   369  	if err != nil {
   370  		return err
   371  	}
   372  
   373  	cryptoReader, err := cc.EncryptContent(fd)
   374  	if err != nil {
   375  		return err
   376  	}
   377  
   378  	var request *oss.PutObjectRequest
   379  	srcLen, err := oss.GetReaderLen(fd)
   380  	if err != nil {
   381  		request = &oss.PutObjectRequest{
   382  			ObjectKey: objectKey,
   383  			Reader:    cryptoReader,
   384  		}
   385  	} else {
   386  		encryptedLen := cc.GetEncryptedLen(srcLen)
   387  		request = &oss.PutObjectRequest{
   388  			ObjectKey: objectKey,
   389  			Reader:    oss.LimitReadCloser(cryptoReader, encryptedLen),
   390  		}
   391  	}
   392  
   393  	opts = addCryptoHeaders(opts, cc.GetCipherData())
   394  	resp, err := bucket.DoPutObject(request, opts)
   395  	if err != nil {
   396  		return err
   397  	}
   398  	defer resp.Body.Close()
   399  	return nil
   400  }
   401  
   402  // AppendObject please refer to Bucket.AppendObject
   403  func (bucket CryptoBucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...oss.Option) (int64, error) {
   404  	return 0, fmt.Errorf("CryptoBucket doesn't support AppendObject")
   405  }
   406  
   407  // DoAppendObject please refer to Bucket.DoAppendObject
   408  func (bucket CryptoBucket) DoAppendObject(request *oss.AppendObjectRequest, options []oss.Option) (*oss.AppendObjectResult, error) {
   409  	return nil, fmt.Errorf("CryptoBucket doesn't support DoAppendObject")
   410  }
   411  
   412  // PutObjectWithURL please refer to Bucket.PutObjectWithURL
   413  func (bucket CryptoBucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...oss.Option) error {
   414  	return fmt.Errorf("CryptoBucket doesn't support PutObjectWithURL")
   415  }
   416  
   417  // PutObjectFromFileWithURL please refer to Bucket.PutObjectFromFileWithURL
   418  func (bucket CryptoBucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...oss.Option) error {
   419  	return fmt.Errorf("CryptoBucket doesn't support PutObjectFromFileWithURL")
   420  }
   421  
   422  // DoPutObjectWithURL please refer to Bucket.DoPutObjectWithURL
   423  func (bucket CryptoBucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []oss.Option) (*oss.Response, error) {
   424  	return nil, fmt.Errorf("CryptoBucket doesn't support DoPutObjectWithURL")
   425  }
   426  
   427  // GetObjectWithURL please refer to Bucket.GetObjectWithURL
   428  func (bucket CryptoBucket) GetObjectWithURL(signedURL string, options ...oss.Option) (io.ReadCloser, error) {
   429  	return nil, fmt.Errorf("CryptoBucket doesn't support GetObjectWithURL")
   430  }
   431  
   432  // GetObjectToFileWithURL please refer to Bucket.GetObjectToFileWithURL
   433  func (bucket CryptoBucket) GetObjectToFileWithURL(signedURL, filePath string, options ...oss.Option) error {
   434  	return fmt.Errorf("CryptoBucket doesn't support GetObjectToFileWithURL")
   435  }
   436  
   437  // DoGetObjectWithURL please refer to Bucket.DoGetObjectWithURL
   438  func (bucket CryptoBucket) DoGetObjectWithURL(signedURL string, options []oss.Option) (*oss.GetObjectResult, error) {
   439  	return nil, fmt.Errorf("CryptoBucket doesn't support DoGetObjectWithURL")
   440  }
   441  
   442  // ProcessObject please refer to Bucket.ProcessObject
   443  func (bucket CryptoBucket) ProcessObject(objectKey string, process string, options ...oss.Option) (oss.ProcessObjectResult, error) {
   444  	var out oss.ProcessObjectResult
   445  	return out, fmt.Errorf("CryptoBucket doesn't support ProcessObject")
   446  }
   447  
   448  func (bucket CryptoBucket) AddEncryptionUaSuffix(options []oss.Option) []oss.Option {
   449  	var outOption []oss.Option
   450  	bSet, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderUserAgent)
   451  	if bSet || bucket.Client.Config.UserSetUa {
   452  		outOption = options
   453  		return outOption
   454  	}
   455  	outOption = append(options, oss.UserAgentHeader(bucket.Client.Config.UserAgent+"/"+EncryptionUaSuffix))
   456  	return outOption
   457  }
   458  
   459  // isEncryptedObject judge the object is encrypted or not
   460  func isEncryptedObject(headers http.Header) bool {
   461  	encrptedKey := headers.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionKey)
   462  	return len(encrptedKey) > 0
   463  }
   464  
   465  // addCryptoHeaders save Envelope information in oss meta
   466  func addCryptoHeaders(options []oss.Option, cd *CipherData) []oss.Option {
   467  	opts := []oss.Option{}
   468  
   469  	// convert content-md5
   470  	md5Option, _ := oss.FindOption(options, oss.HTTPHeaderContentMD5, nil)
   471  	if md5Option != nil {
   472  		opts = append(opts, oss.Meta(OssClientSideEncryptionUnencryptedContentMD5, md5Option.(string)))
   473  		options = oss.DeleteOption(options, oss.HTTPHeaderContentMD5)
   474  	}
   475  
   476  	// convert content-length
   477  	lenOption, _ := oss.FindOption(options, oss.HTTPHeaderContentLength, nil)
   478  	if lenOption != nil {
   479  		opts = append(opts, oss.Meta(OssClientSideEncryptionUnencryptedContentLength, lenOption.(string)))
   480  		options = oss.DeleteOption(options, oss.HTTPHeaderContentLength)
   481  	}
   482  
   483  	opts = append(opts, options...)
   484  
   485  	// matDesc
   486  	if cd.MatDesc != "" {
   487  		opts = append(opts, oss.Meta(OssClientSideEncryptionMatDesc, cd.MatDesc))
   488  	}
   489  
   490  	// encrypted key
   491  	strEncryptedKey := base64.StdEncoding.EncodeToString(cd.EncryptedKey)
   492  	opts = append(opts, oss.Meta(OssClientSideEncryptionKey, strEncryptedKey))
   493  
   494  	// encrypted iv
   495  	strEncryptedIV := base64.StdEncoding.EncodeToString(cd.EncryptedIV)
   496  	opts = append(opts, oss.Meta(OssClientSideEncryptionStart, strEncryptedIV))
   497  
   498  	// wrap alg
   499  	opts = append(opts, oss.Meta(OssClientSideEncryptionWrapAlg, cd.WrapAlgorithm))
   500  
   501  	// cek alg
   502  	opts = append(opts, oss.Meta(OssClientSideEncryptionCekAlg, cd.CEKAlgorithm))
   503  
   504  	return opts
   505  }
   506  
   507  func getEnvelopeFromHeader(header http.Header) (Envelope, error) {
   508  	var envelope Envelope
   509  	envelope.IV = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionStart)
   510  	decodedIV, err := base64.StdEncoding.DecodeString(envelope.IV)
   511  	if err != nil {
   512  		return envelope, err
   513  	}
   514  	envelope.IV = string(decodedIV)
   515  
   516  	envelope.CipherKey = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionKey)
   517  	decodedKey, err := base64.StdEncoding.DecodeString(envelope.CipherKey)
   518  	if err != nil {
   519  		return envelope, err
   520  	}
   521  	envelope.CipherKey = string(decodedKey)
   522  
   523  	envelope.MatDesc = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionMatDesc)
   524  	envelope.WrapAlg = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionWrapAlg)
   525  	envelope.CEKAlg = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionCekAlg)
   526  	envelope.UnencryptedMD5 = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionUnencryptedContentMD5)
   527  	envelope.UnencryptedContentLen = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionUnencryptedContentLength)
   528  	return envelope, err
   529  }
   530  
   531  func isValidContentAlg(algName string) bool {
   532  	// now content encyrption only support aec/ctr algorithm
   533  	return algName == AesCtrAlgorithm
   534  }
   535  
   536  func adjustRangeStart(start int64, cc ContentCipher) int64 {
   537  	alignLen := int64(cc.GetAlignLen())
   538  	return (start / alignLen) * alignLen
   539  }