You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
7.2 KiB

4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package coscrypto
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "os"
  9. "strconv"
  10. "git.ouxuan.net/tommy/cos-go-sdk-v5"
  11. )
  12. type CryptoObjectService struct {
  13. *cos.ObjectService
  14. cryptoClient *CryptoClient
  15. }
  16. type CryptoClient struct {
  17. *cos.Client
  18. Object *CryptoObjectService
  19. ContentCipherBuilder ContentCipherBuilder
  20. userAgent string
  21. }
  22. func NewCryptoClient(client *cos.Client, masterCipher MasterCipher) *CryptoClient {
  23. cc := &CryptoClient{
  24. Client: client,
  25. Object: &CryptoObjectService{
  26. client.Object,
  27. nil,
  28. },
  29. ContentCipherBuilder: CreateAesCtrBuilder(masterCipher),
  30. }
  31. cc.userAgent = cc.Client.UserAgent + "/" + EncryptionUaSuffix
  32. cc.Object.cryptoClient = cc
  33. return cc
  34. }
  35. func (s *CryptoObjectService) Put(ctx context.Context, name string, r io.Reader, opt *cos.ObjectPutOptions) (*cos.Response, error) {
  36. cc, err := s.cryptoClient.ContentCipherBuilder.ContentCipher()
  37. if err != nil {
  38. return nil, err
  39. }
  40. reader, err := cc.EncryptContent(r)
  41. if err != nil {
  42. return nil, err
  43. }
  44. opt = cos.CloneObjectPutOptions(opt)
  45. totalBytes, err := cos.GetReaderLen(r)
  46. if err != nil && opt != nil && opt.Listener != nil && opt.ContentLength == 0 {
  47. return nil, err
  48. }
  49. if err == nil {
  50. if opt != nil && opt.ContentLength == 0 {
  51. // 如果未设置Listener, 非bytes.Buffer/bytes.Reader/strings.Reader由用户指定Contength
  52. if opt.Listener != nil || cos.IsLenReader(r) {
  53. opt.ContentLength = totalBytes
  54. }
  55. }
  56. }
  57. if opt.XOptionHeader == nil {
  58. opt.XOptionHeader = &http.Header{}
  59. }
  60. if opt.ContentMD5 != "" {
  61. opt.XOptionHeader.Add(COSClientSideEncryptionUnencryptedContentMD5, opt.ContentMD5)
  62. opt.ContentMD5 = ""
  63. }
  64. if opt.ContentLength != 0 {
  65. opt.XOptionHeader.Add(COSClientSideEncryptionUnencryptedContentLength, strconv.FormatInt(opt.ContentLength, 10))
  66. opt.ContentLength = cc.GetEncryptedLen(opt.ContentLength)
  67. }
  68. opt.XOptionHeader.Add(UserAgent, s.cryptoClient.userAgent)
  69. addCryptoHeaders(opt.XOptionHeader, cc.GetCipherData())
  70. return s.ObjectService.Put(ctx, name, reader, opt)
  71. }
  72. func (s *CryptoObjectService) PutFromFile(ctx context.Context, name, filePath string, opt *cos.ObjectPutOptions) (resp *cos.Response, err error) {
  73. nr := 0
  74. for nr < 3 {
  75. fd, e := os.Open(filePath)
  76. if e != nil {
  77. err = e
  78. return
  79. }
  80. resp, err = s.Put(ctx, name, fd, opt)
  81. if err != nil {
  82. nr++
  83. fd.Close()
  84. continue
  85. }
  86. fd.Close()
  87. break
  88. }
  89. return
  90. }
  91. func (s *CryptoObjectService) Get(ctx context.Context, name string, opt *cos.ObjectGetOptions, id ...string) (*cos.Response, error) {
  92. meta, err := s.ObjectService.Head(ctx, name, nil, id...)
  93. if err != nil {
  94. return meta, err
  95. }
  96. _isEncrypted := isEncrypted(&meta.Header)
  97. if !_isEncrypted {
  98. return s.ObjectService.Get(ctx, name, opt, id...)
  99. }
  100. envelope, err := getEnvelopeFromHeader(&meta.Header)
  101. if err != nil {
  102. return nil, err
  103. }
  104. if !envelope.IsValid() {
  105. return nil, fmt.Errorf("get envelope from header failed, object:%v", name)
  106. }
  107. encryptMatDesc := s.cryptoClient.ContentCipherBuilder.GetMatDesc()
  108. if envelope.MatDesc != encryptMatDesc {
  109. return nil, fmt.Errorf("provided master cipher error, want:%v, return:%v, object:%v", encryptMatDesc, envelope.MatDesc, name)
  110. }
  111. cc, err := s.cryptoClient.ContentCipherBuilder.ContentCipherEnv(envelope)
  112. if err != nil {
  113. return nil, fmt.Errorf("get content cipher from envelope failed: %v, object:%v", err, name)
  114. }
  115. opt = cos.CloneObjectGetOptions(opt)
  116. if opt.XOptionHeader == nil {
  117. opt.XOptionHeader = &http.Header{}
  118. }
  119. optRange, err := cos.GetRangeOptions(opt)
  120. if err != nil {
  121. return nil, err
  122. }
  123. discardAlignLen := int64(0)
  124. // Range请求
  125. if optRange != nil && optRange.HasStart {
  126. // 加密block对齐
  127. adjustStart := adjustRangeStart(optRange.Start, int64(cc.GetAlignLen()))
  128. discardAlignLen = optRange.Start - adjustStart
  129. if discardAlignLen > 0 {
  130. optRange.Start = adjustStart
  131. opt.Range = cos.FormatRangeOptions(optRange)
  132. }
  133. cd := cc.GetCipherData().Clone()
  134. cd.SeekIV(uint64(adjustStart))
  135. cc, err = cc.Clone(cd)
  136. if err != nil {
  137. return nil, fmt.Errorf("ContentCipher Clone failed:%v, bject:%v", err, name)
  138. }
  139. }
  140. opt.XOptionHeader.Add(UserAgent, s.cryptoClient.userAgent)
  141. resp, err := s.ObjectService.Get(ctx, name, opt, id...)
  142. if err != nil {
  143. return resp, err
  144. }
  145. resp.Body, err = cc.DecryptContent(resp.Body)
  146. if err != nil {
  147. return resp, err
  148. }
  149. // 抛弃多读取的数据
  150. if discardAlignLen > 0 {
  151. resp.Body = &cos.DiscardReadCloser{
  152. RC: resp.Body,
  153. Discard: int(discardAlignLen),
  154. }
  155. }
  156. return resp, err
  157. }
  158. func (s *CryptoObjectService) GetToFile(ctx context.Context, name, localpath string, opt *cos.ObjectGetOptions, id ...string) (*cos.Response, error) {
  159. resp, err := s.Get(ctx, name, opt, id...)
  160. if err != nil {
  161. return resp, err
  162. }
  163. defer resp.Body.Close()
  164. // If file exist, overwrite it
  165. fd, err := os.OpenFile(localpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
  166. if err != nil {
  167. return resp, err
  168. }
  169. _, err = io.Copy(fd, resp.Body)
  170. fd.Close()
  171. if err != nil {
  172. return resp, err
  173. }
  174. return resp, nil
  175. }
  176. func (s *CryptoObjectService) MultiUpload(ctx context.Context, name string, filepath string, opt *cos.MultiUploadOptions) (*cos.CompleteMultipartUploadResult, *cos.Response, error) {
  177. return s.Upload(ctx, name, filepath, opt)
  178. }
  179. func (s *CryptoObjectService) Upload(ctx context.Context, name string, filepath string, opt *cos.MultiUploadOptions) (*cos.CompleteMultipartUploadResult, *cos.Response, error) {
  180. return nil, nil, fmt.Errorf("CryptoObjectService doesn't support Upload Now")
  181. }
  182. func (s *CryptoObjectService) Download(ctx context.Context, name string, filepath string, opt *cos.MultiDownloadOptions) (*cos.Response, error) {
  183. return nil, fmt.Errorf("CryptoObjectService doesn't support Download Now")
  184. }
  185. func adjustRangeStart(start int64, alignLen int64) int64 {
  186. return (start / alignLen) * alignLen
  187. }
  188. func addCryptoHeaders(header *http.Header, cd *CipherData) {
  189. if cd.MatDesc != "" {
  190. header.Add(COSClientSideEncryptionMatDesc, cd.MatDesc)
  191. }
  192. // encrypted key
  193. strEncryptedKey := base64.StdEncoding.EncodeToString(cd.EncryptedKey)
  194. header.Add(COSClientSideEncryptionKey, strEncryptedKey)
  195. // encrypted iv
  196. strEncryptedIV := base64.StdEncoding.EncodeToString(cd.EncryptedIV)
  197. header.Add(COSClientSideEncryptionStart, strEncryptedIV)
  198. header.Add(COSClientSideEncryptionWrapAlg, cd.WrapAlgorithm)
  199. header.Add(COSClientSideEncryptionCekAlg, cd.CEKAlgorithm)
  200. }
  201. func getEnvelopeFromHeader(header *http.Header) (Envelope, error) {
  202. var envelope Envelope
  203. envelope.CipherKey = header.Get(COSClientSideEncryptionKey)
  204. decodedKey, err := base64.StdEncoding.DecodeString(envelope.CipherKey)
  205. if err != nil {
  206. return envelope, err
  207. }
  208. envelope.CipherKey = string(decodedKey)
  209. envelope.IV = header.Get(COSClientSideEncryptionStart)
  210. decodedIV, err := base64.StdEncoding.DecodeString(envelope.IV)
  211. if err != nil {
  212. return envelope, err
  213. }
  214. envelope.IV = string(decodedIV)
  215. envelope.MatDesc = header.Get(COSClientSideEncryptionMatDesc)
  216. envelope.WrapAlg = header.Get(COSClientSideEncryptionWrapAlg)
  217. envelope.CEKAlg = header.Get(COSClientSideEncryptionCekAlg)
  218. return envelope, nil
  219. }
  220. func isEncrypted(header *http.Header) bool {
  221. encryptedKey := header.Get(COSClientSideEncryptionKey)
  222. if len(encryptedKey) > 0 {
  223. return true
  224. }
  225. return false
  226. }