python开发中使用DPAPI保护数据

随着计算机安全受到越来越多的挑战,本博客新开辟一个分类,python安全开发。 本文是安全开发的第一篇文章,主要讨论如何使用DPAPI保护隐私数据。 关于DPAPI在windows官网有详细的介绍,原文链接http://msdn.microsoft.com/en-us/library/ms995355.aspx , 但是文章已经不再更新。笔者认为这仍是一个较好的保证数据安全的方法,所以在现在流行的操作系统win7和win10上进行了测试,幸运的是这套数据保护机制仍然可以正常的运行。至于DPAPI的原理,在官网原文中有详细的介绍,本文不再赘述。
下面的代码是DPAPI的python实现,仅供参考。

In [3]:
#! /usr/bin/env python
# encoding: utf-8
# Matt Clarkson, 2012

'''
DPAPI access library (http://msdn.microsoft.com/en-us/library/ms995355.aspx)
This file uses code originally created by Crusher Joe:
http://article.gmane.org/gmane.comp.python.ctypes/420
And modified by Wayne Koorts:
http://stackoverflow.com/questions/463832/using-dpapi-with-python
源代码出处  https://code.google.com/p/waf/waflib/extras/dpapi.py
本文引用了源代码,为演示作了少量修改  https://wanzhouyi.github.io
'''

from ctypes import windll, byref, cdll, Structure, POINTER, c_char, c_buffer
from ctypes.wintypes import DWORD

LocalFree = windll.kernel32.LocalFree
memcpy = cdll.msvcrt.memcpy
CryptProtectData = windll.crypt32.CryptProtectData
CryptUnprotectData = windll.crypt32.CryptUnprotectData
CRYPTPROTECT_UI_FORBIDDEN = 0x01
try:
	extra_entropy = 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'.encode('ascii')
except AttributeError:
	extra_entropy = 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'

class DATA_BLOB(Structure):
	_fields_ = [
		('cbData', DWORD),
		('pbData', POINTER(c_char))
	]

def get_data(blob_out):
	cbData = int(blob_out.cbData)
	pbData = blob_out.pbData
	buffer = c_buffer(cbData)
	memcpy(buffer, pbData, cbData)
	LocalFree(pbData);
	return buffer.raw

def dpapi_encrypt_data(input_bytes, entropy = extra_entropy):
	'''
	Encrypts data and returns byte string

	:param input_bytes: The data to be encrypted
	:type input_bytes: String or Bytes
	:param entropy: Extra entropy to add to the encryption process (optional)
	:type entropy: String or Bytes
	'''
	if not isinstance(input_bytes, bytes) or not isinstance(entropy, bytes):
		print('The inputs to dpapi must be bytes')
	buffer_in      = c_buffer(input_bytes, len(input_bytes))
	buffer_entropy = c_buffer(entropy, len(entropy))
	blob_in        = DATA_BLOB(len(input_bytes), buffer_in)
	blob_entropy   = DATA_BLOB(len(entropy), buffer_entropy)
	blob_out       = DATA_BLOB()

	if CryptProtectData(byref(blob_in), 'python_data', byref(blob_entropy),
		None, None, CRYPTPROTECT_UI_FORBIDDEN, byref(blob_out)):
		return get_data(blob_out)
	else:
		print('Failed to decrypt data')

def dpapi_decrypt_data(encrypted_bytes, entropy = extra_entropy):
	'''
	Decrypts data and returns byte string

	:param encrypted_bytes: The encrypted data
	:type encrypted_bytes: Bytes
	:param entropy: Extra entropy to add to the encryption process (optional)
	:type entropy: String or Bytes
	'''
	if not isinstance(encrypted_bytes, bytes) or not isinstance(entropy, bytes):
		print('The inputs to dpapi must be bytes')
	buffer_in      = c_buffer(encrypted_bytes, len(encrypted_bytes))
	buffer_entropy = c_buffer(entropy, len(entropy))
	blob_in        = DATA_BLOB(len(encrypted_bytes), buffer_in)
	blob_entropy   = DATA_BLOB(len(entropy), buffer_entropy)
	blob_out       = DATA_BLOB()
	if CryptUnprotectData(byref(blob_in), None, byref(blob_entropy), None,
		None, CRYPTPROTECT_UI_FORBIDDEN, byref(blob_out)):
		return get_data(blob_out)
	else:
		print('Failed to decrypt data')
if __name__ == '__main__':
    encrypted_data=dpapi_encrypt_data("abc".encode())
    print(encrypted_data)
    decrypted_data=dpapi_decrypt_data(encrypted_data)
    print(decrypted_data.decode())
b'\x01\x00\x00\x00\xd0\x8c\x9d\xdf\x01\x15\xd1\x11\x8cz\x00\xc0O\xc2\x97\xeb\x01\x00\x00\x00\x1f\xf4\xdc\xc4]{eI\x940\xd5\xfb\xfb\x0fY%\x00\x00\x00\x00\x18\x00\x00\x00p\x00y\x00t\x00h\x00o\x00n\x00_\x00d\x00a\x00t\x00a\x00\x00\x00\x10f\x00\x00\x00\x01\x00\x00 \x00\x00\x00\xb4\xf6I\'x\x92Yz\x05s\x1d\x07n">"\xf1czN\x84\xcc\xc9\xab|\xd8\xe4X\x0e\xa5\x18\x13\x00\x00\x00\x00\x0e\x80\x00\x00\x00\x02\x00\x00 \x00\x00\x00\xf2\x8e\xbcV\xa6z\x13\x1d\xd83w[\x8dl\xa9\x10\xbaf\x8aT-\xee\x9fR\xbe\x9c\x9f0u\xf2L \x10\x00\x00\x00\xcc\xe0\xfc,\x1ek\x1e~\xfb!\x1aX\xdfP\xa9E@\x00\x00\x00\xa5\xf5v\xa8\xf6qFZ\xd8\xfc\x94*\xf5.\xaeHz\x80\xf9uO\xc2\xe4\xa25\xa8|\x8e\x11k\xd1\xf4H\x8a\x13sJum]>\xe69JZ\x1d\xef\x18\xe6G&\xc5\xc7\xb4\xed_\xda<\xcb\x91\xd9\x1eh_'
abc