使用 PyCrypto AES-256 加密和解密

2024-12-25 08:50:00
admin
原创
183
摘要:问题描述:我正在尝试使用 PyCrypto 构建两个函数,它们接受两个参数:消息和密钥,然后加密/解密消息。我在网上找到了几个可以帮助我的链接,但每个链接都有缺陷:codekoala 上的这个使用了 os.urandom,这是 PyCrypto 不鼓励的。此外,我提供给函数的密钥不能保证具有预期的准确长度。我...

问题描述:

我正在尝试使用 PyCrypto 构建两个函数,它们接受两个参数:消息和密钥,然后加密/解密消息。

我在网上找到了几个可以帮助我的链接,但每个链接都有缺陷:

codekoala 上的这个使用了 os.urandom,这是 PyCrypto 不鼓励的。

此外,我提供给函数的密钥不能保证具有预期的准确长度。我该怎么做才能实现这一点?

另外,有几种模式,推荐哪一种?我不知道该用哪个 :/

最后, IV到底是什么?我可以提供不同的 IV 进行加密和解密吗?或者这会返回不同的结果吗?


解决方案 1:

以下是我的实现,经过一些修复后,它对我来说是有效的。它增强了密钥和密码短语的对齐,将密钥和密码短语从 32 个字节变为 16 个字节:

import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES

class AESCipher(object):

    def __init__(self, key):
        self.bs = AES.block_size
        self.key = hashlib.sha256(key.encode()).digest()

    def encrypt(self, raw):
        raw = self._pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw.encode()))

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return AESCipher._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s)-1:])]

解决方案 2:

您可能需要以下两个函数:pad- 当输入的长度不是 BLOCK_SIZE 的倍数时,使用 pad (加密时) 和unpad- 当输入的长度不是 BLOCK_SIZE 的倍数时,使用 unpad (解密时)。

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[:-ord(s[len(s)-1:])]

所以你问的是密钥的长度?你可以使用密钥的MD5哈希值,而不是直接使用它。

另外,根据我使用 PyCrypto 的一点经验,IV 用于在输入相同时混合加密的输出,因此选择 IV 作为随机字符串,并将其用作加密输出的一部分,然后使用它来解密消息。

这是我的实现:

import base64
from Crypto.Cipher import AES
from Crypto import Random

class AESCipher:
    def __init__( self, key ):
        self.key = key

    def encrypt( self, raw ):
        raw = pad(raw)
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key, AES.MODE_CBC, iv )
        return base64.b64encode( iv + cipher.encrypt( raw ) )

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        iv = enc[:16]
        cipher = AES.new(self.key, AES.MODE_CBC, iv )
        return unpad(cipher.decrypt( enc[16:] ))

解决方案 3:

让我来回答你关于“模式”的问题。AES -256是一种分组密码。它以 32 字节密钥和 16 字节字符串(称为块)作为输入,并输出一个块。我们在操作模式下使用 AES来加密。上面的解决方案建议使用CBC,这是一个例子。另一个称为CTR,使用起来稍微容易一些:

from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto import Random

# AES supports multiple key sizes: 16 (AES128), 24 (AES192), or 32 (AES256).
key_bytes = 32

# Takes as input a 32-byte key and an arbitrary-length plaintext and returns a
# pair (iv, ciphtertext). "iv" stands for initialization vector.
def encrypt(key, plaintext):
    assert len(key) == key_bytes

    # Choose a random, 16-byte IV.
    iv = Random.new().read(AES.block_size)

    # Convert the IV to a Python integer.
    iv_int = int(binascii.hexlify(iv), 16)

    # Create a new Counter object with IV = iv_int.
    ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)

    # Create AES-CTR cipher.
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)

    # Encrypt and return IV and ciphertext.
    ciphertext = aes.encrypt(plaintext)
    return (iv, ciphertext)

# Takes as input a 32-byte key, a 16-byte IV, and a ciphertext, and outputs the
# corresponding plaintext.
def decrypt(key, iv, ciphertext):
    assert len(key) == key_bytes

    # Initialize counter for decryption. iv should be the same as the output of
    # encrypt().
    iv_int = int(iv.encode('hex'), 16)
    ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)

    # Create AES-CTR cipher.
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)

    # Decrypt and return the plaintext.
    plaintext = aes.decrypt(ciphertext)
    return plaintext

(iv, ciphertext) = encrypt(key, 'hella')
print decrypt(key, iv, ciphertext)

这通常称为 AES-CTR。我建议在 PyCrypto 中使用 AES-CBC 时要小心。原因是它要求您指定填充方案,如给出的其他解决方案所示。一般来说,如果您对填充不太小心,就会有攻击完全破坏加密!

