mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-09-17 05:15:22 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: implementing-rsa-key-pair-management
|
||||
description: RSA (Rivest-Shamir-Adleman) is the most widely deployed asymmetric cryptographic algorithm, used for digital signatures, key exchange, and encryption. This skill covers generating, storing, rotating,
|
||||
domain: cybersecurity
|
||||
subdomain: cryptography
|
||||
tags: [cryptography, rsa, key-management, pki, asymmetric-encryption]
|
||||
version: "1.0"
|
||||
author: mahipal
|
||||
license: MIT
|
||||
---
|
||||
# Implementing RSA Key Pair Management
|
||||
|
||||
## Overview
|
||||
|
||||
RSA (Rivest-Shamir-Adleman) is the most widely deployed asymmetric cryptographic algorithm, used for digital signatures, key exchange, and encryption. This skill covers generating, storing, rotating, and managing RSA key pairs following NIST SP 800-57 key management guidelines, including key serialization formats (PEM, DER, PKCS#8), passphrase protection, and key strength validation.
|
||||
|
||||
## Objectives
|
||||
|
||||
- Generate RSA key pairs with appropriate key sizes (2048, 3072, 4096 bits)
|
||||
- Serialize keys in PEM and DER formats with PKCS#8
|
||||
- Protect private keys with strong passphrase encryption
|
||||
- Implement key rotation with versioning
|
||||
- Extract public key components and fingerprints
|
||||
- Validate key strength and detect weak keys
|
||||
- Sign and verify data using RSA-PSS
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### RSA Key Sizes and Security Strength
|
||||
|
||||
| Key Size (bits) | Security Strength (bits) | Recommended Until |
|
||||
|-----------------|-------------------------|-------------------|
|
||||
| 2048 | 112 | 2030 |
|
||||
| 3072 | 128 | Beyond 2030 |
|
||||
| 4096 | ~140 | Beyond 2030 |
|
||||
|
||||
### RSA Padding Schemes
|
||||
|
||||
| Scheme | Use Case | Standard |
|
||||
|--------|----------|----------|
|
||||
| OAEP | Encryption | PKCS#1 v2.2 (RFC 8017) |
|
||||
| PSS | Signatures | PKCS#1 v2.2 (RFC 8017) |
|
||||
| PKCS#1 v1.5 | Legacy only | Deprecated for new systems |
|
||||
|
||||
### Key Storage Formats
|
||||
|
||||
- **PEM**: Base64-encoded with headers, human-readable
|
||||
- **DER**: Binary ASN.1 encoding, compact
|
||||
- **PKCS#8**: Standard for private key encapsulation
|
||||
- **PKCS#12/PFX**: Bundled key + certificate, password-protected
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Minimum 3072-bit keys for new deployments (NIST recommendation)
|
||||
- Always protect private keys with AES-256-CBC passphrase encryption
|
||||
- Use RSA-PSS for signatures (not PKCS#1 v1.5)
|
||||
- Use RSA-OAEP for encryption (not PKCS#1 v1.5)
|
||||
- Store private keys with restrictive file permissions (0600)
|
||||
- Implement key rotation at least annually
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] Key generation produces valid RSA key pair
|
||||
- [ ] Public key can be extracted from private key
|
||||
- [ ] Private key is protected with passphrase
|
||||
- [ ] RSA-PSS signature verification succeeds
|
||||
- [ ] Tampered signature verification fails
|
||||
- [ ] Key fingerprint is computed correctly
|
||||
- [ ] Key rotation maintains old key access for verification
|
||||
@@ -0,0 +1,56 @@
|
||||
# RSA Key Pair Management Template
|
||||
|
||||
## Key Generation Checklist
|
||||
|
||||
- [ ] Select key size (minimum 3072 bits for new deployments)
|
||||
- [ ] Generate key pair using secure random number generator
|
||||
- [ ] Protect private key with strong passphrase (AES-256)
|
||||
- [ ] Compute and record key fingerprint (SHA-256)
|
||||
- [ ] Set restrictive file permissions on private key
|
||||
- [ ] Store public key in accessible location
|
||||
- [ ] Document key metadata (size, algorithm, creation date)
|
||||
|
||||
## Key Metadata Template
|
||||
|
||||
```json
|
||||
{
|
||||
"key_id": "rsa-prod-001",
|
||||
"algorithm": "RSA",
|
||||
"key_size": 4096,
|
||||
"public_exponent": 65537,
|
||||
"fingerprint_sha256": "<hex-digest>",
|
||||
"created_at": "2024-01-01T00:00:00Z",
|
||||
"expires_at": "2025-01-01T00:00:00Z",
|
||||
"usage": ["sign", "verify"],
|
||||
"owner": "security-team",
|
||||
"version": 1
|
||||
}
|
||||
```
|
||||
|
||||
## Key Rotation Schedule
|
||||
|
||||
| Environment | Rotation Frequency | Grace Period |
|
||||
|------------|-------------------|--------------|
|
||||
| Production | 12 months | 30 days |
|
||||
| Staging | 6 months | 14 days |
|
||||
| Development| 3 months | 7 days |
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```python
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa, padding
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
|
||||
# Generate
|
||||
key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
|
||||
|
||||
# Sign (RSA-PSS)
|
||||
signature = key.sign(data, padding.PSS(
|
||||
mgf=padding.MGF1(hashes.SHA256()),
|
||||
salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256())
|
||||
|
||||
# Verify
|
||||
key.public_key().verify(signature, data, padding.PSS(
|
||||
mgf=padding.MGF1(hashes.SHA256()),
|
||||
salt_length=padding.PSS.MAX_LENGTH), hashes.SHA256())
|
||||
```
|
||||
@@ -0,0 +1,41 @@
|
||||
# Standards and References - RSA Key Pair Management
|
||||
|
||||
## Primary Standards
|
||||
|
||||
### NIST FIPS 186-5 - Digital Signature Standard (DSS)
|
||||
- **URL**: https://csrc.nist.gov/publications/detail/fips/186/5/final
|
||||
- **Description**: Specifies RSA digital signature generation and verification
|
||||
- **RSA minimum**: 2048-bit keys
|
||||
|
||||
### RFC 8017 - PKCS #1: RSA Cryptography Specifications Version 2.2
|
||||
- **URL**: https://www.rfc-editor.org/rfc/rfc8017
|
||||
- **Description**: Defines RSA key formats, OAEP encryption, and PSS signatures
|
||||
- **Key operations**: RSAEP, RSADP, RSASP1, RSAVP1
|
||||
|
||||
### RFC 5958 - Asymmetric Key Packages (PKCS#8 v2)
|
||||
- **URL**: https://www.rfc-editor.org/rfc/rfc5958
|
||||
- **Description**: Private key information syntax for storage
|
||||
|
||||
### RFC 7468 - Textual Encodings of PKIX, PKCS, and CMS Structures
|
||||
- **URL**: https://www.rfc-editor.org/rfc/rfc7468
|
||||
- **Description**: PEM encoding format specification
|
||||
|
||||
### NIST SP 800-57 Part 1 Rev. 5 - Key Management
|
||||
- **URL**: https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final
|
||||
- **Description**: Key length recommendations and lifecycle management
|
||||
- **RSA 2048**: Acceptable through 2030
|
||||
- **RSA 3072+**: Recommended for beyond 2030
|
||||
|
||||
### NIST SP 800-131A Rev. 2 - Transitioning Cryptographic Algorithms
|
||||
- **URL**: https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final
|
||||
- **Description**: Transition guidance for algorithm selection
|
||||
- **PKCS#1 v1.5 signatures**: Legacy use only
|
||||
- **RSA-PSS**: Recommended for all new applications
|
||||
|
||||
## Python Library References
|
||||
|
||||
### cryptography (pyca/cryptography)
|
||||
- **RSA Key Generation**: `cryptography.hazmat.primitives.asymmetric.rsa`
|
||||
- **Serialization**: `cryptography.hazmat.primitives.serialization`
|
||||
- **Signatures**: `cryptography.hazmat.primitives.asymmetric.padding`
|
||||
- **Documentation**: https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/
|
||||
@@ -0,0 +1,81 @@
|
||||
# Workflows - RSA Key Pair Management
|
||||
|
||||
## Workflow 1: Key Pair Generation
|
||||
|
||||
```
|
||||
[Select Key Size] (3072 or 4096 bits)
|
||||
|
|
||||
[Generate RSA Key Pair]
|
||||
(public_exponent=65537)
|
||||
|
|
||||
[Serialize Private Key]
|
||||
(PEM/PKCS#8 with AES-256-CBC passphrase)
|
||||
|
|
||||
[Extract and Serialize Public Key]
|
||||
(PEM/SubjectPublicKeyInfo)
|
||||
|
|
||||
[Compute Key Fingerprint]
|
||||
(SHA-256 of DER-encoded public key)
|
||||
|
|
||||
[Store Keys with Metadata]
|
||||
(key_id, creation_date, algorithm, size)
|
||||
```
|
||||
|
||||
## Workflow 2: Digital Signature (RSA-PSS)
|
||||
|
||||
```
|
||||
[Document/Data to Sign]
|
||||
|
|
||||
[Hash Data] (SHA-256)
|
||||
|
|
||||
[Load Private Key] (decrypt with passphrase)
|
||||
|
|
||||
[RSA-PSS Sign]
|
||||
(padding=PSS, mgf=MGF1(SHA256), salt_length=PSS.MAX_LENGTH)
|
||||
|
|
||||
[Output Signature] (DER or Base64)
|
||||
```
|
||||
|
||||
## Workflow 3: Signature Verification
|
||||
|
||||
```
|
||||
[Document + Signature + Public Key]
|
||||
|
|
||||
[Load Public Key]
|
||||
|
|
||||
[RSA-PSS Verify]
|
||||
(same padding parameters as signing)
|
||||
|
|
||||
[Valid?]
|
||||
YES --> Accept
|
||||
NO --> Reject (data or signature tampered)
|
||||
```
|
||||
|
||||
## Workflow 4: Key Rotation
|
||||
|
||||
```
|
||||
[Current Key Pair (version N)]
|
||||
|
|
||||
[Generate New Key Pair (version N+1)]
|
||||
|
|
||||
[Update Active Key Reference]
|
||||
|
|
||||
[Archive Old Key Pair]
|
||||
(mark as "decrypt/verify only")
|
||||
|
|
||||
[After Grace Period: Destroy Old Private Key]
|
||||
(keep public key for verification)
|
||||
```
|
||||
|
||||
## Workflow 5: RSA Encryption (OAEP)
|
||||
|
||||
```
|
||||
[Plaintext] (max size depends on key and padding)
|
||||
|
|
||||
[Load Recipient's Public Key]
|
||||
|
|
||||
[RSA-OAEP Encrypt]
|
||||
(padding=OAEP, mgf=MGF1(SHA256), algorithm=SHA256)
|
||||
|
|
||||
[Ciphertext]
|
||||
```
|
||||
@@ -0,0 +1,348 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
RSA Key Pair Management Tool
|
||||
|
||||
Implements RSA key generation, serialization, signing, verification,
|
||||
encryption, and key rotation using the cryptography library.
|
||||
|
||||
Requirements:
|
||||
pip install cryptography
|
||||
|
||||
Usage:
|
||||
python process.py generate --size 4096 --output ./keys --passphrase "MyKeyPass"
|
||||
python process.py sign --key ./keys/private.pem --input document.pdf --passphrase "MyKeyPass"
|
||||
python process.py verify --key ./keys/public.pem --input document.pdf --signature document.pdf.sig
|
||||
python process.py info --key ./keys/public.pem
|
||||
python process.py rotate --keystore ./keys --passphrase "MyKeyPass"
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import hashlib
|
||||
import argparse
|
||||
import logging
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa, padding, utils
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
RECOMMENDED_KEY_SIZE = 4096
|
||||
PUBLIC_EXPONENT = 65537
|
||||
|
||||
|
||||
def generate_rsa_keypair(
|
||||
key_size: int = RECOMMENDED_KEY_SIZE,
|
||||
passphrase: Optional[str] = None,
|
||||
) -> Tuple[bytes, bytes, Dict]:
|
||||
"""
|
||||
Generate an RSA key pair.
|
||||
|
||||
Returns:
|
||||
Tuple of (private_key_pem, public_key_pem, metadata)
|
||||
"""
|
||||
if key_size < 2048:
|
||||
raise ValueError("Key size must be at least 2048 bits (3072+ recommended)")
|
||||
|
||||
private_key = rsa.generate_private_key(
|
||||
public_exponent=PUBLIC_EXPONENT,
|
||||
key_size=key_size,
|
||||
backend=default_backend(),
|
||||
)
|
||||
|
||||
if passphrase:
|
||||
encryption = serialization.BestAvailableEncryption(passphrase.encode())
|
||||
else:
|
||||
encryption = serialization.NoEncryption()
|
||||
|
||||
private_pem = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=encryption,
|
||||
)
|
||||
|
||||
public_key = private_key.public_key()
|
||||
public_pem = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
|
||||
# Compute fingerprint (SHA-256 of DER-encoded public key)
|
||||
public_der = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.DER,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
fingerprint = hashlib.sha256(public_der).hexdigest()
|
||||
|
||||
metadata = {
|
||||
"algorithm": "RSA",
|
||||
"key_size": key_size,
|
||||
"public_exponent": PUBLIC_EXPONENT,
|
||||
"fingerprint_sha256": fingerprint,
|
||||
"created_at": datetime.datetime.utcnow().isoformat() + "Z",
|
||||
"passphrase_protected": passphrase is not None,
|
||||
"format": "PKCS#8 PEM",
|
||||
}
|
||||
|
||||
return private_pem, public_pem, metadata
|
||||
|
||||
|
||||
def save_keypair(
|
||||
output_dir: str,
|
||||
private_pem: bytes,
|
||||
public_pem: bytes,
|
||||
metadata: Dict,
|
||||
version: int = 1,
|
||||
) -> Dict:
|
||||
"""Save key pair to files with metadata."""
|
||||
output_path = Path(output_dir)
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
prefix = f"v{version}_" if version > 1 else ""
|
||||
|
||||
private_path = output_path / f"{prefix}private.pem"
|
||||
public_path = output_path / f"{prefix}public.pem"
|
||||
meta_path = output_path / f"{prefix}key_metadata.json"
|
||||
|
||||
private_path.write_bytes(private_pem)
|
||||
public_path.write_bytes(public_pem)
|
||||
|
||||
metadata["version"] = version
|
||||
metadata["private_key_path"] = str(private_path)
|
||||
metadata["public_key_path"] = str(public_path)
|
||||
|
||||
meta_path.write_text(json.dumps(metadata, indent=2))
|
||||
|
||||
logger.info(f"Key pair saved to {output_dir} (version {version})")
|
||||
|
||||
return metadata
|
||||
|
||||
|
||||
def load_private_key(key_path: str, passphrase: Optional[str] = None):
|
||||
"""Load an RSA private key from PEM file."""
|
||||
key_data = Path(key_path).read_bytes()
|
||||
pwd = passphrase.encode() if passphrase else None
|
||||
return serialization.load_pem_private_key(key_data, password=pwd, backend=default_backend())
|
||||
|
||||
|
||||
def load_public_key(key_path: str):
|
||||
"""Load an RSA public key from PEM file."""
|
||||
key_data = Path(key_path).read_bytes()
|
||||
return serialization.load_pem_public_key(key_data, backend=default_backend())
|
||||
|
||||
|
||||
def sign_data(data: bytes, private_key) -> bytes:
|
||||
"""Sign data using RSA-PSS with SHA-256."""
|
||||
signature = private_key.sign(
|
||||
data,
|
||||
padding.PSS(
|
||||
mgf=padding.MGF1(hashes.SHA256()),
|
||||
salt_length=padding.PSS.MAX_LENGTH,
|
||||
),
|
||||
hashes.SHA256(),
|
||||
)
|
||||
return signature
|
||||
|
||||
|
||||
def verify_signature(data: bytes, signature: bytes, public_key) -> bool:
|
||||
"""Verify RSA-PSS signature."""
|
||||
try:
|
||||
public_key.verify(
|
||||
signature,
|
||||
data,
|
||||
padding.PSS(
|
||||
mgf=padding.MGF1(hashes.SHA256()),
|
||||
salt_length=padding.PSS.MAX_LENGTH,
|
||||
),
|
||||
hashes.SHA256(),
|
||||
)
|
||||
return True
|
||||
except InvalidSignature:
|
||||
return False
|
||||
|
||||
|
||||
def encrypt_data(plaintext: bytes, public_key) -> bytes:
|
||||
"""Encrypt data using RSA-OAEP."""
|
||||
max_size = (public_key.key_size // 8) - 2 * 32 - 2 # OAEP with SHA-256
|
||||
if len(plaintext) > max_size:
|
||||
raise ValueError(
|
||||
f"Plaintext too large for RSA-OAEP ({len(plaintext)} bytes, max {max_size}). "
|
||||
"Use envelope encryption for large data."
|
||||
)
|
||||
return public_key.encrypt(
|
||||
plaintext,
|
||||
padding.OAEP(
|
||||
mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
||||
algorithm=hashes.SHA256(),
|
||||
label=None,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def decrypt_data(ciphertext: bytes, private_key) -> bytes:
|
||||
"""Decrypt RSA-OAEP encrypted data."""
|
||||
return private_key.decrypt(
|
||||
ciphertext,
|
||||
padding.OAEP(
|
||||
mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
||||
algorithm=hashes.SHA256(),
|
||||
label=None,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def get_key_info(key_path: str, passphrase: Optional[str] = None) -> Dict:
|
||||
"""Get information about an RSA key."""
|
||||
key_data = Path(key_path).read_bytes()
|
||||
|
||||
try:
|
||||
pwd = passphrase.encode() if passphrase else None
|
||||
key = serialization.load_pem_private_key(key_data, password=pwd, backend=default_backend())
|
||||
key_type = "private"
|
||||
public_key = key.public_key()
|
||||
except (ValueError, TypeError):
|
||||
key = serialization.load_pem_public_key(key_data, backend=default_backend())
|
||||
key_type = "public"
|
||||
public_key = key
|
||||
|
||||
public_der = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.DER,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
fingerprint = hashlib.sha256(public_der).hexdigest()
|
||||
|
||||
numbers = public_key.public_numbers()
|
||||
|
||||
info = {
|
||||
"key_type": key_type,
|
||||
"algorithm": "RSA",
|
||||
"key_size": public_key.key_size,
|
||||
"public_exponent": numbers.e,
|
||||
"fingerprint_sha256": fingerprint,
|
||||
"modulus_hex_prefix": hex(numbers.n)[:32] + "...",
|
||||
}
|
||||
|
||||
if public_key.key_size < 2048:
|
||||
info["warning"] = "Key size below 2048 bits is considered insecure"
|
||||
elif public_key.key_size < 3072:
|
||||
info["note"] = "Key size below 3072 bits; consider upgrading for post-2030 use"
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def rotate_keys(keystore_dir: str, passphrase: Optional[str] = None) -> Dict:
|
||||
"""Rotate RSA key pair, archiving the old one."""
|
||||
keystore = Path(keystore_dir)
|
||||
|
||||
# Find current version
|
||||
version = 1
|
||||
meta_files = sorted(keystore.glob("*key_metadata.json"))
|
||||
if meta_files:
|
||||
for mf in meta_files:
|
||||
meta = json.loads(mf.read_text())
|
||||
v = meta.get("version", 1)
|
||||
if v >= version:
|
||||
version = v + 1
|
||||
|
||||
# Generate new key pair
|
||||
private_pem, public_pem, metadata = generate_rsa_keypair(
|
||||
key_size=RECOMMENDED_KEY_SIZE, passphrase=passphrase
|
||||
)
|
||||
|
||||
result = save_keypair(keystore_dir, private_pem, public_pem, metadata, version=version)
|
||||
|
||||
# Update current key symlink info
|
||||
current_meta = {
|
||||
"current_version": version,
|
||||
"current_fingerprint": metadata["fingerprint_sha256"],
|
||||
"rotated_at": datetime.datetime.utcnow().isoformat() + "Z",
|
||||
"all_versions": list(range(1, version + 1)),
|
||||
}
|
||||
(keystore / "current.json").write_text(json.dumps(current_meta, indent=2))
|
||||
|
||||
logger.info(f"Key rotated to version {version}")
|
||||
return result
|
||||
|
||||
|
||||
def sign_file(key_path: str, input_path: str, passphrase: Optional[str] = None) -> str:
|
||||
"""Sign a file and save the signature."""
|
||||
private_key = load_private_key(key_path, passphrase)
|
||||
data = Path(input_path).read_bytes()
|
||||
signature = sign_data(data, private_key)
|
||||
|
||||
sig_path = input_path + ".sig"
|
||||
Path(sig_path).write_bytes(signature)
|
||||
logger.info(f"Signature saved to {sig_path}")
|
||||
return sig_path
|
||||
|
||||
|
||||
def verify_file(key_path: str, input_path: str, sig_path: str) -> bool:
|
||||
"""Verify a file's signature."""
|
||||
public_key = load_public_key(key_path)
|
||||
data = Path(input_path).read_bytes()
|
||||
signature = Path(sig_path).read_bytes()
|
||||
valid = verify_signature(data, signature, public_key)
|
||||
logger.info(f"Signature verification: {'VALID' if valid else 'INVALID'}")
|
||||
return valid
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="RSA Key Pair Management Tool")
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
gen = subparsers.add_parser("generate", help="Generate RSA key pair")
|
||||
gen.add_argument("--size", type=int, default=4096, help="Key size in bits")
|
||||
gen.add_argument("--output", "-o", default="./keys", help="Output directory")
|
||||
gen.add_argument("--passphrase", "-p", help="Passphrase for private key")
|
||||
|
||||
sig = subparsers.add_parser("sign", help="Sign a file")
|
||||
sig.add_argument("--key", required=True, help="Private key path")
|
||||
sig.add_argument("--input", "-i", required=True, help="File to sign")
|
||||
sig.add_argument("--passphrase", "-p", help="Key passphrase")
|
||||
|
||||
ver = subparsers.add_parser("verify", help="Verify a signature")
|
||||
ver.add_argument("--key", required=True, help="Public key path")
|
||||
ver.add_argument("--input", "-i", required=True, help="Original file")
|
||||
ver.add_argument("--signature", "-s", required=True, help="Signature file")
|
||||
|
||||
info = subparsers.add_parser("info", help="Show key information")
|
||||
info.add_argument("--key", required=True, help="Key file path")
|
||||
info.add_argument("--passphrase", "-p", help="Key passphrase")
|
||||
|
||||
rot = subparsers.add_parser("rotate", help="Rotate key pair")
|
||||
rot.add_argument("--keystore", required=True, help="Keystore directory")
|
||||
rot.add_argument("--passphrase", "-p", help="Passphrase for new key")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "generate":
|
||||
priv, pub, meta = generate_rsa_keypair(args.size, args.passphrase)
|
||||
result = save_keypair(args.output, priv, pub, meta)
|
||||
print(json.dumps(result, indent=2))
|
||||
elif args.command == "sign":
|
||||
sig_path = sign_file(args.key, args.input, args.passphrase)
|
||||
print(json.dumps({"signature_file": sig_path}))
|
||||
elif args.command == "verify":
|
||||
valid = verify_file(args.key, args.input, args.signature)
|
||||
print(json.dumps({"valid": valid}))
|
||||
if not valid:
|
||||
sys.exit(1)
|
||||
elif args.command == "info":
|
||||
result = get_key_info(args.key, args.passphrase)
|
||||
print(json.dumps(result, indent=2))
|
||||
elif args.command == "rotate":
|
||||
result = rotate_keys(args.keystore, args.passphrase)
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user