Skip to content

Commit 84ab09d

Browse files
authored
添加客户端加密功能 (#132)
* add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption * add funtion of client side encryption
1 parent b7b11f4 commit 84ab09d

File tree

6 files changed

+750
-47
lines changed

6 files changed

+750
-47
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ install:
1111
- pip install pycodestyle
1212
- pip install dicttoxml
1313
- pip install crcmod
14+
- pip install pycryptodome
1415
notifications:
1516
email:
1617
recipients:
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# -*- coding=utf-8
2+
3+
import logging
4+
from qcloud_cos import CosS3Client
5+
from qcloud_cos.crypto import RSAProvider
6+
from qcloud_cos.crypto import MetaHandle
7+
from qcloud_cos.crypto import DataDecryptAdapter
8+
from .cos_exception import CosClientError
9+
from .cos_comm import *
10+
from .cos_auth import CosS3Auth
11+
logger = logging.getLogger(__name__)
12+
13+
14+
class CosEncryptionClient(CosS3Client):
15+
"""cos支持加密的客户端,封装相应请求"""
16+
def __init__(self, conf, provider, retry=1, session=None):
17+
"""初始化client对象
18+
19+
:param conf(CosConfig): 用户的配置.
20+
:param provider(BaseProvider): 客户端主密钥加密类
21+
:param retry(int): 失败重试的次数.
22+
:param session(object): http session.
23+
"""
24+
super(CosEncryptionClient, self).__init__(conf, retry, session)
25+
self.provider = provider
26+
27+
def put_object(self, Bucket, Body, Key, EnableMD5=False, **kwargs):
28+
"""单文件加密上传接口,适用于小文件,最大不得超过5GB
29+
30+
:param Bucket(string): 存储桶名称.
31+
:param Body(file|string): 上传的文件内容,类型为文件流或字节流.
32+
:param Key(string): COS路径.
33+
:param EnableMD5(bool): 是否需要SDK计算Content-MD5,打开此开关会增加上传耗时.
34+
:kwargs(dict): 设置上传的headers.
35+
:return(dict): 上传成功返回的结果,包含ETag等信息.
36+
37+
.. code-block:: python
38+
39+
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token) # 获取配置对象
40+
provider = RSAProvider()
41+
client = CosEncryptionClient(config, provider)
42+
# 上传本地文件到cos
43+
with open('test.txt', 'rb') as fp:
44+
response = client.put_object(
45+
Bucket='bucket',
46+
Body=fp,
47+
Key='test.txt'
48+
)
49+
print (response['ETag'])
50+
"""
51+
encrypt_key, encrypt_start = self.provider.init_data_cipher()
52+
meta_handle = MetaHandle(encrypt_key, encrypt_start)
53+
kwargs = meta_handle.set_object_meta(kwargs)
54+
data = self.provider.make_data_encrypt_adapter(Body)
55+
response = super(CosEncryptionClient, self).put_object(Bucket, data, Key, EnableMD5, **kwargs)
56+
return response
57+
58+
def get_object(self, Bucket, Key, **kwargs):
59+
"""单文件加密下载接口
60+
61+
:param Bucket(string): 存储桶名称.
62+
:param Key(string): COS路径.
63+
:param kwargs(dict): 设置下载的headers.
64+
:return(dict): 下载成功返回的结果,包含Body对应的StreamBody,可以获取文件流或下载文件到本地.
65+
66+
.. code-block:: python
67+
68+
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token) # 获取配置对象
69+
provider = RSAProvider()
70+
client = CosEncryptionClient(config, provider)
71+
# 下载cos上的文件到本地
72+
response = client.get_object(
73+
Bucket='bucket',
74+
Key='test.txt'
75+
)
76+
response['Body'].get_stream_to_file('local_file.txt')
77+
"""
78+
response = self.head_object(Bucket, Key, **kwargs)
79+
meta_handle = MetaHandle()
80+
encrypt_key, encrypt_start = meta_handle.get_object_meta(response)
81+
82+
headers = mapped(kwargs)
83+
offset = 0
84+
real_start = 0
85+
if 'Range' in headers:
86+
read_range = headers['Range']
87+
read_range = read_range.replace('bytes=', '').strip().split('-')
88+
if len(read_range) != 2:
89+
raise CosClientError('key:Range is wrong format, except first-last')
90+
91+
real_start = self.provider.adjust_read_offset(int(read_range[0]))
92+
headers['Range'] = 'bytes=' + str(real_start) + '-' + read_range[1]
93+
offset = int(read_range[0]) - real_start
94+
final_headers = {}
95+
params = {}
96+
for key in headers:
97+
if key.startswith("response"):
98+
params[key] = headers[key]
99+
else:
100+
final_headers[key] = headers[key]
101+
headers = final_headers
102+
103+
if 'versionId' in headers:
104+
params['versionId'] = headers['versionId']
105+
del headers['versionId']
106+
params = format_values(params)
107+
108+
url = self._conf.uri(bucket=Bucket, path=Key)
109+
logger.info("get object, url=:{url} ,headers=:{headers}, params=:{params}".format(
110+
url=url,
111+
headers=headers,
112+
params=params))
113+
rt = self.send_request(
114+
method='GET',
115+
url=url,
116+
bucket=Bucket,
117+
stream=True,
118+
auth=CosS3Auth(self._conf, Key, params=params),
119+
params=params,
120+
headers=headers)
121+
122+
self.provider.init_data_cipter_by_user(encrypt_key, encrypt_start, real_start)
123+
response['Body'] = self.provider.make_data_decrypt_adapter(rt, offset)
124+
return response
125+
126+
def create_multipart_upload(self, Bucket, Key, **kwargs):
127+
"""创建分块上传,适用于大文件上传
128+
129+
:param Bucket(string): 存储桶名称.
130+
:param Key(string): COS路径.
131+
:param kwargs(dict): 设置请求headers.
132+
:return(dict): 初始化分块上传返回的结果,包含UploadId等信息.
133+
134+
.. code-block:: python
135+
136+
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token) # 获取配置对象
137+
provider = RSAProvider()
138+
client = CosEncryptionClient(config, provider)
139+
# 创建分块上传
140+
response = client.create_multipart_upload(
141+
Bucket='bucket',
142+
Key='test.txt'
143+
)
144+
"""
145+
encrypt_key, encrypt_start = self.provider.init_data_cipher()
146+
meta_handle = MetaHandle(encrypt_key, encrypt_start)
147+
kwargs = meta_handle.set_object_meta(kwargs)
148+
response = super(CosEncryptionClient, self).create_multipart_upload(Bucket, Key, **kwargs)
149+
return response
150+
151+
def upload_part(self, Bucket, Key, Body, PartNumber, UploadId, EnableMD5=False, **kwargs):
152+
"""上传分块,单个大小不得超过5GB
153+
154+
:param Bucket(string): 存储桶名称.
155+
:param Key(string): COS路径.
156+
:param Body(file|string): 上传分块的内容,可以为文件流或者字节流.
157+
:param PartNumber(int): 上传分块的编号.
158+
:param UploadId(string): 分块上传创建的UploadId.
159+
:param kwargs(dict): 设置请求headers.
160+
:param EnableMD5(bool): 是否需要SDK计算Content-MD5,打开此开关会增加上传耗时.
161+
:return(dict): 上传成功返回的结果,包含单个分块ETag等信息.
162+
163+
.. code-block:: python
164+
165+
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token) # 获取配置对象
166+
provider = RSAProvider()
167+
client = CosEncryptionClient(config, provider)
168+
# 分块上传
169+
with open('test.txt', 'rb') as fp:
170+
data = fp.read(1024*1024)
171+
response = client.upload_part(
172+
Bucket='bucket',
173+
Body=data,
174+
Key='test.txt'
175+
)
176+
"""
177+
data = self.provider.make_data_encrypt_adapter(Body)
178+
response = super(CosEncryptionClient, self).upload_part(Bucket, Key, data, PartNumber, UploadId, EnableMD5=False, **kwargs)
179+
return response

0 commit comments

Comments
 (0)