现在,需要注意的是,密钥必须是随机的 32 字节字符串;密码是不够的。通常,密钥的生成方式如下:

# Nominal way to generate a fresh key. This calls the system's random number
# generator (RNG).
key1 = Random.new().read(key_bytes)

密钥也可以从密码中派生出来

# It's also possible to derive a key from a password, but it's important that
# the password have high entropy, meaning difficult to predict.
password = "This is a rather weak password."

# For added # security, we add a "salt", which increases the entropy.
#
# In this example, we use the same RNG to produce the salt that we used to
# produce key1.
salt_bytes = 8
salt = Random.new().read(salt_bytes)

# Stands for "Password-based key derivation function 2"
key2 = PBKDF2(password, salt, key_bytes)

上述一些解决方案建议使用SHA-256来派生密钥,但这通常被认为是糟糕的加密做法。
有关操作模式的更多信息,请参阅维基百科。

解决方案 4:

我很感谢其他答案给我的启发,但这对我没有用。

在花了几个小时试图弄清楚它是如何工作的之后,我想出了下面的使用最新PyCryptodomex库的实现(至于我如何在 Windows 上的虚拟环境中在代理后面设置它则是另一个故事了...呼)

它正在处理您的实现。记得记下填充、编码和加密步骤(反之亦然)。您必须打包和解包,并牢记顺序。

import base64
import hashlib
from Cryptodome.Cipher import AES
from Cryptodome.Random import get_random_bytes

__key__ = hashlib.sha256(b'16-character key').digest()

def encrypt(raw):
    BS = AES.block_size
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)

    raw = base64.b64encode(pad(raw).encode('utf8'))
    iv = get_random_bytes(AES.block_size)
    cipher = AES.new(key= __key__, mode= AES.MODE_CFB,iv= iv)
    return base64.b64encode(iv + cipher.encrypt(raw))

def decrypt(enc):
    unpad = lambda s: s[:-ord(s[-1:])]

    enc = base64.b64decode(enc)
    iv = enc[:AES.block_size]
    cipher = AES.new(__key__, AES.MODE_CFB, iv)
    return unpad(base64.b64decode(cipher.decrypt(enc[AES.block_size:])).decode('utf8'))

解决方案 5:

对于想要使用 urlsafe_b64encode 和 urlsafe_b64decode 的人,以下是适合我的版本(在花了一些时间解决 unicode 问题之后)

BS = 16
key = hashlib.md5(settings.SECRET_KEY).hexdigest()[:BS]
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[:-ord(s[len(s)-1:])]

class AESCipher:
    def __init__(self, key):
        self.key = key

    def encrypt(self, raw):
        raw = pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.urlsafe_b64encode(iv + cipher.encrypt(raw)) 

    def decrypt(self, enc):
        enc = base64.urlsafe_b64decode(enc.encode('utf-8'))
        iv = enc[:BS]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(enc[BS:]))

解决方案 6:

对此的另一种看法(主要源自上述解决方案)但是

  • 使用 null 进行填充

  • 不使用 lambda(从来都不喜欢)

  • 使用 Python 2.7 和 3.6.5 进行测试

#!/usr/bin/python2.7
# you'll have to adjust for your setup, e.g., #!/usr/bin/python3


import base64, re
from Crypto.Cipher import AES
from Crypto import Random
from django.conf import settings

class AESCipher:
    """
      Usage:
      aes = AESCipher( settings.SECRET_KEY[:16], 32)
      encryp_msg = aes.encrypt( 'ppppppppppppppppppppppppppppppppppppppppppppppppppppppp' )
      msg = aes.decrypt( encryp_msg )
      print("'{}'".format(msg))
    """
    def __init__(self, key, blk_sz):
        self.key = key
        self.blk_sz = blk_sz

    def encrypt( self, raw ):
        if raw is None or len(raw) == 0:
            raise NameError("No value given to encrypt")
        raw = raw + '' * (self.blk_sz - len(raw) % self.blk_sz)
        raw = raw.encode('utf-8')
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key.encode('utf-8'), AES.MODE_CBC, iv )
        return base64.b64encode( iv + cipher.encrypt( raw ) ).decode('utf-8')

    def decrypt( self, enc ):
        if enc is None or len(enc) == 0:
            raise NameError("No value given to decrypt")
        enc = base64.b64decode(enc)
        iv = enc[:16]
        cipher = AES.new(self.key.encode('utf-8'), AES.MODE_CBC, iv )
        return re.sub(b'x00*$', b'', cipher.decrypt( enc[16:])).decode('utf-8')

