将文件解密为流并将流读取到熊猫(hdf 或 stata)



概述我正在尝试做的事情。 我有加密版本的文件,我需要读入熊猫。 出于几个原因,解密到流而不是文件要好得多,所以这是我下面的兴趣,尽管我也尝试解密到文件只是作为中间步骤(但这也不起作用)。

我能够让它适用于 csv,但不适用于 hdf 或 stata(我会接受适用于 hdf 或 stata 的答案,尽管两者的答案可能相同,这就是我组合在一个问题中的原因)。

加密/解密文件的代码取自另一个堆栈溢出问题(我目前找不到)。

import pandas as pd
import io
from Crypto import Random
from Crypto.Cipher import AES
def pad(s):
    return s + b"" * (AES.block_size - len(s) % AES.block_size)
def encrypt(message, key, key_size=256):
    message = pad(message)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(message)
def decrypt(ciphertext, key):
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext[AES.block_size:])
    return plaintext.rstrip(b"")
def encrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        plaintext = fo.read()
    enc = encrypt(plaintext, key)
    with open(file_name + ".enc", 'wb') as fo:
        fo.write(enc)
def decrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        ciphertext = fo.read()
    dec = decrypt(ciphertext, key)
    with open(file_name[:-4], 'wb') as fo:
        fo.write(dec)

这是我尝试扩展代码以解密到流而不是文件。

def decrypt_stream(file_name, key):
    with open(file_name, 'rb') as fo:
        ciphertext = fo.read()
    dec = decrypt(ciphertext, key)
    cipherbyte = io.BytesIO()
    cipherbyte.write(dec)
    cipherbyte.seek(0)
    return cipherbyte 

最后,下面是示例程序,其中包含尝试使其工作的示例数据:

key = 'this is an example key'[:16]
df = pd.DataFrame({ 'x':[1,2], 'y':[3,4] })
df.to_csv('test.csv',index=False)
df.to_hdf('test.h5','test',mode='w')
df.to_stata('test.dta')
encrypt_file('test.csv',key)
encrypt_file('test.h5',key)
encrypt_file('test.dta',key)
decrypt_file('test.csv.enc',key)
decrypt_file('test.h5.enc',key)
decrypt_file('test.dta.enc',key)
# csv works here but hdf and stata don't
# I'm less interested in this part but include it for completeness
df_from_file = pd.read_csv('test.csv')
df_from_file = pd.read_hdf('test.h5','test')
df_from_file = pd.read_stata('test.dta')
# csv works here but hdf and stata don't
# the hdf and stata lines below are what I really need to get working
df_from_stream = pd.read_csv( decrypt_stream('test.csv.enc',key) )
df_from_stream = pd.read_hdf( decrypt_stream('test.h5.enc',key), 'test' )
df_from_stream = pd.read_stata( decrypt_stream('test.dta.enc',key) )

不幸的是,我认为我不能再缩小这段代码了,仍然有一个完整的示例。

同样,我希望上面的所有 4 行非工作行都正常工作(hdf 和 stata 的文件和流),但我很高兴接受一个仅适用于 hdf 流或仅适用于 stata 流的答案。

此外,我对其他加密替代方案持开放态度,我只是使用了我在 SO 上找到的一些现有的基于 pycrypto 的代码。 我的工作明确要求 256 位 AES,但除此之外我是开放的,所以这个解决方案不需要专门基于 pycrypto 库或上面的特定代码示例。

有关我的设置的信息:

python: 3.4.3
pandas: 0.17.0 (anaconda 2.3.0 distribution)
mac os: 10.11.3

最大的问题是填充/取消填充方法。它假定空字符不能是实际内容的一部分。由于stata/hdf文件是二进制的,因此使用我们使用的额外字节数(编码为字符)进行填充更安全。此数字将在取消填充期间使用。

同样在目前,read_hdf不支持从类似对象之类的文件中读取,即使 API 文档声称如此。如果我们将自己限制为stata格式,则以下代码将执行您需要的操作:

import pandas as pd
import io
from Crypto import Random
from Crypto.Cipher import AES
def pad(s):
    n = AES.block_size - len(s) % AES.block_size
    return s + n * chr(n)
def unpad(s):
    return s[:-ord(s[-1])]
def encrypt(message, key, key_size=256):
    message = pad(message)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(message)
def decrypt(ciphertext, key):
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext[AES.block_size:])
    return unpad(plaintext)
def encrypt_file(file_name, key):
    with open(file_name, 'rb') as fo:
        plaintext = fo.read()
    enc = encrypt(plaintext, key)
    with open(file_name + ".enc", 'wb') as fo:
        fo.write(enc)
def decrypt_stream(file_name, key):
    with open(file_name, 'rb') as fo:
        ciphertext = fo.read()
    dec = decrypt(ciphertext, key)
    cipherbyte = io.BytesIO()
    cipherbyte.write(dec)
    cipherbyte.seek(0)
    return cipherbyte
key = 'this is an example key'[:16]
df = pd.DataFrame({
    'x': [1,2],
    'y': [3,4]
})
df.to_stata('test.dta')
encrypt_file('test.dta', key)
print pd.read_stata(decrypt_stream('test.dta.enc', key))

输出:

   index  x  y
0      0  1  3
1      1  2  4

在python 3中,您可以使用以下padunpad版本:

def pad(s):
    n = AES.block_size - len(s) % AES.block_size
    return s + bytearray([n] * n)
def unpad(s):
    return s[:-s[-1]]

.h5格式和cryptography库的情况下,对我有用的是:

from cryptography.fernet import Fernet
def read_h5_file(new_file:str, decrypted: bytes, verbose=False):
        with open(new_file, 'wb') as f:
                f.write(decrypted)
        print(f'Created {new_file}') if verbose else ''
        df = pd.read_hdf(new_file)
        os.remove(new_file)
        print(f'Deleted {new_file}') if verbose else ''
        return df
with open(path_to_file, 'rb') as f:
    data = f.read()
fernet = Fernet(key)
decrypted = fernet.decrypt(data)
new_file = './example_path/example.h5'
df = read_h5_file(new_file, decrypted, verbose=verbose)

所以我创建了一个.h5文件。阅读其内容。将其与函数一起返回。再次删除解密的文件。

也许这种方法有帮助,因为我在网上没有找到任何其他或类似的解决方案。

最新更新