# 114. Data Encryption with cryptography

Below are 10 Python code snippets demonstrating how to perform data encryption and decryption using the `cryptography` library. This includes symmetric and asymmetric encryption, hashing, and key generation.

***

**1. Generating a Symmetric Key**

```python
from cryptography.fernet import Fernet

# Generate a symmetric key
key = Fernet.generate_key()
print(f"Generated Key: {key.decode()}")
```

***

**2. Encrypting and Decrypting Data with Symmetric Key**

```python
from cryptography.fernet import Fernet

# Generate and save the key
key = Fernet.generate_key()
cipher = Fernet(key)

# Encrypt data
data = b"Secret data"
encrypted_data = cipher.encrypt(data)
print(f"Encrypted: {encrypted_data}")

# Decrypt data
decrypted_data = cipher.decrypt(encrypted_data)
print(f"Decrypted: {decrypted_data.decode()}")
```

***

**3. Saving and Loading a Key**

```python
from cryptography.fernet import Fernet

# Generate and save the key
key = Fernet.generate_key()
with open("symmetric.key", "wb") as key_file:
    key_file.write(key)

# Load the key
with open("symmetric.key", "rb") as key_file:
    loaded_key = key_file.read()

cipher = Fernet(loaded_key)
print(f"Key loaded successfully: {loaded_key.decode()}")
```

***

**4. Asymmetric Key Generation with RSA**

```python
from cryptography.hazmat.primitives.asymmetric import rsa

# Generate private and public keys
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

print("Private key and public key generated successfully.")
```

***

**5. Serializing RSA Keys**

```python
from cryptography.hazmat.primitives import serialization

# Serialize private key
pem_private = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)
print(f"Private Key:\n{pem_private.decode()}")

# Serialize public key
pem_public = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)
print(f"Public Key:\n{pem_public.decode()}")
```

***

**6. Encrypting Data with RSA**

```python
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes

# Encrypt data
message = b"Confidential data"
encrypted_message = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)
print(f"Encrypted Message: {encrypted_message}")
```

***

**7. Decrypting Data with RSA**

```python
# Decrypt data
decrypted_message = private_key.decrypt(
    encrypted_message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)
print(f"Decrypted Message: {decrypted_message.decode()}")
```

***

**8. Hashing Data with SHA-256**

```python
from cryptography.hazmat.primitives import hashes

# Create a SHA-256 hash
digest = hashes.Hash(hashes.SHA256())
digest.update(b"Sensitive data")
hash_value = digest.finalize()
print(f"SHA-256 Hash: {hash_value.hex()}")
```

***

**9. Generating a Key Derivation Function (KDF)**

```python
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import os
import base64

# Derive a key using a password
password = b"securepassword"
salt = os.urandom(16)  # Generate a salt
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=100000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
print(f"Derived Key: {key.decode()}")
```

***

**10. Signing and Verifying Data with RSA**

```python
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes

# Sign data
data = b"Message to sign"
signature = private_key.sign(
    data,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)
print(f"Signature: {signature}")

# Verify signature
try:
    public_key.verify(
        signature,
        data,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    print("Signature is valid.")
except Exception as e:
    print(f"Signature verification failed: {e}")
```

***

**Summary:**

These examples demonstrate how to perform:

* Symmetric encryption with `Fernet`.
* Asymmetric encryption with RSA.
* Hashing with SHA-256.
* Key derivation with PBKDF2.
* Digital signature creation and verification.

Install the `cryptography` library with:

```bash
pip install cryptography
```

Let me know if you'd like further details on any of these techniques!
