0
# Input/Output Operations
1
2
Utilities for encoding, decoding, and serializing cryptographic objects in standard formats. Provides support for PEM (Privacy-Enhanced Mail) and PKCS#8 formats commonly used for key storage and exchange.
3
4
## Capabilities
5
6
### PEM Encoding and Decoding
7
8
Privacy-Enhanced Mail (PEM) format provides ASCII-armored encoding of binary cryptographic data with clear text headers and optional encryption.
9
10
```python { .api }
11
def encode(data, marker, passphrase=None, randfunc=None):
12
"""
13
Encode binary data in PEM format.
14
15
Parameters:
16
- data (bytes): Binary data to encode
17
- marker (str): PEM marker string (e.g., 'RSA PRIVATE KEY', 'CERTIFICATE')
18
- passphrase (bytes/str): Optional password for encryption
19
- randfunc (callable): Random function for encryption (default: get_random_bytes)
20
21
Returns:
22
bytes: PEM-encoded data with -----BEGIN/END----- headers
23
"""
24
25
def decode(pem_data, passphrase=None):
26
"""
27
Decode PEM-formatted data.
28
29
Parameters:
30
- pem_data (bytes/str): PEM-encoded data with headers
31
- passphrase (bytes/str): Password for encrypted PEM data
32
33
Returns:
34
tuple: (decoded_data, marker, encrypted)
35
- decoded_data (bytes): Binary data
36
- marker (str): PEM marker string
37
- encrypted (bool): Whether data was encrypted
38
39
Raises:
40
ValueError: Invalid PEM format or incorrect passphrase
41
"""
42
```
43
44
### PKCS#8 Private Key Format
45
46
PKCS#8 (Private-Key Information Syntax Standard) provides a format for storing private key information with optional encryption and algorithm identification.
47
48
```python { .api }
49
def wrap(private_key, key_oid, passphrase=None, protection=None, prot_params=None, key_params=None, randfunc=None):
50
"""
51
Wrap private key in PKCS#8 format.
52
53
Parameters:
54
- private_key (bytes): Private key data to wrap
55
- key_oid (str): Object identifier for key algorithm
56
- passphrase (bytes/str): Optional password for encryption
57
- protection (str): Encryption algorithm ('PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC',
58
'PBKDF2WithHMAC-SHA1AndAES128-CBC', 'PBKDF2WithHMAC-SHA1AndAES256-CBC',
59
'scryptAndAES128-CBC', 'scryptAndAES256-CBC')
60
- prot_params (dict): Protection algorithm parameters
61
- key_params (bytes): Optional algorithm parameters
62
- randfunc (callable): Random function for encryption
63
64
Returns:
65
bytes: PKCS#8 wrapped private key (DER-encoded)
66
"""
67
68
def unwrap(p8_private_key, passphrase=None):
69
"""
70
Unwrap PKCS#8 private key.
71
72
Parameters:
73
- p8_private_key (bytes): PKCS#8 wrapped private key (DER or PEM)
74
- passphrase (bytes/str): Password for encrypted keys
75
76
Returns:
77
tuple: (key_data, key_oid, key_params)
78
- key_data (bytes): Private key data
79
- key_oid (str): Key algorithm OID
80
- key_params (bytes): Optional algorithm parameters
81
82
Raises:
83
ValueError: Invalid PKCS#8 format or incorrect passphrase
84
"""
85
```
86
87
## Usage Examples
88
89
### PEM Operations
90
```python
91
from Crypto.IO import PEM
92
from Crypto.PublicKey import RSA
93
94
# Generate RSA key for examples
95
key = RSA.generate(2048)
96
private_der = key.export_key('DER')
97
98
# Encode private key in PEM format (unencrypted)
99
pem_data = PEM.encode(private_der, "RSA PRIVATE KEY")
100
print(pem_data.decode())
101
# Output:
102
# -----BEGIN RSA PRIVATE KEY-----
103
# MIIEpAIBAAKCAQEA...
104
# -----END RSA PRIVATE KEY-----
105
106
# Encode with password protection
107
encrypted_pem = PEM.encode(private_der, "RSA PRIVATE KEY", passphrase="secret123")
108
109
# Decode PEM data
110
decoded_data, marker, was_encrypted = PEM.decode(pem_data)
111
print(f"Marker: {marker}, Encrypted: {was_encrypted}")
112
113
# Decode encrypted PEM
114
decrypted_data, marker, was_encrypted = PEM.decode(encrypted_pem, passphrase="secret123")
115
```
116
117
### Custom PEM Markers
118
```python
119
from Crypto.IO import PEM
120
121
# Custom data with custom marker
122
custom_data = b"Custom binary data for application"
123
pem_encoded = PEM.encode(custom_data, "CUSTOM DATA")
124
125
# Decode custom PEM
126
decoded, marker, encrypted = PEM.decode(pem_encoded)
127
assert decoded == custom_data
128
assert marker == "CUSTOM DATA"
129
```
130
131
### PKCS#8 Operations
132
```python
133
from Crypto.IO import PKCS8
134
from Crypto.PublicKey import RSA, ECC
135
136
# RSA private key in PKCS#8 format
137
rsa_key = RSA.generate(2048)
138
rsa_der = rsa_key.export_key('DER')
139
140
# Wrap RSA key in PKCS#8 (unencrypted)
141
pkcs8_data = PKCS8.wrap(rsa_der, "1.2.840.113549.1.1.1") # RSA OID
142
143
# Wrap with AES-256 encryption
144
encrypted_pkcs8 = PKCS8.wrap(
145
rsa_der,
146
"1.2.840.113549.1.1.1",
147
passphrase="strong_password",
148
protection="PBKDF2WithHMAC-SHA1AndAES256-CBC"
149
)
150
151
# Unwrap PKCS#8 data
152
key_data, key_oid, key_params = PKCS8.unwrap(pkcs8_data)
153
print(f"Key algorithm OID: {key_oid}")
154
155
# Unwrap encrypted PKCS#8
156
decrypted_key, oid, params = PKCS8.unwrap(encrypted_pkcs8, passphrase="strong_password")
157
```
158
159
### ECC Keys in PKCS#8
160
```python
161
from Crypto.IO import PKCS8
162
from Crypto.PublicKey import ECC
163
164
# Generate ECC key
165
ecc_key = ECC.generate(curve='P-256')
166
ecc_der = ecc_key.export_key(format='DER', use_pkcs8=False)
167
168
# Wrap ECC key in PKCS#8 with scrypt protection
169
pkcs8_ecc = PKCS8.wrap(
170
ecc_der,
171
"1.2.840.10045.2.1", # EC public key OID
172
passphrase="ecc_password",
173
protection="scryptAndAES256-CBC"
174
)
175
176
# Unwrap ECC key
177
ecc_data, oid, params = PKCS8.unwrap(pkcs8_ecc, passphrase="ecc_password")
178
```
179
180
### Integration with Key Objects
181
```python
182
from Crypto.PublicKey import RSA
183
from Crypto.IO import PEM, PKCS8
184
185
# Generate and export key in different formats
186
key = RSA.generate(2048)
187
188
# Method 1: Direct PEM export (built-in)
189
pem_direct = key.export_key('PEM', passphrase="pass123")
190
191
# Method 2: Manual PEM encoding
192
der_data = key.export_key('DER')
193
pem_manual = PEM.encode(der_data, "RSA PRIVATE KEY", passphrase="pass123")
194
195
# Method 3: PKCS#8 format
196
pkcs8_wrapped = PKCS8.wrap(
197
der_data,
198
key.oid, # RSA OID from key object
199
passphrase="pass123",
200
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC"
201
)
202
203
# Convert PKCS#8 to PEM
204
pkcs8_pem = PEM.encode(pkcs8_wrapped, "PRIVATE KEY", passphrase="pass123")
205
```
206
207
## Supported Encryption Algorithms
208
209
### PEM Encryption
210
PEM encoding supports the following encryption algorithms for password protection:
211
- **DES-EDE3-CBC**: Triple DES in CBC mode (legacy)
212
- **AES-128-CBC**: AES-128 in CBC mode
213
- **AES-192-CBC**: AES-192 in CBC mode
214
- **AES-256-CBC**: AES-256 in CBC mode (recommended)
215
216
### PKCS#8 Protection Schemes
217
PKCS#8 supports various protection schemes combining key derivation with encryption:
218
219
```python
220
# PBKDF2-based schemes
221
"PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC" # Legacy
222
"PBKDF2WithHMAC-SHA1AndAES128-CBC" # Standard
223
"PBKDF2WithHMAC-SHA1AndAES192-CBC" # High security
224
"PBKDF2WithHMAC-SHA1AndAES256-CBC" # Highest security
225
226
# scrypt-based schemes (more secure against hardware attacks)
227
"scryptAndAES128-CBC" # Modern standard
228
"scryptAndAES256-CBC" # Modern high security
229
```
230
231
## Format Identification
232
233
### PEM Format Detection
234
```python
235
from Crypto.IO import PEM
236
237
def is_pem_format(data):
238
"""Check if data is in PEM format."""
239
if isinstance(data, bytes):
240
data = data.decode('ascii', errors='ignore')
241
return data.strip().startswith('-----BEGIN') and '-----END' in data
242
243
# Example usage
244
pem_data = b"-----BEGIN RSA PRIVATE KEY-----\n..."
245
if is_pem_format(pem_data):
246
decoded, marker, encrypted = PEM.decode(pem_data)
247
```
248
249
### PKCS#8 vs Traditional Format
250
```python
251
from Crypto.IO import PKCS8
252
253
def detect_private_key_format(der_data):
254
"""Detect if DER data is PKCS#8 or traditional format."""
255
try:
256
# Try PKCS#8 unwrap
257
key_data, oid, params = PKCS8.unwrap(der_data)
258
return "PKCS#8"
259
except ValueError:
260
# Probably traditional format (PKCS#1 for RSA, SEC1 for ECC, etc.)
261
return "Traditional"
262
263
# Example usage
264
key = RSA.generate(2048)
265
traditional_der = key.export_key('DER')
266
pkcs8_der = PKCS8.wrap(traditional_der, key.oid)
267
268
print(detect_private_key_format(traditional_der)) # "Traditional"
269
print(detect_private_key_format(pkcs8_der)) # "PKCS#8"
270
```
271
272
## Security Considerations
273
274
### Password Protection
275
- Use strong passwords for encrypted PEM/PKCS#8 data
276
- Prefer AES-256 over legacy DES-based encryption
277
- Consider scrypt-based protection for PKCS#8 (more secure against hardware attacks)
278
279
### Key Storage Best Practices
280
- Store private keys in PKCS#8 format with encryption
281
- Use appropriate file permissions (600) for private key files
282
- Consider hardware security modules (HSMs) for high-value keys
283
- Implement proper key lifecycle management
284
285
### Format Selection Guidelines
286
- **PEM**: Human-readable, widely supported, good for configuration files
287
- **DER**: Binary format, smaller size, good for protocols and embedded systems
288
- **PKCS#8**: Standard format, algorithm-agnostic, recommended for private keys
289
290
## Common OIDs for Key Algorithms
291
292
```python
293
# RSA
294
RSA_OID = "1.2.840.113549.1.1.1"
295
296
# Elliptic Curve
297
EC_PUBLIC_KEY_OID = "1.2.840.10045.2.1"
298
299
# DSA
300
DSA_OID = "1.2.840.10040.4.1"
301
302
# Ed25519
303
ED25519_OID = "1.3.101.112"
304
305
# Ed448
306
ED448_OID = "1.3.101.113"
307
```
308
309
## Error Handling
310
311
- `ValueError`: Invalid format, incorrect passphrase, or malformed data
312
- `TypeError`: Incorrect parameter types
313
- `UnicodeDecodeError`: Invalid PEM text encoding
314
- `KeyError`: Missing required ASN.1 fields in PKCS#8 structures