解决方案 7:

您可以使用加密哈希函数(不是Python 的内置函数hash)从任意密码中获取密码短语,例如 SHA-1 或 SHA-256。Python 在其标准库中支持这两种函数:

import hashlib

hashlib.sha1("this is my awesome password").digest() # => a 20 byte string
hashlib.sha256("another awesome password").digest() # => a 32 byte string

您只需使用[:16]或即可截断加密哈希值,[:24]并且它将在您指定的长度内保留其安全性。

解决方案 8:

我已经使用了这两个CryptoPyCryptodomex,它们的速度非常快......

import base64
import hashlib
from Cryptodome.Cipher import AES as domeAES
from Cryptodome.Random import get_random_bytes
from Crypto import Random
from Crypto.Cipher import AES as cryptoAES

BLOCK_SIZE = AES.block_size

key = "my_secret_key".encode()
__key__ = hashlib.sha256(key).digest()
print(__key__)

def encrypt(raw):
    BS = cryptoAES.block_size
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
    raw = base64.b64encode(pad(raw).encode('utf8'))
    iv = get_random_bytes(cryptoAES.block_size)
    cipher = cryptoAES.new(key= __key__, mode= cryptoAES.MODE_CFB,iv= iv)
    a= base64.b64encode(iv + cipher.encrypt(raw))
    IV = Random.new().read(BLOCK_SIZE)
    aes = domeAES.new(__key__, domeAES.MODE_CFB, IV)
    b = base64.b64encode(IV + aes.encrypt(a))
    return b

def decrypt(enc):
    passphrase = __key__
    encrypted = base64.b64decode(enc)
    IV = encrypted[:BLOCK_SIZE]
    aes = domeAES.new(passphrase, domeAES.MODE_CFB, IV)
    enc = aes.decrypt(encrypted[BLOCK_SIZE:])
    unpad = lambda s: s[:-ord(s[-1:])]
    enc = base64.b64decode(enc)
    iv = enc[:cryptoAES.block_size]
    cipher = cryptoAES.new(__key__, cryptoAES.MODE_CFB, iv)
    b=  unpad(base64.b64decode(cipher.decrypt(enc[cryptoAES.block_size:])).decode('utf8'))
    return b

encrypted_data =encrypt("Hi Steven!!!!!")
print(encrypted_data)
print("=======")
decrypted_data = decrypt(encrypted_data)
print(decrypted_data)

解决方案 9:

为了其他人的利益,这是我通过结合@Cyril 和@Marcus 的答案得出的解密实现。这假设这是通过 HTTP 请求传入的,其中引用了加密文本并进行了 base64 编码。

import base64
import urllib2
from Crypto.Cipher import AES


def decrypt(quotedEncodedEncrypted):
    key = 'SecretKey'

    encodedEncrypted = urllib2.unquote(quotedEncodedEncrypted)

    cipher = AES.new(key)
    decrypted = cipher.decrypt(base64.b64decode(encodedEncrypted))[:16]

    for i in range(1, len(base64.b64decode(encodedEncrypted))/16):
        cipher = AES.new(key, AES.MODE_CBC, base64.b64decode(encodedEncrypted)[(i-1)*16:i*16])
        decrypted += cipher.decrypt(base64.b64decode(encodedEncrypted)[i*16:])[:16]

    return decrypted.strip()

解决方案 10:

您可以使用类似 PKCS#7 填充的方案。您可以使用它代替以前的函数来填充(进行加密时)和取消填充(进行解密时)。我将在下面提供完整的源代码。

import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
import pkcs7

class Encryption:

    def __init__(self):
        pass

    def Encrypt(self, PlainText, SecurePassword):
        pw_encode = SecurePassword.encode('utf-8')
        text_encode = PlainText.encode('utf-8')

        key = hashlib.sha256(pw_encode).digest()
        iv = Random.new().read(AES.block_size)

        cipher = AES.new(key, AES.MODE_CBC, iv)
        pad_text = pkcs7.encode(text_encode)
        msg = iv + cipher.encrypt(pad_text)

        EncodeMsg = base64.b64encode(msg)
        return EncodeMsg

    def Decrypt(self, Encrypted, SecurePassword):
        decodbase64 = base64.b64decode(Encrypted.decode("utf-8"))
        pw_encode = SecurePassword.decode('utf-8')

        iv = decodbase64[:AES.block_size]
        key = hashlib.sha256(pw_encode).digest()

        cipher = AES.new(key, AES.MODE_CBC, iv)
        msg = cipher.decrypt(decodbase64[AES.block_size:])
        pad_text = pkcs7.decode(msg)

        decryptedString = pad_text.decode('utf-8')
        return decryptedString
import StringIO
import binascii


def decode(text, k=16):
    nl = len(text)
    val = int(binascii.hexlify(text[-1]), 16)
    if val > k:
        raise ValueError('Input is not padded or padding is corrupt')

    l = nl - val
    return text[:l]


def encode(text, k=16):
    l = len(text)
    output = StringIO.StringIO()
    val = k - (l % k)
    for _ in xrange(val):
        output.write('%02x' % val)
    return text + binascii.unhexlify(output.getvalue())

解决方案 11:

参见mnothic 的回答。

兼容UTF-8编码:

def _pad(self, s):
    s = s.encode()
    res = s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs).encode()
    return res

解决方案 12:

PyCrypto 已经老旧并且破产了。

如今,加密技术得到了更好的支持。

这是另一种实现。请注意,这将返回字节,您需要使用 base64 将它们转换为字符串才能进行传输。

import os
import hashlib
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

_BLOCK_SIZE = 16

class AesStringCipher:
    def __init__(self, key):
        self._key = hashlib.sha256(key.encode()).digest()

    def encrypt_str(self, raw:str) -> bytes:
        iv = os.urandom(_BLOCK_SIZE)
        cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv), default_backend())
        encryptor = cipher.encryptor()
        raw = _pad(raw)
        return iv + encryptor.update(raw.encode('utf-8')) + encryptor.finalize()

    def decrypt_str(self, enc:bytes) -> str:
        iv = enc[:_BLOCK_SIZE]
        enc = enc[_BLOCK_SIZE:]
        cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv), default_backend())
        decryptor = cipher.decryptor()
        raw = decryptor.update(enc) + decryptor.finalize()
        raw = raw.decode('utf-8')
        return _unpad(raw)

def _pad(s:str) -> str:
    padding = (_BLOCK_SIZE - (len(s) % _BLOCK_SIZE))
    return s + padding * chr(padding)

def _unpad(s:str) -> str:
    return s[:-ord(s[len(s)-1:])]


if __name__ == '__main__':
    cipher = AesStringCipher('my secret password')

    secret_msg = 'this is a super secret msg ...'
    enc_msg = cipher.encrypt_str(secret_msg)
    dec_msg = cipher.decrypt_str(enc_msg)

    assert secret_msg == dec_msg

解决方案 13:

使用带有 utf8mb4 的 AES-256 对拉丁字符和特殊字符(中文)进行加密和解密:

对于那些需要加密解密拉丁语和特殊值(例如中文)的人,这里是对@MIkee 代码的修改来完成此任务。

请记住,UTF-8 本身无法处理这种类型的编码。

import base64, re
from Crypto.Cipher import AES
from Crypto import Random
from django.conf import settings

import codecs

# Make utf8mb4 recognizable.
codecs.register(lambda name: codecs.lookup('utf8') if name == 'utf8mb4' else None)


class AESCipher:

    def __init__(self, key, blk_sz):
        self.key = key
        self.blk_sz = blk_sz

    def encrypt( self, raw ):
        # raw is the main value
        if raw is None or len(raw) == 0:
            raise NameError("No value given to encrypt")
        raw = raw + '' * (self.blk_sz - len(raw) % self.blk_sz)
        raw = raw.encode('utf8mb4')
        # Initialization vector to avoid same encrypt for same strings.
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key.encode('utf8mb4'), AES.MODE_CFB, iv )
        return base64.b64encode( iv + cipher.encrypt( raw ) ).decode('utf8mb4')

    def decrypt( self, enc ):
        # enc is the encrypted value
        if enc is None or len(enc) == 0:
            raise NameError("No value given to decrypt")
        enc = base64.b64decode(enc)
        iv = enc[:16]
        # AES.MODE_CFB that allows bigger length or Latin values
        cipher = AES.new(self.key.encode('utf8mb4'), AES.MODE_CFB, iv )
        return re.sub(b'x00*$', b'', cipher.decrypt( enc[16:])).decode('utf8mb4')

用法:

>>> from django.conf import settings
>>> from aesencryption import AESCipher
>>>
>>> aes = AESCipher(settings.SECRET_KEY[:16], 32)
>>>
>>> value = aes.encrypt('漢字')
>>>
>>> value
'hnuRwBjwAHDp5X0DmMF3lWzbjR0r81WlW9MRrWukgQwTL0ZI88oQaWvMfBM+W87w9JtSTw=='
>>> dec_value = aes.decrypt(value)
>>> dec_value
'漢字'
>>>

拉丁字母也是一样,例如ã, á, à, â, ã, ç,等等。

注意点

请记住,如果您要将拉丁值存储到数据库中,则需要将其设置为允许此类数据。因此,如果您的数据库设置为utf-8,它将不接受此类数据。您也需要在那里进行更改。

解决方案 14:

from Crypto import Random
from Crypto.Cipher import AES
import base64

BLOCK_SIZE=16
def trans(key):
     return md5.new(key).digest()

def encrypt(message, passphrase):
    passphrase = trans(passphrase)
    IV = Random.new().read(BLOCK_SIZE)
    aes = AES.new(passphrase, AES.MODE_CFB, IV)
    return base64.b64encode(IV + aes.encrypt(message))

def decrypt(encrypted, passphrase):
    passphrase = trans(passphrase)
    encrypted = base64.b64decode(encrypted)
    IV = encrypted[:BLOCK_SIZE]
    aes = AES.new(passphrase, AES.MODE_CFB, IV)
    return aes.decrypt(encrypted[BLOCK_SIZE:])

解决方案 15:

您可以使用新的django-mirage-field包。

解决方案 16:

按照 @mnothic 的实现,我做了一些更改以修复几个问题。首先,我将其变成_unpad了一个实例方法,因为在其中使用前缀而不是前缀decrypt来调用它。我做的第二件事是更改了填充操作顺序,以解释使用字符的文本,这些字符在编码时会产生不止一个字节,从而移动填充。所以我所做的就是将填充放在编码之后。最后,我更改了填充和取消填充逻辑,以便能够处理字节而不是字符串,同时也解释了以前的逻辑会失败的极端情况,例如原始文本不需要填充。self.`AESCipher`

import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES

class AESCipher(object):

    def __init__(self, key):
        self.bs = AES.block_size
        self.key = hashlib.sha256(key.encode()).digest()

    def encrypt(self, raw):
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(self._pad(raw.encode())))

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * bytes(0x01)

    def _unpad(self, s):
        for i in range(1, len(s) - 1):
            if s[-i] != 0:
                break
        i -= 1
        return s[:-i] if i > 0 else s

解决方案 17:

这是另一个答案(Python 3):

# Python 3 - Encrypt/Decrypt using AES 256
# pip install pycryptodome
# Crypto.__version__ == 3.20.0

import base64
import hashlib

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes


def encrypt_binary(data: bytes, password: str) -> bytes:
    plain = data

    padding_length = 16 - len(plain) % 16
    padding = b"" * padding_length

    padded_plain = plain + padding

    key = hashlib.sha256(password.encode("utf-8")).digest()

    iv = get_random_bytes(16)

    aes = AES.new(key, AES.MODE_CBC, iv)

    cipher = aes.encrypt(padded_plain)

    encrypted_data = iv + padding_length.to_bytes(length=1) + cipher

    return encrypted_data


def decrypt_binary(encrypted_data, password):
    iv = encrypted_data[:16]

    padding_length = int.from_bytes(encrypted_data[16:17])

    cipher = encrypted_data[17:]

    key = hashlib.sha256(password.encode("utf-8")).digest()

    aes = AES.new(key, AES.MODE_CBC, iv)

    padded_plain = aes.decrypt(cipher)

    plain = padded_plain[:-padding_length]

    return plain


def encrypt_string(data: str, password: str) -> str:
    encrypted_data = encrypt_binary(data.encode("utf-8"), password)
    return base64.b64encode(encrypted_data).decode("utf-8")


def decrypt_string(encrypted_data: str, password: str) -> str:
    decrypted_data = decrypt_binary(base64.b64decode(encrypted_data), password)
    return decrypted_data.decode("utf-8")


# Example usage

binary_data = b"hello, world!"
string_data = "hello, world!"
password = "123456"

# Binary data

print("data (binary):", binary_data)

encrypted_data = encrypt_binary(binary_data, password)
print("encrypted_data (binary):", encrypted_data)

decrypted_data = decrypt_binary(encrypted_data, password)
print("decrypted_data (binary):", decrypted_data)

assert binary_data == decrypted_data

# String data

print("data (string):", string_data)

encrypted_data = encrypt_string(string_data, password)
print("encrypted_data (string):", encrypted_data)

decrypted_data = decrypt_string(encrypted_data, password)
print("decrypted_data (string):", decrypted_data)

assert string_data == decrypted_data
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1120  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   75  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   66  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   76  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   70  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用