jojoliang
4 years ago
12 changed files with 2081 additions and 0 deletions
-
67crypto/aes_ctr.go
-
138crypto/aes_ctr_cipher.go
-
107crypto/aes_ctr_cipher_test.go
-
69crypto/cipher.go
-
228crypto/crypto_object.go
-
76crypto/crypto_object_part.go
-
387crypto/crypto_object_part_test.go
-
385crypto/crypto_object_test.go
-
141crypto/crypto_type.go
-
87crypto/master_kms_cipher.go
-
91crypto/master_kms_cipher_test.go
-
305example/crypto/crypto_sample.go
@ -0,0 +1,67 @@ |
|||||
|
package coscrypto |
||||
|
|
||||
|
import ( |
||||
|
"crypto/aes" |
||||
|
"crypto/cipher" |
||||
|
"io" |
||||
|
) |
||||
|
|
||||
|
type aesCtr struct { |
||||
|
encrypter cipher.Stream |
||||
|
decrypter cipher.Stream |
||||
|
} |
||||
|
|
||||
|
func newAesCtr(cd CipherData) (Cipher, error) { |
||||
|
block, err := aes.NewCipher(cd.Key) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
encrypter := cipher.NewCTR(block, cd.IV) |
||||
|
decrypter := cipher.NewCTR(block, cd.IV) |
||||
|
return &aesCtr{encrypter, decrypter}, nil |
||||
|
} |
||||
|
|
||||
|
func (c *aesCtr) Encrypt(src io.Reader) io.Reader { |
||||
|
reader := &ctrEncryptReader{ |
||||
|
encrypter: c.encrypter, |
||||
|
src: src, |
||||
|
} |
||||
|
return reader |
||||
|
} |
||||
|
|
||||
|
type ctrEncryptReader struct { |
||||
|
encrypter cipher.Stream |
||||
|
src io.Reader |
||||
|
} |
||||
|
|
||||
|
func (reader *ctrEncryptReader) Read(data []byte) (int, error) { |
||||
|
plainText := make([]byte, len(data), len(data)) |
||||
|
n, err := reader.src.Read(plainText) |
||||
|
if n > 0 { |
||||
|
plainText = plainText[0:n] |
||||
|
reader.encrypter.XORKeyStream(data, plainText) |
||||
|
} |
||||
|
return n, err |
||||
|
} |
||||
|
|
||||
|
func (c *aesCtr) Decrypt(src io.Reader) io.Reader { |
||||
|
return &ctrDecryptReader{ |
||||
|
decrypter: c.decrypter, |
||||
|
src: src, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
type ctrDecryptReader struct { |
||||
|
decrypter cipher.Stream |
||||
|
src io.Reader |
||||
|
} |
||||
|
|
||||
|
func (reader *ctrDecryptReader) Read(data []byte) (int, error) { |
||||
|
cryptoText := make([]byte, len(data), len(data)) |
||||
|
n, err := reader.src.Read(cryptoText) |
||||
|
if n > 0 { |
||||
|
cryptoText = cryptoText[0:n] |
||||
|
reader.decrypter.XORKeyStream(data, cryptoText) |
||||
|
} |
||||
|
return n, err |
||||
|
} |
@ -0,0 +1,138 @@ |
|||||
|
package coscrypto |
||||
|
|
||||
|
import ( |
||||
|
"io" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
aesKeySize = 32 |
||||
|
ivSize = 16 |
||||
|
) |
||||
|
|
||||
|
type aesCtrCipherBuilder struct { |
||||
|
MasterCipher MasterCipher |
||||
|
} |
||||
|
|
||||
|
type aesCtrCipher struct { |
||||
|
CipherData CipherData |
||||
|
Cipher Cipher |
||||
|
} |
||||
|
|
||||
|
func CreateAesCtrBuilder(cipher MasterCipher) ContentCipherBuilder { |
||||
|
return aesCtrCipherBuilder{MasterCipher: cipher} |
||||
|
} |
||||
|
|
||||
|
func (builder aesCtrCipherBuilder) createCipherData() (CipherData, error) { |
||||
|
var cd CipherData |
||||
|
var err error |
||||
|
err = cd.RandomKeyIv(aesKeySize, ivSize) |
||||
|
if err != nil { |
||||
|
return cd, err |
||||
|
} |
||||
|
|
||||
|
cd.WrapAlgorithm = builder.MasterCipher.GetWrapAlgorithm() |
||||
|
cd.CEKAlgorithm = AesCtrAlgorithm |
||||
|
cd.MatDesc = builder.MasterCipher.GetMatDesc() |
||||
|
|
||||
|
// EncryptedKey
|
||||
|
cd.EncryptedKey, err = builder.MasterCipher.Encrypt(cd.Key) |
||||
|
if err != nil { |
||||
|
return cd, err |
||||
|
} |
||||
|
|
||||
|
// EncryptedIV
|
||||
|
cd.EncryptedIV, err = builder.MasterCipher.Encrypt(cd.IV) |
||||
|
if err != nil { |
||||
|
return cd, err |
||||
|
} |
||||
|
|
||||
|
return cd, nil |
||||
|
} |
||||
|
|
||||
|
func (builder aesCtrCipherBuilder) contentCipherCD(cd CipherData) (ContentCipher, error) { |
||||
|
cipher, err := newAesCtr(cd) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return &aesCtrCipher{ |
||||
|
CipherData: cd, |
||||
|
Cipher: cipher, |
||||
|
}, nil |
||||
|
} |
||||
|
|
||||
|
func (builder aesCtrCipherBuilder) ContentCipher() (ContentCipher, error) { |
||||
|
cd, err := builder.createCipherData() |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return builder.contentCipherCD(cd) |
||||
|
} |
||||
|
|
||||
|
func (builder aesCtrCipherBuilder) ContentCipherEnv(envelope Envelope) (ContentCipher, error) { |
||||
|
var cd CipherData |
||||
|
cd.EncryptedKey = make([]byte, len(envelope.CipherKey)) |
||||
|
copy(cd.EncryptedKey, []byte(envelope.CipherKey)) |
||||
|
|
||||
|
plainKey, err := builder.MasterCipher.Decrypt([]byte(envelope.CipherKey)) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
cd.Key = make([]byte, len(plainKey)) |
||||
|
copy(cd.Key, plainKey) |
||||
|
|
||||
|
cd.EncryptedIV = make([]byte, len(envelope.IV)) |
||||
|
copy(cd.EncryptedIV, []byte(envelope.IV)) |
||||
|
|
||||
|
plainIV, err := builder.MasterCipher.Decrypt([]byte(envelope.IV)) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
cd.IV = make([]byte, len(plainIV)) |
||||
|
copy(cd.IV, plainIV) |
||||
|
|
||||
|
cd.MatDesc = envelope.MatDesc |
||||
|
cd.WrapAlgorithm = envelope.WrapAlg |
||||
|
cd.CEKAlgorithm = envelope.CEKAlg |
||||
|
|
||||
|
return builder.contentCipherCD(cd) |
||||
|
} |
||||
|
|
||||
|
func (builder aesCtrCipherBuilder) GetMatDesc() string { |
||||
|
return builder.MasterCipher.GetMatDesc() |
||||
|
} |
||||
|
|
||||
|
func (cc *aesCtrCipher) EncryptContent(src io.Reader) (io.ReadCloser, error) { |
||||
|
reader := cc.Cipher.Encrypt(src) |
||||
|
return &CryptoEncrypter{Body: src, Encrypter: reader}, nil |
||||
|
} |
||||
|
|
||||
|
func (cc *aesCtrCipher) DecryptContent(src io.Reader) (io.ReadCloser, error) { |
||||
|
reader := cc.Cipher.Decrypt(src) |
||||
|
return &CryptoDecrypter{Body: src, Decrypter: reader}, nil |
||||
|
} |
||||
|
|
||||
|
func (cc *aesCtrCipher) GetCipherData() *CipherData { |
||||
|
return &(cc.CipherData) |
||||
|
} |
||||
|
|
||||
|
func (cc *aesCtrCipher) GetEncryptedLen(plainTextLen int64) int64 { |
||||
|
return plainTextLen |
||||
|
} |
||||
|
|
||||
|
func (cc *aesCtrCipher) GetAlignLen() int { |
||||
|
return len(cc.CipherData.IV) |
||||
|
} |
||||
|
|
||||
|
func (cc *aesCtrCipher) Clone(cd CipherData) (ContentCipher, error) { |
||||
|
cipher, err := newAesCtr(cd) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return &aesCtrCipher{ |
||||
|
CipherData: cd, |
||||
|
Cipher: cipher, |
||||
|
}, nil |
||||
|
} |
@ -0,0 +1,107 @@ |
|||||
|
package coscrypto_test |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"crypto/aes" |
||||
|
"crypto/cipher" |
||||
|
"crypto/rand" |
||||
|
"github.com/stretchr/testify/assert" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5/crypto" |
||||
|
"io/ioutil" |
||||
|
math_rand "math/rand" |
||||
|
) |
||||
|
|
||||
|
type EmptyMasterCipher struct{} |
||||
|
|
||||
|
func (mc EmptyMasterCipher) Encrypt(b []byte) ([]byte, error) { |
||||
|
return b, nil |
||||
|
} |
||||
|
func (mc EmptyMasterCipher) Decrypt(b []byte) ([]byte, error) { |
||||
|
return b, nil |
||||
|
} |
||||
|
func (mc EmptyMasterCipher) GetWrapAlgorithm() string { |
||||
|
return "Test/EmptyWrapAlgo" |
||||
|
} |
||||
|
func (mc EmptyMasterCipher) GetMatDesc() string { |
||||
|
return "Empty Desc" |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestCryptoObjectService_EncryptAndDecrypt() { |
||||
|
var masterCipher EmptyMasterCipher |
||||
|
builder := coscrypto.CreateAesCtrBuilder(masterCipher) |
||||
|
|
||||
|
contentCipher, err := builder.ContentCipher() |
||||
|
assert.Nil(s.T(), err, "CryptoObject.CreateAesCtrBuilder Failed") |
||||
|
|
||||
|
dataSize := math_rand.Int63n(1024 * 1024 * 32) |
||||
|
originData := make([]byte, dataSize) |
||||
|
rand.Read(originData) |
||||
|
// 加密
|
||||
|
r1 := bytes.NewReader(originData) |
||||
|
reader1, err := contentCipher.EncryptContent(r1) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.contentCipher.Encrypt Failed") |
||||
|
encryptedData, err := ioutil.ReadAll(reader1) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.Read Failed") |
||||
|
|
||||
|
// 解密
|
||||
|
r2 := bytes.NewReader(encryptedData) |
||||
|
reader2, err := contentCipher.DecryptContent(r2) |
||||
|
decryptedData, err := ioutil.ReadAll(reader2) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.Read Failed") |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestCryptoObjectService_Encrypt() { |
||||
|
var masterCipher EmptyMasterCipher |
||||
|
builder := coscrypto.CreateAesCtrBuilder(masterCipher) |
||||
|
|
||||
|
contentCipher, err := builder.ContentCipher() |
||||
|
assert.Nil(s.T(), err, "CryptoObject.CreateAesCtrBuilder Failed") |
||||
|
|
||||
|
dataSize := math_rand.Int63n(1024 * 1024 * 32) |
||||
|
originData := make([]byte, dataSize) |
||||
|
rand.Read(originData) |
||||
|
|
||||
|
// 加密
|
||||
|
r := bytes.NewReader(originData) |
||||
|
reader, err := contentCipher.EncryptContent(r) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.contentCipher.Encrypt Failed") |
||||
|
encryptedData, err := ioutil.ReadAll(reader) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.Read Failed") |
||||
|
|
||||
|
// 直接解密
|
||||
|
cd := contentCipher.GetCipherData() |
||||
|
block, err := aes.NewCipher(cd.Key) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.NewCipher Failed") |
||||
|
decrypter := cipher.NewCTR(block, cd.IV) |
||||
|
decryptedData := make([]byte, len(originData)) |
||||
|
decrypter.XORKeyStream(decryptedData, encryptedData) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestCryptoObjectService_Decrypt() { |
||||
|
var masterCipher EmptyMasterCipher |
||||
|
builder := coscrypto.CreateAesCtrBuilder(masterCipher) |
||||
|
|
||||
|
contentCipher, err := builder.ContentCipher() |
||||
|
assert.Nil(s.T(), err, "CryptoObject.CreateAesCtrBuilder Failed") |
||||
|
dataSize := math_rand.Int63n(1024 * 1024 * 32) |
||||
|
originData := make([]byte, dataSize) |
||||
|
rand.Read(originData) |
||||
|
|
||||
|
// 直接加密
|
||||
|
cd := contentCipher.GetCipherData() |
||||
|
block, err := aes.NewCipher(cd.Key) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.NewCipher Failed") |
||||
|
encrypter := cipher.NewCTR(block, cd.IV) |
||||
|
encryptedData := make([]byte, len(originData)) |
||||
|
encrypter.XORKeyStream(encryptedData, originData) |
||||
|
|
||||
|
// 解密
|
||||
|
r := bytes.NewReader(encryptedData) |
||||
|
reader, err := contentCipher.DecryptContent(r) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.contentCipher.Encrypt Failed") |
||||
|
decryptedData, err := ioutil.ReadAll(reader) |
||||
|
assert.Nil(s.T(), err, "CryptoObject.Read Failed") |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
} |
@ -0,0 +1,69 @@ |
|||||
|
package coscrypto |
||||
|
|
||||
|
import ( |
||||
|
"io" |
||||
|
) |
||||
|
|
||||
|
// Cipher is interface for encryption or decryption of an object
|
||||
|
type Cipher interface { |
||||
|
Encrypter |
||||
|
Decrypter |
||||
|
} |
||||
|
|
||||
|
// Encrypter is interface with only encrypt method
|
||||
|
type Encrypter interface { |
||||
|
Encrypt(io.Reader) io.Reader |
||||
|
} |
||||
|
|
||||
|
// Decrypter is interface with only decrypt method
|
||||
|
type Decrypter interface { |
||||
|
Decrypt(io.Reader) io.Reader |
||||
|
} |
||||
|
|
||||
|
// CryptoEncrypter provides close method for Encrypter
|
||||
|
type CryptoEncrypter struct { |
||||
|
Body io.Reader |
||||
|
Encrypter io.Reader |
||||
|
isClosed bool |
||||
|
} |
||||
|
|
||||
|
// Close lets the CryptoEncrypter satisfy io.ReadCloser interface
|
||||
|
func (rc *CryptoEncrypter) Close() error { |
||||
|
rc.isClosed = true |
||||
|
if closer, ok := rc.Body.(io.ReadCloser); ok { |
||||
|
return closer.Close() |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Read lets the CryptoEncrypter satisfy io.ReadCloser interface
|
||||
|
func (rc *CryptoEncrypter) Read(b []byte) (int, error) { |
||||
|
if rc.isClosed { |
||||
|
return 0, io.EOF |
||||
|
} |
||||
|
return rc.Encrypter.Read(b) |
||||
|
} |
||||
|
|
||||
|
// CryptoDecrypter provides close method for Decrypter
|
||||
|
type CryptoDecrypter struct { |
||||
|
Body io.Reader |
||||
|
Decrypter io.Reader |
||||
|
isClosed bool |
||||
|
} |
||||
|
|
||||
|
// Close lets the CryptoDecrypter satisfy io.ReadCloser interface
|
||||
|
func (rc *CryptoDecrypter) Close() error { |
||||
|
rc.isClosed = true |
||||
|
if closer, ok := rc.Body.(io.ReadCloser); ok { |
||||
|
return closer.Close() |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Read lets the CryptoDecrypter satisfy io.ReadCloser interface
|
||||
|
func (rc *CryptoDecrypter) Read(b []byte) (int, error) { |
||||
|
if rc.isClosed { |
||||
|
return 0, io.EOF |
||||
|
} |
||||
|
return rc.Decrypter.Read(b) |
||||
|
} |
@ -0,0 +1,228 @@ |
|||||
|
package coscrypto |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"fmt" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5" |
||||
|
"io" |
||||
|
"net/http" |
||||
|
"os" |
||||
|
"strconv" |
||||
|
) |
||||
|
|
||||
|
type CryptoObjectService struct { |
||||
|
*cos.ObjectService |
||||
|
cryptoClient *CryptoClient |
||||
|
} |
||||
|
|
||||
|
type CryptoClient struct { |
||||
|
*cos.Client |
||||
|
Object *CryptoObjectService |
||||
|
ContentCipherBuilder ContentCipherBuilder |
||||
|
|
||||
|
userAgent string |
||||
|
} |
||||
|
|
||||
|
func NewCryptoClient(client *cos.Client, masterCipher MasterCipher) *CryptoClient { |
||||
|
cc := &CryptoClient{ |
||||
|
Client: client, |
||||
|
Object: &CryptoObjectService{ |
||||
|
client.Object, |
||||
|
nil, |
||||
|
}, |
||||
|
ContentCipherBuilder: CreateAesCtrBuilder(masterCipher), |
||||
|
} |
||||
|
cc.userAgent = cc.Client.UserAgent + "/" + EncryptionUaSuffix |
||||
|
cc.Object.cryptoClient = cc |
||||
|
|
||||
|
return cc |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) Put(ctx context.Context, name string, r io.Reader, opt *cos.ObjectPutOptions) (*cos.Response, error) { |
||||
|
cc, err := s.cryptoClient.ContentCipherBuilder.ContentCipher() |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
reader, err := cc.EncryptContent(r) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
opt = cos.CloneObjectPutOptions(opt) |
||||
|
totalBytes, err := cos.GetReaderLen(r) |
||||
|
if err != nil && opt != nil && opt.Listener != nil && opt.ContentLength == 0 { |
||||
|
return nil, err |
||||
|
} |
||||
|
if err == nil { |
||||
|
if opt != nil && opt.ContentLength == 0 { |
||||
|
// 如果未设置Listener, 非bytes.Buffer/bytes.Reader/strings.Reader由用户指定Contength
|
||||
|
if opt.Listener != nil || cos.IsLenReader(r) { |
||||
|
opt.ContentLength = totalBytes |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if opt.XOptionHeader == nil { |
||||
|
opt.XOptionHeader = &http.Header{} |
||||
|
} |
||||
|
if opt.ContentMD5 != "" { |
||||
|
opt.XOptionHeader.Add(COSClientSideEncryptionUnencryptedContentMD5, opt.ContentMD5) |
||||
|
opt.ContentMD5 = "" |
||||
|
} |
||||
|
if opt.ContentLength != 0 { |
||||
|
opt.XOptionHeader.Add(COSClientSideEncryptionUnencryptedContentLength, strconv.FormatInt(opt.ContentLength, 10)) |
||||
|
opt.ContentLength = cc.GetEncryptedLen(opt.ContentLength) |
||||
|
} |
||||
|
addCryptoHeaders(opt.XOptionHeader, cc.GetCipherData()) |
||||
|
|
||||
|
return s.ObjectService.Put(ctx, name, reader, opt) |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) PutFromFile(ctx context.Context, name, filePath string, opt *cos.ObjectPutOptions) (resp *cos.Response, err error) { |
||||
|
nr := 0 |
||||
|
for nr < 3 { |
||||
|
fd, e := os.Open(filePath) |
||||
|
if e != nil { |
||||
|
err = e |
||||
|
return |
||||
|
} |
||||
|
resp, err = s.Put(ctx, name, fd, opt) |
||||
|
if err != nil { |
||||
|
nr++ |
||||
|
fd.Close() |
||||
|
continue |
||||
|
} |
||||
|
fd.Close() |
||||
|
break |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) Get(ctx context.Context, name string, opt *cos.ObjectGetOptions) (*cos.Response, error) { |
||||
|
meta, err := s.ObjectService.Head(ctx, name, nil) |
||||
|
if err != nil { |
||||
|
return meta, err |
||||
|
} |
||||
|
_isEncrypted := isEncrypted(&meta.Header) |
||||
|
if !_isEncrypted { |
||||
|
return s.ObjectService.Get(ctx, name, opt) |
||||
|
} |
||||
|
|
||||
|
envelope := getEnvelopeFromHeader(&meta.Header) |
||||
|
if !envelope.IsValid() { |
||||
|
return nil, fmt.Errorf("get envelope from header failed, object:%v", name) |
||||
|
} |
||||
|
encryptMatDesc := s.cryptoClient.ContentCipherBuilder.GetMatDesc() |
||||
|
if envelope.MatDesc != encryptMatDesc { |
||||
|
return nil, fmt.Errorf("provided master cipher error, want:%v, return:%v, object:%v", encryptMatDesc, envelope.MatDesc, name) |
||||
|
} |
||||
|
|
||||
|
cc, err := s.cryptoClient.ContentCipherBuilder.ContentCipherEnv(envelope) |
||||
|
if err != nil { |
||||
|
return nil, fmt.Errorf("get content cipher from envelope failed: %v, object:%v", err, name) |
||||
|
} |
||||
|
|
||||
|
optRange, err := cos.GetRangeOptions(opt) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
discardAlignLen := int64(0) |
||||
|
// Range请求
|
||||
|
if optRange != nil && optRange.HasStart { |
||||
|
// 加密block对齐
|
||||
|
adjustStart := adjustRangeStart(optRange.Start, int64(cc.GetAlignLen())) |
||||
|
discardAlignLen = optRange.Start - adjustStart |
||||
|
if discardAlignLen > 0 { |
||||
|
optRange.Start = adjustStart |
||||
|
opt = cos.CloneObjectGetOptions(opt) |
||||
|
opt.Range = cos.FormatRangeOptions(optRange) |
||||
|
} |
||||
|
|
||||
|
cd := cc.GetCipherData().Clone() |
||||
|
cd.SeekIV(uint64(adjustStart)) |
||||
|
cc, err = cc.Clone(cd) |
||||
|
if err != nil { |
||||
|
return nil, fmt.Errorf("ContentCipher Clone failed:%v, bject:%v", err, name) |
||||
|
} |
||||
|
} |
||||
|
resp, err := s.ObjectService.Get(ctx, name, opt) |
||||
|
if err != nil { |
||||
|
return resp, err |
||||
|
} |
||||
|
resp.Body, err = cc.DecryptContent(resp.Body) |
||||
|
if err != nil { |
||||
|
return resp, err |
||||
|
} |
||||
|
// 抛弃多读取的数据
|
||||
|
if discardAlignLen > 0 { |
||||
|
resp.Body = &cos.DiscardReadCloser{ |
||||
|
RC: resp.Body, |
||||
|
Discard: int(discardAlignLen), |
||||
|
} |
||||
|
} |
||||
|
return resp, err |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) GetToFile(ctx context.Context, name, localpath string, opt *cos.ObjectGetOptions) (*cos.Response, error) { |
||||
|
resp, err := s.Get(ctx, name, opt) |
||||
|
if err != nil { |
||||
|
return resp, err |
||||
|
} |
||||
|
defer resp.Body.Close() |
||||
|
|
||||
|
// If file exist, overwrite it
|
||||
|
fd, err := os.OpenFile(localpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660) |
||||
|
if err != nil { |
||||
|
return resp, err |
||||
|
} |
||||
|
|
||||
|
_, err = io.Copy(fd, resp.Body) |
||||
|
fd.Close() |
||||
|
if err != nil { |
||||
|
return resp, err |
||||
|
} |
||||
|
|
||||
|
return resp, nil |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) MultiUpload(ctx context.Context, name string, filepath string, opt *cos.MultiUploadOptions) (*cos.CompleteMultipartUploadResult, *cos.Response, error) { |
||||
|
return s.Upload(ctx, name, filepath, opt) |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) Upload(ctx context.Context, name string, filepath string, opt *cos.MultiUploadOptions) (*cos.CompleteMultipartUploadResult, *cos.Response, error) { |
||||
|
return nil, nil, fmt.Errorf("CryptoObjectService doesn't support Upload Now") |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) Download(ctx context.Context, name string, filepath string, opt *cos.MultiDownloadOptions) (*cos.Response, error) { |
||||
|
return nil, fmt.Errorf("CryptoObjectService doesn't support Download Now") |
||||
|
} |
||||
|
|
||||
|
func adjustRangeStart(start int64, alignLen int64) int64 { |
||||
|
return (start / alignLen) * alignLen |
||||
|
} |
||||
|
|
||||
|
func addCryptoHeaders(header *http.Header, cd *CipherData) { |
||||
|
if cd.MatDesc != "" { |
||||
|
header.Add(COSClientSideEncryptionMatDesc, cd.MatDesc) |
||||
|
} |
||||
|
header.Add(COSClientSideEncryptionKey, string(cd.EncryptedKey)) |
||||
|
header.Add(COSClientSideEncryptionStart, string(cd.EncryptedIV)) |
||||
|
header.Add(COSClientSideEncryptionWrapAlg, cd.WrapAlgorithm) |
||||
|
header.Add(COSClientSideEncryptionCekAlg, cd.CEKAlgorithm) |
||||
|
} |
||||
|
|
||||
|
func getEnvelopeFromHeader(header *http.Header) Envelope { |
||||
|
var envelope Envelope |
||||
|
envelope.CipherKey = header.Get(COSClientSideEncryptionKey) |
||||
|
envelope.IV = header.Get(COSClientSideEncryptionStart) |
||||
|
envelope.MatDesc = header.Get(COSClientSideEncryptionMatDesc) |
||||
|
envelope.WrapAlg = header.Get(COSClientSideEncryptionWrapAlg) |
||||
|
envelope.CEKAlg = header.Get(COSClientSideEncryptionCekAlg) |
||||
|
return envelope |
||||
|
} |
||||
|
|
||||
|
func isEncrypted(header *http.Header) bool { |
||||
|
encryptedKey := header.Get(COSClientSideEncryptionKey) |
||||
|
if len(encryptedKey) > 0 { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
@ -0,0 +1,76 @@ |
|||||
|
package coscrypto |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"fmt" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5" |
||||
|
"io" |
||||
|
"net/http" |
||||
|
"strconv" |
||||
|
) |
||||
|
|
||||
|
type CryptoContext struct { |
||||
|
DataSize int64 |
||||
|
PartSize int64 |
||||
|
ContentCipher ContentCipher |
||||
|
} |
||||
|
|
||||
|
func partSizeIsValid(partSize int64, alignLen int64) bool { |
||||
|
if partSize%alignLen == 0 { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) InitiateMultipartUpload(ctx context.Context, name string, opt *cos.InitiateMultipartUploadOptions, cryptoCtx *CryptoContext) (*cos.InitiateMultipartUploadResult, *cos.Response, error) { |
||||
|
contentCipher, err := s.cryptoClient.ContentCipherBuilder.ContentCipher() |
||||
|
if err != nil { |
||||
|
return nil, nil, err |
||||
|
} |
||||
|
if !partSizeIsValid(cryptoCtx.PartSize, int64(contentCipher.GetAlignLen())) { |
||||
|
return nil, nil, fmt.Errorf("PartSize is invalid, it should be %v aligned", contentCipher.GetAlignLen()) |
||||
|
} |
||||
|
// 添加自定义头部
|
||||
|
cryptoCtx.ContentCipher = contentCipher |
||||
|
opt = cos.CloneInitiateMultipartUploadOptions(opt) |
||||
|
if opt.XOptionHeader == nil { |
||||
|
opt.XOptionHeader = &http.Header{} |
||||
|
} |
||||
|
if opt.ContentMD5 != "" { |
||||
|
opt.XOptionHeader.Add(COSClientSideEncryptionUnencryptedContentMD5, opt.ContentMD5) |
||||
|
opt.ContentMD5 = "" |
||||
|
} |
||||
|
opt.XOptionHeader.Add(COSClientSideEncryptionUnencryptedContentLength, strconv.FormatInt(cryptoCtx.DataSize, 10)) |
||||
|
addCryptoHeaders(opt.XOptionHeader, contentCipher.GetCipherData()) |
||||
|
|
||||
|
return s.ObjectService.InitiateMultipartUpload(ctx, name, opt) |
||||
|
} |
||||
|
|
||||
|
func (s *CryptoObjectService) UploadPart(ctx context.Context, name, uploadID string, partNumber int, r io.Reader, opt *cos.ObjectUploadPartOptions, cryptoCtx *CryptoContext) (*cos.Response, error) { |
||||
|
if cryptoCtx.PartSize == 0 { |
||||
|
return nil, fmt.Errorf("CryptoContext's PartSize is zero") |
||||
|
} |
||||
|
opt = cos.CloneObjectUploadPartOptions(opt) |
||||
|
if cryptoCtx.ContentCipher == nil { |
||||
|
return nil, fmt.Errorf("ContentCipher is nil, Please call the InitiateMultipartUpload") |
||||
|
} |
||||
|
totalBytes, err := cos.GetReaderLen(r) |
||||
|
if err == nil { |
||||
|
// 与 go http 保持一致, 非bytes.Buffer/bytes.Reader/strings.Reader需用户指定ContentLength
|
||||
|
if opt != nil && opt.ContentLength == 0 && cos.IsLenReader(r) { |
||||
|
opt.ContentLength = totalBytes |
||||
|
} |
||||
|
} |
||||
|
cd := cryptoCtx.ContentCipher.GetCipherData().Clone() |
||||
|
cd.SeekIV(uint64(partNumber-1) * uint64(cryptoCtx.PartSize)) |
||||
|
cc, err := cryptoCtx.ContentCipher.Clone(cd) |
||||
|
opt.ContentLength = cc.GetEncryptedLen(opt.ContentLength) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
reader, err := cc.EncryptContent(r) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return s.ObjectService.UploadPart(ctx, name, uploadID, partNumber, reader, opt) |
||||
|
} |
@ -0,0 +1,387 @@ |
|||||
|
package coscrypto_test |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"context" |
||||
|
"crypto/aes" |
||||
|
"crypto/cipher" |
||||
|
"crypto/md5" |
||||
|
"crypto/rand" |
||||
|
"encoding/base64" |
||||
|
"fmt" |
||||
|
"github.com/stretchr/testify/assert" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5/crypto" |
||||
|
"io" |
||||
|
"io/ioutil" |
||||
|
math_rand "math/rand" |
||||
|
"net/http" |
||||
|
"net/url" |
||||
|
"os" |
||||
|
"sort" |
||||
|
"sync" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func (s *CosTestSuite) TestMultiUpload_Normal() { |
||||
|
name := "test/ObjectPut" + time.Now().Format(time.RFC3339) |
||||
|
contentLength := int64(1024*1024*10 + 1) |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err := rand.Read(originData) |
||||
|
|
||||
|
cryptoCtx := coscrypto.CryptoContext{ |
||||
|
DataSize: contentLength, |
||||
|
PartSize: (contentLength / 16 / 3) * 16, |
||||
|
} |
||||
|
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "Init Failed") |
||||
|
chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize) |
||||
|
assert.Nil(s.T(), err, "Split Failed") |
||||
|
optcom := &cos.CompleteMultipartUploadOptions{} |
||||
|
for _, chunk := range chunks { |
||||
|
opt := &cos.ObjectUploadPartOptions{ |
||||
|
ContentLength: chunk.Size, |
||||
|
} |
||||
|
f := bytes.NewReader(originData[chunk.OffSet : chunk.OffSet+chunk.Size]) |
||||
|
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "UploadPart failed") |
||||
|
optcom.Parts = append(optcom.Parts, cos.Object{ |
||||
|
PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"), |
||||
|
}) |
||||
|
} |
||||
|
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom) |
||||
|
assert.Nil(s.T(), err, "Complete Failed") |
||||
|
|
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestMultiUpload_DecryptWithKey() { |
||||
|
name := "test/ObjectPut" + time.Now().Format(time.RFC3339) |
||||
|
contentLength := int64(1024*1024*10 + 1) |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err := rand.Read(originData) |
||||
|
f := bytes.NewReader(originData) |
||||
|
|
||||
|
// 分块上传
|
||||
|
cryptoCtx := coscrypto.CryptoContext{ |
||||
|
DataSize: contentLength, |
||||
|
PartSize: (contentLength / 16 / 3) * 16, |
||||
|
} |
||||
|
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "Init Failed") |
||||
|
chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize) |
||||
|
assert.Nil(s.T(), err, "Split Failed") |
||||
|
optcom := &cos.CompleteMultipartUploadOptions{} |
||||
|
for _, chunk := range chunks { |
||||
|
opt := &cos.ObjectUploadPartOptions{ |
||||
|
ContentLength: chunk.Size, |
||||
|
Listener: &cos.DefaultProgressListener{}, |
||||
|
} |
||||
|
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "UploadPart failed") |
||||
|
optcom.Parts = append(optcom.Parts, cos.Object{ |
||||
|
PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"), |
||||
|
}) |
||||
|
} |
||||
|
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom) |
||||
|
assert.Nil(s.T(), err, "Complete Failed") |
||||
|
|
||||
|
// 正常读取
|
||||
|
resp, err := s.Client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
encryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.NotEqual(s.T(), bytes.Compare(encryptedData, originData), 0, "encryptedData == originData") |
||||
|
|
||||
|
// 获取解密信息
|
||||
|
resp, err = s.CClient.Object.Head(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "HeadObject Failed") |
||||
|
cipherKey := resp.Header.Get(coscrypto.COSClientSideEncryptionKey) |
||||
|
cipherIV := resp.Header.Get(coscrypto.COSClientSideEncryptionStart) |
||||
|
key, err := s.Master.Decrypt([]byte(cipherKey)) |
||||
|
assert.Nil(s.T(), err, "Master Decrypt Failed") |
||||
|
iv, err := s.Master.Decrypt([]byte(cipherIV)) |
||||
|
assert.Nil(s.T(), err, "Master Decrypt Failed") |
||||
|
|
||||
|
// 手动解密
|
||||
|
block, err := aes.NewCipher(key) |
||||
|
assert.Nil(s.T(), err, "NewCipher Failed") |
||||
|
decrypter := cipher.NewCTR(block, iv) |
||||
|
decryptedData := make([]byte, len(originData)) |
||||
|
decrypter.XORKeyStream(decryptedData, encryptedData) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestMultiUpload_PutFromFile() { |
||||
|
name := "test/ObjectPut" + time.Now().Format(time.RFC3339) |
||||
|
filepath := "tmpfile" + time.Now().Format(time.RFC3339) |
||||
|
newfile, err := os.Create(filepath) |
||||
|
assert.Nil(s.T(), err, "Create File Failed") |
||||
|
defer os.Remove(filepath) |
||||
|
|
||||
|
contentLength := int64(1024*1024*10 + 1) |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err = rand.Read(originData) |
||||
|
newfile.Write(originData) |
||||
|
newfile.Close() |
||||
|
|
||||
|
m := md5.New() |
||||
|
m.Write(originData) |
||||
|
contentMD5 := m.Sum(nil) |
||||
|
cryptoCtx := coscrypto.CryptoContext{ |
||||
|
DataSize: contentLength, |
||||
|
PartSize: (contentLength / 16 / 3) * 16, |
||||
|
} |
||||
|
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "Init Failed") |
||||
|
_, chunks, _, err := cos.SplitFileIntoChunks(filepath, cryptoCtx.PartSize) |
||||
|
assert.Nil(s.T(), err, "Split Failed") |
||||
|
optcom := &cos.CompleteMultipartUploadOptions{} |
||||
|
var wg sync.WaitGroup |
||||
|
var mtx sync.Mutex |
||||
|
for _, chunk := range chunks { |
||||
|
wg.Add(1) |
||||
|
go func(chk cos.Chunk) { |
||||
|
defer wg.Done() |
||||
|
fd, err := os.Open(filepath) |
||||
|
assert.Nil(s.T(), err, "Open File Failed") |
||||
|
opt := &cos.ObjectUploadPartOptions{ |
||||
|
ContentLength: chk.Size, |
||||
|
} |
||||
|
fd.Seek(chk.OffSet, os.SEEK_SET) |
||||
|
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chk.Number, io.LimitReader(fd, chk.Size), opt, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "UploadPart failed") |
||||
|
mtx.Lock() |
||||
|
optcom.Parts = append(optcom.Parts, cos.Object{ |
||||
|
PartNumber: chk.Number, ETag: resp.Header.Get("ETag"), |
||||
|
}) |
||||
|
mtx.Unlock() |
||||
|
}(chunk) |
||||
|
} |
||||
|
wg.Wait() |
||||
|
sort.Sort(cos.ObjectList(optcom.Parts)) |
||||
|
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom) |
||||
|
assert.Nil(s.T(), err, "Complete Failed") |
||||
|
|
||||
|
downfile := "downfile" + time.Now().Format(time.RFC3339) |
||||
|
_, err = s.CClient.Object.GetToFile(context.Background(), name, downfile, nil) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
|
||||
|
m = md5.New() |
||||
|
fd, err := os.Open(downfile) |
||||
|
assert.Nil(s.T(), err, "Open File Failed") |
||||
|
defer os.Remove(downfile) |
||||
|
defer fd.Close() |
||||
|
io.Copy(m, fd) |
||||
|
downContentMD5 := m.Sum(nil) |
||||
|
assert.Equal(s.T(), bytes.Compare(contentMD5, downContentMD5), 0, "decryptData != originData") |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestMultiUpload_GetWithRange() { |
||||
|
name := "test/ObjectPut" + time.Now().Format(time.RFC3339) |
||||
|
filepath := "tmpfile" + time.Now().Format(time.RFC3339) |
||||
|
newfile, err := os.Create(filepath) |
||||
|
assert.Nil(s.T(), err, "Create File Failed") |
||||
|
defer os.Remove(filepath) |
||||
|
|
||||
|
contentLength := int64(1024*1024*10 + 1) |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err = rand.Read(originData) |
||||
|
newfile.Write(originData) |
||||
|
newfile.Close() |
||||
|
|
||||
|
m := md5.New() |
||||
|
m.Write(originData) |
||||
|
contentMD5 := m.Sum(nil) |
||||
|
cryptoCtx := coscrypto.CryptoContext{ |
||||
|
DataSize: contentLength, |
||||
|
PartSize: (contentLength / 16 / 3) * 16, |
||||
|
} |
||||
|
iniopt := &cos.InitiateMultipartUploadOptions{ |
||||
|
&cos.ACLHeaderOptions{ |
||||
|
XCosACL: "private", |
||||
|
}, |
||||
|
&cos.ObjectPutHeaderOptions{ |
||||
|
ContentMD5: base64.StdEncoding.EncodeToString(contentMD5), |
||||
|
XCosMetaXXX: &http.Header{}, |
||||
|
}, |
||||
|
} |
||||
|
iniopt.XCosMetaXXX.Add("x-cos-meta-isEncrypted", "true") |
||||
|
|
||||
|
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, iniopt, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "Init Failed") |
||||
|
_, chunks, _, err := cos.SplitFileIntoChunks(filepath, cryptoCtx.PartSize) |
||||
|
assert.Nil(s.T(), err, "Split Failed") |
||||
|
optcom := &cos.CompleteMultipartUploadOptions{} |
||||
|
var wg sync.WaitGroup |
||||
|
var mtx sync.Mutex |
||||
|
for _, chunk := range chunks { |
||||
|
wg.Add(1) |
||||
|
go func(chk cos.Chunk) { |
||||
|
defer wg.Done() |
||||
|
fd, err := os.Open(filepath) |
||||
|
assert.Nil(s.T(), err, "Open File Failed") |
||||
|
opt := &cos.ObjectUploadPartOptions{ |
||||
|
ContentLength: chk.Size, |
||||
|
} |
||||
|
fd.Seek(chk.OffSet, os.SEEK_SET) |
||||
|
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chk.Number, io.LimitReader(fd, chk.Size), opt, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "UploadPart failed") |
||||
|
mtx.Lock() |
||||
|
optcom.Parts = append(optcom.Parts, cos.Object{ |
||||
|
PartNumber: chk.Number, ETag: resp.Header.Get("ETag"), |
||||
|
}) |
||||
|
mtx.Unlock() |
||||
|
}(chunk) |
||||
|
} |
||||
|
wg.Wait() |
||||
|
sort.Sort(cos.ObjectList(optcom.Parts)) |
||||
|
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom) |
||||
|
assert.Nil(s.T(), err, "Complete Failed") |
||||
|
|
||||
|
// Range解密读取
|
||||
|
for i := 0; i < 10; i++ { |
||||
|
math_rand.Seed(time.Now().UnixNano()) |
||||
|
rangeStart := math_rand.Int63n(contentLength) |
||||
|
rangeEnd := rangeStart + math_rand.Int63n(contentLength-rangeStart) |
||||
|
if rangeEnd == rangeStart || rangeStart >= contentLength-1 { |
||||
|
continue |
||||
|
} |
||||
|
opt := &cos.ObjectGetOptions{ |
||||
|
Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd), |
||||
|
Listener: &cos.DefaultProgressListener{}, |
||||
|
} |
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, opt) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData[rangeStart:rangeEnd+1], decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
|
||||
|
opt := &cos.ObjectGetOptions{ |
||||
|
Listener: &cos.DefaultProgressListener{}, |
||||
|
} |
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, opt) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
assert.Equal(s.T(), resp.Header.Get("x-cos-meta-isEncrypted"), "true", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionCekAlg), "AES/CTR/NoPadding", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionWrapAlg), "COS/KMS/Crypto", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionUnencryptedContentMD5), base64.StdEncoding.EncodeToString(contentMD5), "meta data isn't consistent") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
|
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestMultiUpload_GetWithNewClient() { |
||||
|
name := "test/ObjectPut" + time.Now().Format(time.RFC3339) |
||||
|
contentLength := int64(1024*1024*10 + 1) |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err := rand.Read(originData) |
||||
|
|
||||
|
cryptoCtx := coscrypto.CryptoContext{ |
||||
|
DataSize: contentLength, |
||||
|
PartSize: (contentLength / 16 / 3) * 16, |
||||
|
} |
||||
|
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "Init Failed") |
||||
|
chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize) |
||||
|
assert.Nil(s.T(), err, "Split Failed") |
||||
|
optcom := &cos.CompleteMultipartUploadOptions{} |
||||
|
for _, chunk := range chunks { |
||||
|
opt := &cos.ObjectUploadPartOptions{ |
||||
|
ContentLength: chunk.Size, |
||||
|
} |
||||
|
f := bytes.NewReader(originData[chunk.OffSet : chunk.OffSet+chunk.Size]) |
||||
|
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx) |
||||
|
assert.Nil(s.T(), err, "UploadPart failed") |
||||
|
optcom.Parts = append(optcom.Parts, cos.Object{ |
||||
|
PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"), |
||||
|
}) |
||||
|
} |
||||
|
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom) |
||||
|
assert.Nil(s.T(), err, "Complete Failed") |
||||
|
|
||||
|
u, _ := url.Parse("https://" + kBucket + ".cos." + kRegion + ".myqcloud.com") |
||||
|
b := &cos.BaseURL{BucketURL: u} |
||||
|
c := cos.NewClient(b, &http.Client{ |
||||
|
Transport: &cos.AuthorizationTransport{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
}, |
||||
|
}) |
||||
|
{ |
||||
|
// 使用不同的MatDesc客户端读取, 期待错误
|
||||
|
material := make(map[string]string) |
||||
|
material["desc"] = "cos crypto suite test 2" |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion) |
||||
|
master, _ := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material) |
||||
|
client := coscrypto.NewCryptoClient(c, master) |
||||
|
resp, err := client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), resp, "Get Object Failed") |
||||
|
assert.NotNil(s.T(), err, "Get Object Failed") |
||||
|
} |
||||
|
|
||||
|
{ |
||||
|
// 使用相同的MatDesc客户端读取, 但KMSID不一样,期待正确,kms解密是不需要KMSID
|
||||
|
material := make(map[string]string) |
||||
|
material["desc"] = "cos crypto suite test" |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion) |
||||
|
master, _ := coscrypto.CreateMasterKMS(kmsclient, "KMSID", material) |
||||
|
client := coscrypto.NewCryptoClient(c, master) |
||||
|
resp, err := client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "Get Object Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
|
||||
|
{ |
||||
|
// 使用相同的MatDesc客户端读取, 地域不一样,期待错误
|
||||
|
material := make(map[string]string) |
||||
|
material["desc"] = "cos crypto suite test" |
||||
|
diffRegion := "ap-shanghai" |
||||
|
if diffRegion == kRegion { |
||||
|
diffRegion = "ap-guangzhou" |
||||
|
} |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), diffRegion) |
||||
|
master, _ := coscrypto.CreateMasterKMS(kmsclient, "KMSID", material) |
||||
|
client := coscrypto.NewCryptoClient(c, master) |
||||
|
resp, err := client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), resp, "Get Object Failed") |
||||
|
assert.NotNil(s.T(), err, "Get Object Failed") |
||||
|
} |
||||
|
|
||||
|
{ |
||||
|
// 使用相同的MatDesc和KMSID客户端读取, 期待正确
|
||||
|
material := make(map[string]string) |
||||
|
material["desc"] = "cos crypto suite test" |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion) |
||||
|
master, _ := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material) |
||||
|
client := coscrypto.NewCryptoClient(c, master) |
||||
|
resp, err := client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "Get Object Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
@ -0,0 +1,385 @@ |
|||||
|
package coscrypto_test |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"context" |
||||
|
"crypto/aes" |
||||
|
"crypto/cipher" |
||||
|
"crypto/md5" |
||||
|
"crypto/rand" |
||||
|
"encoding/base64" |
||||
|
"fmt" |
||||
|
"github.com/stretchr/testify/assert" |
||||
|
"github.com/stretchr/testify/suite" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5/crypto" |
||||
|
"io" |
||||
|
"io/ioutil" |
||||
|
math_rand "math/rand" |
||||
|
"net/http" |
||||
|
"net/url" |
||||
|
"os" |
||||
|
"testing" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
kAppid = 1259654469 |
||||
|
kBucket = "cosgosdktest-1259654469" |
||||
|
kRegion = "ap-guangzhou" |
||||
|
) |
||||
|
|
||||
|
type CosTestSuite struct { |
||||
|
suite.Suite |
||||
|
Client *cos.Client |
||||
|
CClient *coscrypto.CryptoClient |
||||
|
Master coscrypto.MasterCipher |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) SetupSuite() { |
||||
|
u, _ := url.Parse("https://" + kBucket + ".cos." + kRegion + ".myqcloud.com") |
||||
|
b := &cos.BaseURL{BucketURL: u} |
||||
|
s.Client = cos.NewClient(b, &http.Client{ |
||||
|
Transport: &cos.AuthorizationTransport{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
}, |
||||
|
}) |
||||
|
material := make(map[string]string) |
||||
|
material["desc"] = "cos crypto suite test" |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(s.Client.GetCredential(), kRegion) |
||||
|
s.Master, _ = coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material) |
||||
|
s.CClient = coscrypto.NewCryptoClient(s.Client, s.Master) |
||||
|
opt := &cos.BucketPutOptions{ |
||||
|
XCosACL: "public-read", |
||||
|
} |
||||
|
r, err := s.Client.Bucket.Put(context.Background(), opt) |
||||
|
if err != nil && r != nil && r.StatusCode == 409 { |
||||
|
fmt.Println("BucketAlreadyOwnedByYou") |
||||
|
} else if err != nil { |
||||
|
assert.Nil(s.T(), err, "PutBucket Failed") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestPutGetDeleteObject_DecryptWithKey_10MB() { |
||||
|
name := "test/objectPut" + time.Now().Format(time.RFC3339) |
||||
|
originData := make([]byte, 1024*1024*10+1) |
||||
|
_, err := rand.Read(originData) |
||||
|
f := bytes.NewReader(originData) |
||||
|
|
||||
|
// 加密存储
|
||||
|
_, err = s.CClient.Object.Put(context.Background(), name, f, nil) |
||||
|
assert.Nil(s.T(), err, "PutObject Failed") |
||||
|
|
||||
|
// 获取解密信息
|
||||
|
resp, err := s.CClient.Object.Head(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "HeadObject Failed") |
||||
|
cipherKey := resp.Header.Get(coscrypto.COSClientSideEncryptionKey) |
||||
|
cipherIV := resp.Header.Get(coscrypto.COSClientSideEncryptionStart) |
||||
|
key, err := s.Master.Decrypt([]byte(cipherKey)) |
||||
|
assert.Nil(s.T(), err, "Master Decrypt Failed") |
||||
|
iv, err := s.Master.Decrypt([]byte(cipherIV)) |
||||
|
assert.Nil(s.T(), err, "Master Decrypt Failed") |
||||
|
|
||||
|
// 正常读取
|
||||
|
resp, err = s.Client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
encryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.NotEqual(s.T(), bytes.Compare(encryptedData, originData), 0, "encryptedData == originData") |
||||
|
|
||||
|
// 手动解密
|
||||
|
block, err := aes.NewCipher(key) |
||||
|
assert.Nil(s.T(), err, "NewCipher Failed") |
||||
|
decrypter := cipher.NewCTR(block, iv) |
||||
|
decryptedData := make([]byte, len(originData)) |
||||
|
decrypter.XORKeyStream(decryptedData, encryptedData) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestPutGetDeleteObject_Normal_10MB() { |
||||
|
name := "test/objectPut" + time.Now().Format(time.RFC3339) |
||||
|
originData := make([]byte, 1024*1024*10+1) |
||||
|
_, err := rand.Read(originData) |
||||
|
f := bytes.NewReader(originData) |
||||
|
|
||||
|
// 加密存储
|
||||
|
_, err = s.CClient.Object.Put(context.Background(), name, f, nil) |
||||
|
assert.Nil(s.T(), err, "PutObject Failed") |
||||
|
|
||||
|
// 解密读取
|
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestPutGetDeleteObject_ZeroFile() { |
||||
|
name := "test/objectPut" + time.Now().Format(time.RFC3339) |
||||
|
// 加密存储
|
||||
|
_, err := s.CClient.Object.Put(context.Background(), name, bytes.NewReader([]byte("")), nil) |
||||
|
assert.Nil(s.T(), err, "PutObject Failed") |
||||
|
|
||||
|
// 解密读取
|
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare([]byte(""), decryptedData), 0, "decryptData != originData") |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestPutGetDeleteObject_WithMetaData() { |
||||
|
name := "test/objectPut" + time.Now().Format(time.RFC3339) |
||||
|
originData := make([]byte, 1024*1024*10+1) |
||||
|
_, err := rand.Read(originData) |
||||
|
f := bytes.NewReader(originData) |
||||
|
|
||||
|
m := md5.New() |
||||
|
m.Write(originData) |
||||
|
contentMD5 := m.Sum(nil) |
||||
|
opt := &cos.ObjectPutOptions{ |
||||
|
&cos.ACLHeaderOptions{ |
||||
|
XCosACL: "private", |
||||
|
}, |
||||
|
&cos.ObjectPutHeaderOptions{ |
||||
|
ContentLength: 1024*1024*10 + 1, |
||||
|
ContentMD5: base64.StdEncoding.EncodeToString(contentMD5), |
||||
|
XCosMetaXXX: &http.Header{}, |
||||
|
}, |
||||
|
} |
||||
|
opt.XCosMetaXXX.Add("x-cos-meta-isEncrypted", "true") |
||||
|
// 加密存储
|
||||
|
_, err = s.CClient.Object.Put(context.Background(), name, f, opt) |
||||
|
assert.Nil(s.T(), err, "PutObject Failed") |
||||
|
|
||||
|
// 解密读取
|
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
assert.Equal(s.T(), resp.Header.Get("x-cos-meta-isEncrypted"), "true", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionCekAlg), "AES/CTR/NoPadding", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionWrapAlg), "COS/KMS/Crypto", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionUnencryptedContentMD5), base64.StdEncoding.EncodeToString(contentMD5), "meta data isn't consistent") |
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestPutGetDeleteObject_ByFile() { |
||||
|
name := "test/objectPut" + time.Now().Format(time.RFC3339) |
||||
|
filepath := "tmpfile" + time.Now().Format(time.RFC3339) |
||||
|
newfile, err := os.Create(filepath) |
||||
|
assert.Nil(s.T(), err, "Create File Failed") |
||||
|
defer os.Remove(filepath) |
||||
|
|
||||
|
originData := make([]byte, 1024*1024*10+1) |
||||
|
_, err = rand.Read(originData) |
||||
|
newfile.Write(originData) |
||||
|
newfile.Close() |
||||
|
|
||||
|
m := md5.New() |
||||
|
m.Write(originData) |
||||
|
contentMD5 := m.Sum(nil) |
||||
|
opt := &cos.ObjectPutOptions{ |
||||
|
&cos.ACLHeaderOptions{ |
||||
|
XCosACL: "private", |
||||
|
}, |
||||
|
&cos.ObjectPutHeaderOptions{ |
||||
|
ContentLength: 1024*1024*10 + 1, |
||||
|
ContentMD5: base64.StdEncoding.EncodeToString(contentMD5), |
||||
|
XCosMetaXXX: &http.Header{}, |
||||
|
}, |
||||
|
} |
||||
|
opt.XCosMetaXXX.Add("x-cos-meta-isEncrypted", "true") |
||||
|
// 加密存储
|
||||
|
_, err = s.CClient.Object.PutFromFile(context.Background(), name, filepath, opt) |
||||
|
assert.Nil(s.T(), err, "PutFromFile Failed") |
||||
|
|
||||
|
// 解密读取
|
||||
|
downfile := "downfile" + time.Now().Format(time.RFC3339) |
||||
|
resp, err := s.CClient.Object.GetToFile(context.Background(), name, downfile, nil) |
||||
|
assert.Nil(s.T(), err, "GetToFile Failed") |
||||
|
assert.Equal(s.T(), resp.Header.Get("x-cos-meta-isEncrypted"), "true", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionCekAlg), "AES/CTR/NoPadding", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionWrapAlg), "COS/KMS/Crypto", "meta data isn't consistent") |
||||
|
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionUnencryptedContentMD5), base64.StdEncoding.EncodeToString(contentMD5), "meta data isn't consistent") |
||||
|
|
||||
|
fd, err := os.Open(downfile) |
||||
|
assert.Nil(s.T(), err, "Open File Failed") |
||||
|
defer os.Remove(downfile) |
||||
|
defer fd.Close() |
||||
|
m = md5.New() |
||||
|
io.Copy(m, fd) |
||||
|
downContentMD5 := m.Sum(nil) |
||||
|
assert.Equal(s.T(), bytes.Compare(contentMD5, downContentMD5), 0, "decryptData != originData") |
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestPutGetDeleteObject_DecryptWithNewClient_10MB() { |
||||
|
name := "test/objectPut" + time.Now().Format(time.RFC3339) |
||||
|
originData := make([]byte, 1024*1024*10+1) |
||||
|
_, err := rand.Read(originData) |
||||
|
f := bytes.NewReader(originData) |
||||
|
|
||||
|
// 加密存储
|
||||
|
_, err = s.CClient.Object.Put(context.Background(), name, f, nil) |
||||
|
assert.Nil(s.T(), err, "PutObject Failed") |
||||
|
|
||||
|
u, _ := url.Parse("https://" + kBucket + ".cos." + kRegion + ".myqcloud.com") |
||||
|
b := &cos.BaseURL{BucketURL: u} |
||||
|
c := cos.NewClient(b, &http.Client{ |
||||
|
Transport: &cos.AuthorizationTransport{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
}, |
||||
|
}) |
||||
|
{ |
||||
|
// 使用不同的MatDesc客户端读取, 期待错误
|
||||
|
material := make(map[string]string) |
||||
|
material["desc"] = "cos crypto suite test 2" |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion) |
||||
|
master, _ := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material) |
||||
|
client := coscrypto.NewCryptoClient(c, master) |
||||
|
resp, err := client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), resp, "Get Object Failed") |
||||
|
assert.NotNil(s.T(), err, "Get Object Failed") |
||||
|
} |
||||
|
|
||||
|
{ |
||||
|
// 使用相同的MatDesc客户端读取, 但KMSID不一样,期待正确,kms解密是不需要KMSID
|
||||
|
material := make(map[string]string) |
||||
|
material["desc"] = "cos crypto suite test" |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(s.Client.GetCredential(), kRegion) |
||||
|
master, _ := coscrypto.CreateMasterKMS(kmsclient, "KMSID", material) |
||||
|
client := coscrypto.NewCryptoClient(c, master) |
||||
|
resp, err := client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "Get Object Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
|
||||
|
{ |
||||
|
// 使用相同的MatDesc和KMSID客户端读取, 期待正确
|
||||
|
material := make(map[string]string) |
||||
|
material["desc"] = "cos crypto suite test" |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(s.Client.GetCredential(), kRegion) |
||||
|
master, _ := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material) |
||||
|
client := coscrypto.NewCryptoClient(c, master) |
||||
|
resp, err := client.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "Get Object Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestPutGetDeleteObject_RangeGet() { |
||||
|
name := "test/objectPut" + time.Now().Format(time.RFC3339) |
||||
|
contentLength := 1024*1024*10 + 1 |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err := rand.Read(originData) |
||||
|
f := bytes.NewReader(originData) |
||||
|
|
||||
|
// 加密存储
|
||||
|
_, err = s.CClient.Object.Put(context.Background(), name, f, nil) |
||||
|
assert.Nil(s.T(), err, "PutObject Failed") |
||||
|
|
||||
|
// Range解密读取
|
||||
|
for i := 0; i < 10; i++ { |
||||
|
math_rand.Seed(time.Now().UnixNano()) |
||||
|
rangeStart := math_rand.Intn(contentLength) |
||||
|
rangeEnd := rangeStart + math_rand.Intn(contentLength-rangeStart) |
||||
|
if rangeEnd == rangeStart || rangeStart >= contentLength-1 { |
||||
|
continue |
||||
|
} |
||||
|
opt := &cos.ObjectGetOptions{ |
||||
|
Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd), |
||||
|
} |
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, opt) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData[rangeStart:rangeEnd+1], decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
|
||||
|
// 解密读取
|
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, nil) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestPutGetDeleteObject_WithListenerAndRange() { |
||||
|
name := "test/objectPut" + time.Now().Format(time.RFC3339) |
||||
|
contentLength := 1024*1024*10 + 1 |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err := rand.Read(originData) |
||||
|
f := bytes.NewReader(originData) |
||||
|
|
||||
|
// 加密存储
|
||||
|
popt := &cos.ObjectPutOptions{ |
||||
|
nil, |
||||
|
&cos.ObjectPutHeaderOptions{ |
||||
|
Listener: &cos.DefaultProgressListener{}, |
||||
|
}, |
||||
|
} |
||||
|
_, err = s.CClient.Object.Put(context.Background(), name, f, popt) |
||||
|
assert.Nil(s.T(), err, "PutObject Failed") |
||||
|
|
||||
|
// Range解密读取
|
||||
|
for i := 0; i < 10; i++ { |
||||
|
math_rand.Seed(time.Now().UnixNano()) |
||||
|
rangeStart := math_rand.Intn(contentLength) |
||||
|
rangeEnd := rangeStart + math_rand.Intn(contentLength-rangeStart) |
||||
|
if rangeEnd == rangeStart || rangeStart >= contentLength-1 { |
||||
|
continue |
||||
|
} |
||||
|
opt := &cos.ObjectGetOptions{ |
||||
|
Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd), |
||||
|
Listener: &cos.DefaultProgressListener{}, |
||||
|
} |
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, opt) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData[rangeStart:rangeEnd+1], decryptedData), 0, "decryptData != originData") |
||||
|
} |
||||
|
// 解密读取
|
||||
|
opt := &cos.ObjectGetOptions{ |
||||
|
Listener: &cos.DefaultProgressListener{}, |
||||
|
} |
||||
|
resp, err := s.CClient.Object.Get(context.Background(), name, opt) |
||||
|
assert.Nil(s.T(), err, "GetObject Failed") |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData") |
||||
|
|
||||
|
_, err = s.CClient.Object.Delete(context.Background(), name) |
||||
|
assert.Nil(s.T(), err, "DeleteObject Failed") |
||||
|
} |
||||
|
|
||||
|
func TestCosTestSuite(t *testing.T) { |
||||
|
suite.Run(t, new(CosTestSuite)) |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TearDownSuite() { |
||||
|
} |
@ -0,0 +1,141 @@ |
|||||
|
package coscrypto |
||||
|
|
||||
|
import ( |
||||
|
"crypto/rand" |
||||
|
"encoding/binary" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
math_rand "math/rand" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
COSClientSideEncryptionKey string = "x-cos-meta-client-side-encryption-key" |
||||
|
COSClientSideEncryptionStart = "x-cos-meta-client-side-encryption-start" |
||||
|
COSClientSideEncryptionCekAlg = "x-cos-meta-client-side-encryption-cek-alg" |
||||
|
COSClientSideEncryptionWrapAlg = "x-cos-meta-client-side-encryption-wrap-alg" |
||||
|
COSClientSideEncryptionMatDesc = "x-cos-meta-client-side-encryption-matdesc" |
||||
|
COSClientSideEncryptionUnencryptedContentLength = "x-cos-meta-client-side-encryption-unencrypted-content-length" |
||||
|
COSClientSideEncryptionUnencryptedContentMD5 = "x-cos-meta-client-side-encryption-unencrypted-content-md5" |
||||
|
COSClientSideEncryptionDataSize = "x-cos-meta-client-side-encryption-data-size" |
||||
|
COSClientSideEncryptionPartSize = "x-cos-meta-client-side-encryption-part-size" |
||||
|
COSClientUserAgent = "User-Agent" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
CosKmsCryptoWrap = "COS/KMS/Crypto" |
||||
|
AesCtrAlgorithm = "AES/CTR/NoPadding" |
||||
|
EncryptionUaSuffix = "COSEncryptionClient" |
||||
|
) |
||||
|
|
||||
|
type MasterCipher interface { |
||||
|
Encrypt([]byte) ([]byte, error) |
||||
|
Decrypt([]byte) ([]byte, error) |
||||
|
GetWrapAlgorithm() string |
||||
|
GetMatDesc() string |
||||
|
} |
||||
|
|
||||
|
type ContentCipherBuilder interface { |
||||
|
ContentCipher() (ContentCipher, error) |
||||
|
ContentCipherEnv(Envelope) (ContentCipher, error) |
||||
|
GetMatDesc() string |
||||
|
} |
||||
|
|
||||
|
type ContentCipher interface { |
||||
|
EncryptContent(io.Reader) (io.ReadCloser, error) |
||||
|
DecryptContent(io.Reader) (io.ReadCloser, error) |
||||
|
Clone(cd CipherData) (ContentCipher, error) |
||||
|
GetEncryptedLen(int64) int64 |
||||
|
GetCipherData() *CipherData |
||||
|
GetAlignLen() int |
||||
|
} |
||||
|
|
||||
|
type Envelope struct { |
||||
|
IV string |
||||
|
CipherKey string |
||||
|
MatDesc string |
||||
|
WrapAlg string |
||||
|
CEKAlg string |
||||
|
UnencryptedMD5 string |
||||
|
UnencryptedContentLen string |
||||
|
} |
||||
|
|
||||
|
func (el Envelope) IsValid() bool { |
||||
|
return len(el.IV) > 0 && |
||||
|
len(el.CipherKey) > 0 && |
||||
|
len(el.WrapAlg) > 0 && |
||||
|
len(el.CEKAlg) > 0 |
||||
|
} |
||||
|
|
||||
|
func (el Envelope) String() string { |
||||
|
return fmt.Sprintf("IV=%s&CipherKey=%s&WrapAlg=%s&CEKAlg=%s", el.IV, el.CipherKey, el.WrapAlg, el.CEKAlg) |
||||
|
} |
||||
|
|
||||
|
type CipherData struct { |
||||
|
IV []byte |
||||
|
Key []byte |
||||
|
MatDesc string |
||||
|
WrapAlgorithm string |
||||
|
CEKAlgorithm string |
||||
|
EncryptedIV []byte |
||||
|
EncryptedKey []byte |
||||
|
} |
||||
|
|
||||
|
func (cd *CipherData) RandomKeyIv(keyLen int, ivLen int) error { |
||||
|
math_rand.Seed(time.Now().UnixNano()) |
||||
|
|
||||
|
// Key
|
||||
|
cd.Key = make([]byte, keyLen) |
||||
|
if _, err := io.ReadFull(rand.Reader, cd.Key); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
// sizeof uint64
|
||||
|
if ivLen < 8 { |
||||
|
return fmt.Errorf("ivLen:%d less than 8", ivLen) |
||||
|
} |
||||
|
|
||||
|
// IV: | nonce: 8 bytes | Serial number: 8 bytes |
|
||||
|
cd.IV = make([]byte, ivLen) |
||||
|
if _, err := io.ReadFull(rand.Reader, cd.IV[0:ivLen-8]); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
// only use 4 byte,in order not to overflow when SeekIV()
|
||||
|
randNumber := math_rand.Uint32() |
||||
|
cd.SetIV(uint64(randNumber)) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (cd *CipherData) SetIV(iv uint64) { |
||||
|
ivLen := len(cd.IV) |
||||
|
binary.BigEndian.PutUint64(cd.IV[ivLen-8:], iv) |
||||
|
} |
||||
|
|
||||
|
func (cd *CipherData) GetIV() uint64 { |
||||
|
ivLen := len(cd.IV) |
||||
|
return binary.BigEndian.Uint64(cd.IV[ivLen-8:]) |
||||
|
} |
||||
|
|
||||
|
func (cd *CipherData) SeekIV(startPos uint64) { |
||||
|
cd.SetIV(cd.GetIV() + startPos/uint64(len(cd.IV))) |
||||
|
} |
||||
|
|
||||
|
func (cd *CipherData) Clone() CipherData { |
||||
|
var cloneCd CipherData |
||||
|
cloneCd = *cd |
||||
|
|
||||
|
cloneCd.Key = make([]byte, len(cd.Key)) |
||||
|
copy(cloneCd.Key, cd.Key) |
||||
|
|
||||
|
cloneCd.IV = make([]byte, len(cd.IV)) |
||||
|
copy(cloneCd.IV, cd.IV) |
||||
|
|
||||
|
cloneCd.EncryptedIV = make([]byte, len(cd.EncryptedIV)) |
||||
|
copy(cloneCd.EncryptedIV, cd.EncryptedIV) |
||||
|
|
||||
|
cloneCd.EncryptedKey = make([]byte, len(cd.EncryptedKey)) |
||||
|
copy(cloneCd.EncryptedKey, cd.EncryptedKey) |
||||
|
|
||||
|
return cloneCd |
||||
|
} |
@ -0,0 +1,87 @@ |
|||||
|
package coscrypto |
||||
|
|
||||
|
import ( |
||||
|
"encoding/base64" |
||||
|
"encoding/json" |
||||
|
"fmt" |
||||
|
|
||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" |
||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" |
||||
|
kms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms/v20190118" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
KMSEndPoint = "kms.tencentcloudapi.com" |
||||
|
) |
||||
|
|
||||
|
type MasterKMSCipher struct { |
||||
|
Client *kms.Client |
||||
|
KmsId string |
||||
|
MatDesc string |
||||
|
} |
||||
|
|
||||
|
func NewKMSClient(cred *cos.Credential, region string) (*kms.Client, error) { |
||||
|
if cred == nil { |
||||
|
fmt.Errorf("credential is nil") |
||||
|
} |
||||
|
credential := common.NewTokenCredential( |
||||
|
cred.SecretID, |
||||
|
cred.SecretKey, |
||||
|
cred.SessionToken, |
||||
|
) |
||||
|
cpf := profile.NewClientProfile() |
||||
|
cpf.HttpProfile.Endpoint = KMSEndPoint |
||||
|
client, err := kms.NewClient(credential, region, cpf) |
||||
|
return client, err |
||||
|
} |
||||
|
|
||||
|
func CreateMasterKMS(client *kms.Client, kmsId string, desc map[string]string) (MasterCipher, error) { |
||||
|
if kmsId == "" || client == nil { |
||||
|
return nil, fmt.Errorf("KMS ID is empty or kms client is nil") |
||||
|
} |
||||
|
var kmsCipher MasterKMSCipher |
||||
|
var jdesc string |
||||
|
if len(desc) > 0 { |
||||
|
bs, err := json.Marshal(desc) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
jdesc = string(bs) |
||||
|
} |
||||
|
kmsCipher.Client = client |
||||
|
kmsCipher.KmsId = kmsId |
||||
|
kmsCipher.MatDesc = jdesc |
||||
|
return &kmsCipher, nil |
||||
|
} |
||||
|
|
||||
|
func (kc *MasterKMSCipher) Encrypt(plaintext []byte) ([]byte, error) { |
||||
|
request := kms.NewEncryptRequest() |
||||
|
request.KeyId = common.StringPtr(kc.KmsId) |
||||
|
request.EncryptionContext = common.StringPtr(kc.MatDesc) |
||||
|
request.Plaintext = common.StringPtr(base64.StdEncoding.EncodeToString(plaintext)) |
||||
|
resp, err := kc.Client.Encrypt(request) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return []byte(*resp.Response.CiphertextBlob), nil |
||||
|
} |
||||
|
|
||||
|
func (kc *MasterKMSCipher) Decrypt(ciphertext []byte) ([]byte, error) { |
||||
|
request := kms.NewDecryptRequest() |
||||
|
request.CiphertextBlob = common.StringPtr(string(ciphertext)) |
||||
|
request.EncryptionContext = common.StringPtr(kc.MatDesc) |
||||
|
resp, err := kc.Client.Decrypt(request) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return base64.StdEncoding.DecodeString(*resp.Response.Plaintext) |
||||
|
} |
||||
|
|
||||
|
func (kc *MasterKMSCipher) GetWrapAlgorithm() string { |
||||
|
return CosKmsCryptoWrap |
||||
|
} |
||||
|
|
||||
|
func (kc *MasterKMSCipher) GetMatDesc() string { |
||||
|
return kc.MatDesc |
||||
|
} |
@ -0,0 +1,91 @@ |
|||||
|
package coscrypto_test |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"crypto/rand" |
||||
|
"encoding/base64" |
||||
|
"encoding/json" |
||||
|
"github.com/stretchr/testify/assert" |
||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" |
||||
|
kms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms/v20190118" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5/crypto" |
||||
|
"os" |
||||
|
) |
||||
|
|
||||
|
func (s *CosTestSuite) TestMasterKmsCipher_TestKmsClient() { |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(&cos.Credential{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
}, kRegion) |
||||
|
|
||||
|
originData := make([]byte, 1024) |
||||
|
_, err := rand.Read(originData) |
||||
|
|
||||
|
ctx := make(map[string]string) |
||||
|
ctx["desc"] = string(originData[:10]) |
||||
|
bs, _ := json.Marshal(ctx) |
||||
|
ctxJson := string(bs) |
||||
|
enReq := kms.NewEncryptRequest() |
||||
|
enReq.KeyId = common.StringPtr(os.Getenv("KMSID")) |
||||
|
enReq.EncryptionContext = common.StringPtr(ctxJson) |
||||
|
enReq.Plaintext = common.StringPtr(base64.StdEncoding.EncodeToString(originData)) |
||||
|
enResp, err := kmsclient.Encrypt(enReq) |
||||
|
assert.Nil(s.T(), err, "Encrypt Failed") |
||||
|
encryptedData := []byte(*enResp.Response.CiphertextBlob) |
||||
|
|
||||
|
deReq := kms.NewDecryptRequest() |
||||
|
deReq.CiphertextBlob = common.StringPtr(string(encryptedData)) |
||||
|
deReq.EncryptionContext = common.StringPtr(ctxJson) |
||||
|
deResp, err := kmsclient.Decrypt(deReq) |
||||
|
assert.Nil(s.T(), err, "Decrypt Failed") |
||||
|
decryptedData, err := base64.StdEncoding.DecodeString(*deResp.Response.Plaintext) |
||||
|
assert.Nil(s.T(), err, "base64 Decode Failed") |
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "originData != decryptedData") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestMasterKmsCipher_TestNormal() { |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(&cos.Credential{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
}, kRegion) |
||||
|
|
||||
|
desc := make(map[string]string) |
||||
|
desc["test"] = "TestMasterKmsCipher_TestNormal" |
||||
|
master, err := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), desc) |
||||
|
assert.Nil(s.T(), err, "CreateMasterKMS Failed") |
||||
|
|
||||
|
originData := make([]byte, 1024) |
||||
|
_, err = rand.Read(originData) |
||||
|
|
||||
|
encryptedData, err := master.Encrypt(originData) |
||||
|
assert.Nil(s.T(), err, "Encrypt Failed") |
||||
|
|
||||
|
decryptedData, err := master.Decrypt(encryptedData) |
||||
|
assert.Nil(s.T(), err, "Decrypt Failed") |
||||
|
|
||||
|
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "originData != decryptedData") |
||||
|
} |
||||
|
|
||||
|
func (s *CosTestSuite) TestMasterKmsCipher_TestError() { |
||||
|
kmsclient, _ := coscrypto.NewKMSClient(&cos.Credential{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
}, kRegion) |
||||
|
|
||||
|
desc := make(map[string]string) |
||||
|
desc["test"] = "TestMasterKmsCipher_TestNormal" |
||||
|
master, err := coscrypto.CreateMasterKMS(kmsclient, "ErrorKMSID", desc) |
||||
|
assert.Nil(s.T(), err, "CreateMasterKMS Failed") |
||||
|
|
||||
|
originData := make([]byte, 1024) |
||||
|
_, err = rand.Read(originData) |
||||
|
|
||||
|
encryptedData, err := master.Encrypt(originData) |
||||
|
assert.NotNil(s.T(), err, "Encrypt Failed") |
||||
|
|
||||
|
decryptedData, err := master.Decrypt(encryptedData) |
||||
|
assert.NotNil(s.T(), err, "Decrypt Failed") |
||||
|
|
||||
|
assert.NotEqual(s.T(), bytes.Compare(originData, decryptedData), 0, "originData != decryptedData") |
||||
|
} |
@ -0,0 +1,305 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"context" |
||||
|
"crypto/md5" |
||||
|
"crypto/rand" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"io/ioutil" |
||||
|
math_rand "math/rand" |
||||
|
"net/http" |
||||
|
"net/url" |
||||
|
"os" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/tencentyun/cos-go-sdk-v5" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5/crypto" |
||||
|
"github.com/tencentyun/cos-go-sdk-v5/debug" |
||||
|
) |
||||
|
|
||||
|
func log_status(err error) { |
||||
|
if err == nil { |
||||
|
return |
||||
|
} |
||||
|
if cos.IsNotFoundError(err) { |
||||
|
// WARN
|
||||
|
fmt.Println("WARN: Resource is not existed") |
||||
|
} else if e, ok := cos.IsCOSError(err); ok { |
||||
|
fmt.Printf("ERROR: Code: %v\n", e.Code) |
||||
|
fmt.Printf("ERROR: Message: %v\n", e.Message) |
||||
|
fmt.Printf("ERROR: Resource: %v\n", e.Resource) |
||||
|
fmt.Printf("ERROR: RequestId: %v\n", e.RequestID) |
||||
|
// ERROR
|
||||
|
} else { |
||||
|
fmt.Printf("ERROR: %v\n", err) |
||||
|
// ERROR
|
||||
|
} |
||||
|
os.Exit(1) |
||||
|
} |
||||
|
|
||||
|
func simple_put_object() { |
||||
|
u, _ := url.Parse("https://test-1259654469.cos.ap-guangzhou.myqcloud.com") |
||||
|
b := &cos.BaseURL{BucketURL: u} |
||||
|
c := cos.NewClient(b, &http.Client{ |
||||
|
Transport: &cos.AuthorizationTransport{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
Transport: &debug.DebugRequestTransport{ |
||||
|
RequestHeader: true, |
||||
|
RequestBody: false, |
||||
|
ResponseHeader: true, |
||||
|
ResponseBody: false, |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
// Case1 上传对象
|
||||
|
name := "test/example2" |
||||
|
|
||||
|
fmt.Println("============== simple_put_object ======================") |
||||
|
// 该标识信息唯一确认一个主加密密钥, 解密时,需要传入相同的标识信息
|
||||
|
materialDesc := make(map[string]string) |
||||
|
materialDesc["desc"] = "<material information of your master encrypt key>" |
||||
|
|
||||
|
// 创建KMS客户端
|
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), "ap-guangzhou") |
||||
|
// 创建KMS主加密密钥,标识信息和主密钥一一对应
|
||||
|
kmsID := os.Getenv("KMSID") |
||||
|
masterCipher, _ := coscrypto.CreateMasterKMS(kmsclient, kmsID, materialDesc) |
||||
|
// 创建加密客户端
|
||||
|
client := coscrypto.NewCryptoClient(c, masterCipher) |
||||
|
|
||||
|
contentLength := 1024*1024*10 + 1 |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err := rand.Read(originData) |
||||
|
f := bytes.NewReader(originData) |
||||
|
// 加密上传
|
||||
|
_, err = client.Object.Put(context.Background(), name, f, nil) |
||||
|
log_status(err) |
||||
|
|
||||
|
math_rand.Seed(time.Now().UnixNano()) |
||||
|
rangeStart := math_rand.Intn(contentLength) |
||||
|
rangeEnd := rangeStart + math_rand.Intn(contentLength-rangeStart) |
||||
|
opt := &cos.ObjectGetOptions{ |
||||
|
Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd), |
||||
|
} |
||||
|
// 解密下载
|
||||
|
resp, err := client.Object.Get(context.Background(), name, opt) |
||||
|
log_status(err) |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
if bytes.Compare(decryptedData, originData[rangeStart:rangeEnd+1]) != 0 { |
||||
|
fmt.Println("Error: encryptedData != originData") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func simple_put_object_from_file() { |
||||
|
u, _ := url.Parse("https://test-1259654469.cos.ap-guangzhou.myqcloud.com") |
||||
|
b := &cos.BaseURL{BucketURL: u} |
||||
|
c := cos.NewClient(b, &http.Client{ |
||||
|
Transport: &cos.AuthorizationTransport{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
Transport: &debug.DebugRequestTransport{ |
||||
|
RequestHeader: true, |
||||
|
RequestBody: false, |
||||
|
ResponseHeader: true, |
||||
|
ResponseBody: false, |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
// Case1 上传对象
|
||||
|
name := "test/example1" |
||||
|
|
||||
|
fmt.Println("============== simple_put_object_from_file ======================") |
||||
|
// 该标识信息唯一确认一个主加密密钥, 解密时,需要传入相同的标识信息
|
||||
|
materialDesc := make(map[string]string) |
||||
|
materialDesc["desc"] = "<material information of your master encrypt key>" |
||||
|
|
||||
|
// 创建KMS客户端
|
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), "ap-guangzhou") |
||||
|
// 创建KMS主加密密钥,标识信息和主密钥一一对应
|
||||
|
kmsID := os.Getenv("KMSID") |
||||
|
masterCipher, _ := coscrypto.CreateMasterKMS(kmsclient, kmsID, materialDesc) |
||||
|
// 创建加密客户端
|
||||
|
client := coscrypto.NewCryptoClient(c, masterCipher) |
||||
|
|
||||
|
filepath := "test" |
||||
|
fd, err := os.Open(filepath) |
||||
|
log_status(err) |
||||
|
defer fd.Close() |
||||
|
m := md5.New() |
||||
|
io.Copy(m, fd) |
||||
|
originDataMD5 := m.Sum(nil) |
||||
|
|
||||
|
// 加密上传
|
||||
|
_, err = client.Object.PutFromFile(context.Background(), name, filepath, nil) |
||||
|
log_status(err) |
||||
|
|
||||
|
// 解密下载
|
||||
|
_, err = client.Object.GetToFile(context.Background(), name, "./test.download", nil) |
||||
|
log_status(err) |
||||
|
|
||||
|
fd, err = os.Open("./test.download") |
||||
|
log_status(err) |
||||
|
defer fd.Close() |
||||
|
m = md5.New() |
||||
|
io.Copy(m, fd) |
||||
|
decryptedDataMD5 := m.Sum(nil) |
||||
|
|
||||
|
if bytes.Compare(decryptedDataMD5, originDataMD5) != 0 { |
||||
|
fmt.Println("Error: encryptedData != originData") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func multi_put_object() { |
||||
|
u, _ := url.Parse("https://test-1259654469.cos.ap-guangzhou.myqcloud.com") |
||||
|
b := &cos.BaseURL{BucketURL: u} |
||||
|
c := cos.NewClient(b, &http.Client{ |
||||
|
Transport: &cos.AuthorizationTransport{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
Transport: &debug.DebugRequestTransport{ |
||||
|
RequestHeader: true, |
||||
|
RequestBody: false, |
||||
|
ResponseHeader: true, |
||||
|
ResponseBody: false, |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
// Case1 上传对象
|
||||
|
name := "test/example1" |
||||
|
|
||||
|
fmt.Println("============== multi_put_object ======================") |
||||
|
// 该标识信息唯一确认一个主加密密钥, 解密时,需要传入相同的标识信息
|
||||
|
materialDesc := make(map[string]string) |
||||
|
materialDesc["desc"] = "<material information of your master encrypt key>" |
||||
|
|
||||
|
// 创建KMS客户端
|
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), "ap-guangzhou") |
||||
|
// 创建KMS主加密密钥,标识信息和主密钥一一对应
|
||||
|
kmsID := os.Getenv("KMSID") |
||||
|
masterCipher, _ := coscrypto.CreateMasterKMS(kmsclient, kmsID, materialDesc) |
||||
|
// 创建加密客户端
|
||||
|
client := coscrypto.NewCryptoClient(c, masterCipher) |
||||
|
|
||||
|
contentLength := int64(1024*1024*10 + 1) |
||||
|
originData := make([]byte, contentLength) |
||||
|
_, err := rand.Read(originData) |
||||
|
log_status(err) |
||||
|
|
||||
|
// 分块上传
|
||||
|
cryptoCtx := coscrypto.CryptoContext{ |
||||
|
DataSize: contentLength, |
||||
|
// 每个分块需要16字节对齐
|
||||
|
PartSize: (contentLength / 16 / 3) * 16, |
||||
|
} |
||||
|
v, _, err := client.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx) |
||||
|
log_status(err) |
||||
|
// 切分数据
|
||||
|
chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize) |
||||
|
log_status(err) |
||||
|
optcom := &cos.CompleteMultipartUploadOptions{} |
||||
|
for _, chunk := range chunks { |
||||
|
opt := &cos.ObjectUploadPartOptions{ |
||||
|
ContentLength: chunk.Size, |
||||
|
} |
||||
|
f := bytes.NewReader(originData[chunk.OffSet : chunk.OffSet+chunk.Size]) |
||||
|
resp, err := client.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, f, opt, &cryptoCtx) |
||||
|
log_status(err) |
||||
|
optcom.Parts = append(optcom.Parts, cos.Object{ |
||||
|
PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"), |
||||
|
}) |
||||
|
} |
||||
|
_, _, err = client.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom) |
||||
|
log_status(err) |
||||
|
|
||||
|
resp, err := client.Object.Get(context.Background(), name, nil) |
||||
|
log_status(err) |
||||
|
defer resp.Body.Close() |
||||
|
decryptedData, _ := ioutil.ReadAll(resp.Body) |
||||
|
if bytes.Compare(decryptedData, originData) != 0 { |
||||
|
fmt.Println("Error: encryptedData != originData") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func multi_put_object_from_file() { |
||||
|
u, _ := url.Parse("https://test-1259654469.cos.ap-guangzhou.myqcloud.com") |
||||
|
b := &cos.BaseURL{BucketURL: u} |
||||
|
c := cos.NewClient(b, &http.Client{ |
||||
|
Transport: &cos.AuthorizationTransport{ |
||||
|
SecretID: os.Getenv("COS_SECRETID"), |
||||
|
SecretKey: os.Getenv("COS_SECRETKEY"), |
||||
|
Transport: &debug.DebugRequestTransport{ |
||||
|
RequestHeader: true, |
||||
|
RequestBody: false, |
||||
|
ResponseHeader: true, |
||||
|
ResponseBody: false, |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
// Case1 上传对象
|
||||
|
name := "test/example1" |
||||
|
|
||||
|
fmt.Println("============== multi_put_object_from_file ======================") |
||||
|
// 该标识信息唯一确认一个主加密密钥, 解密时,需要传入相同的标识信息
|
||||
|
materialDesc := make(map[string]string) |
||||
|
materialDesc["desc"] = "<material information of your master encrypt key>" |
||||
|
|
||||
|
// 创建KMS客户端
|
||||
|
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), "ap-guangzhou") |
||||
|
// 创建KMS主加密密钥,标识信息和主密钥一一对应
|
||||
|
kmsID := os.Getenv("KMSID") |
||||
|
masterCipher, _ := coscrypto.CreateMasterKMS(kmsclient, kmsID, materialDesc) |
||||
|
// 创建加密客户端
|
||||
|
client := coscrypto.NewCryptoClient(c, masterCipher) |
||||
|
|
||||
|
filepath := "test" |
||||
|
stat, err := os.Stat(filepath) |
||||
|
log_status(err) |
||||
|
contentLength := stat.Size() |
||||
|
|
||||
|
// 分块上传
|
||||
|
cryptoCtx := coscrypto.CryptoContext{ |
||||
|
DataSize: contentLength, |
||||
|
// 每个分块需要16字节对齐
|
||||
|
PartSize: (contentLength / 16 / 3) * 16, |
||||
|
} |
||||
|
// 切分数据
|
||||
|
_, chunks, _, err := cos.SplitFileIntoChunks(filepath, cryptoCtx.PartSize) |
||||
|
log_status(err) |
||||
|
|
||||
|
// init mulitupload
|
||||
|
v, _, err := client.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx) |
||||
|
log_status(err) |
||||
|
|
||||
|
// part upload
|
||||
|
optcom := &cos.CompleteMultipartUploadOptions{} |
||||
|
for _, chunk := range chunks { |
||||
|
fd, err := os.Open(filepath) |
||||
|
log_status(err) |
||||
|
opt := &cos.ObjectUploadPartOptions{ |
||||
|
ContentLength: chunk.Size, |
||||
|
} |
||||
|
fd.Seek(chunk.OffSet, os.SEEK_SET) |
||||
|
resp, err := client.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(fd, chunk.Size), opt, &cryptoCtx) |
||||
|
log_status(err) |
||||
|
optcom.Parts = append(optcom.Parts, cos.Object{ |
||||
|
PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"), |
||||
|
}) |
||||
|
} |
||||
|
// complete upload
|
||||
|
_, _, err = client.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom) |
||||
|
log_status(err) |
||||
|
|
||||
|
_, err = client.Object.GetToFile(context.Background(), name, "test.download", nil) |
||||
|
log_status(err) |
||||
|
} |
||||
|
|
||||
|
func main() { |
||||
|
simple_put_object() |
||||
|
simple_put_object_from_file() |
||||
|
multi_put_object() |
||||
|
multi_put_object_from_file() |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue