mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
265 lines
8.9 KiB
Markdown
265 lines
8.9 KiB
Markdown
|
|
# GoFrame RSA Package
|
|||
|
|
|
|||
|
|
Package `grsa` provides useful API for RSA encryption/decryption algorithms within the GoFrame framework.
|
|||
|
|
|
|||
|
|
## Features
|
|||
|
|
|
|||
|
|
- Generating RSA key pairs in PKCS#1 and PKCS#8 formats
|
|||
|
|
- Encrypting and decrypting data with various key formats
|
|||
|
|
- Handling Base64 encoded keys
|
|||
|
|
- Detecting private key types
|
|||
|
|
- Plaintext size validation
|
|||
|
|
- **OAEP padding support (recommended for new applications)**
|
|||
|
|
|
|||
|
|
## Security Considerations
|
|||
|
|
|
|||
|
|
This package provides two padding schemes for RSA encryption:
|
|||
|
|
|
|||
|
|
### 1. PKCS#1 v1.5 (Legacy)
|
|||
|
|
|
|||
|
|
Used by `Encrypt*`, `DecryptPKCS1*`, `DecryptPKCS8*` functions.
|
|||
|
|
|
|||
|
|
⚠️ **Security Warning**: PKCS#1 v1.5 padding is considered less secure and vulnerable to padding oracle attacks. It is provided for backward compatibility with existing systems.
|
|||
|
|
|
|||
|
|
### 2. OAEP (Recommended)
|
|||
|
|
|
|||
|
|
Used by `EncryptOAEP*`, `DecryptOAEP*` functions.
|
|||
|
|
|
|||
|
|
✅ **Recommended**: OAEP (Optimal Asymmetric Encryption Padding) provides better security guarantees and should be used for all new applications.
|
|||
|
|
|
|||
|
|
## Quick Start
|
|||
|
|
|
|||
|
|
### Basic Encryption/Decryption (OAEP - Recommended)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"github.com/gogf/gf/v2/crypto/grsa"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
// Generate a default RSA key pair (2048 bits)
|
|||
|
|
privateKey, publicKey, err := grsa.GenerateDefaultKeyPair()
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Data to encrypt
|
|||
|
|
plainText := []byte("Hello, World!")
|
|||
|
|
|
|||
|
|
// Encrypt with public key using OAEP (recommended)
|
|||
|
|
cipherText, err := grsa.EncryptOAEP(plainText, publicKey)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Decrypt with private key using OAEP
|
|||
|
|
decryptedText, err := grsa.DecryptOAEP(cipherText, privateKey)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Println(string(decryptedText)) // Output: Hello, World!
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Legacy Encryption/Decryption (PKCS#1 v1.5)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"github.com/gogf/gf/v2/crypto/grsa"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
// Generate a default RSA key pair (2048 bits)
|
|||
|
|
privateKey, publicKey, err := grsa.GenerateDefaultKeyPair()
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Data to encrypt
|
|||
|
|
plainText := []byte("Hello, World!")
|
|||
|
|
|
|||
|
|
// Encrypt with public key (PKCS#1 v1.5 - legacy)
|
|||
|
|
cipherText, err := grsa.Encrypt(plainText, publicKey)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Decrypt with private key
|
|||
|
|
decryptedText, err := grsa.Decrypt(cipherText, privateKey)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Println(string(decryptedText)) // Output: Hello, World!
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Working with Base64 Encoded Keys
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"encoding/base64"
|
|||
|
|
"fmt"
|
|||
|
|
"github.com/gogf/gf/v2/crypto/grsa"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
// Generate a key pair
|
|||
|
|
privateKey, publicKey, err := grsa.GenerateDefaultKeyPair()
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Encode keys to Base64
|
|||
|
|
privateKeyBase64 := base64.StdEncoding.EncodeToString(privateKey)
|
|||
|
|
publicKeyBase64 := base64.StdEncoding.EncodeToString(publicKey)
|
|||
|
|
|
|||
|
|
// Data to encrypt
|
|||
|
|
plainText := []byte("Hello, Base64 World!")
|
|||
|
|
|
|||
|
|
// Encrypt with Base64 encoded public key using OAEP (recommended)
|
|||
|
|
cipherTextBase64, err := grsa.EncryptOAEPBase64(plainText, publicKeyBase64)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Decrypt with Base64 encoded private key using OAEP
|
|||
|
|
decryptedText, err := grsa.DecryptOAEPBase64(cipherTextBase64, privateKeyBase64)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Println(string(decryptedText)) // Output: Hello, Base64 World!
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Functions
|
|||
|
|
|
|||
|
|
### Key Generation
|
|||
|
|
|
|||
|
|
- `GenerateKeyPair(bits int)`: Generates a new RSA key pair with the given bits in PKCS#1 format
|
|||
|
|
- `GenerateKeyPairPKCS8(bits int)`: Generates a new RSA key pair with the given bits in PKCS#8 format
|
|||
|
|
- `GenerateDefaultKeyPair()`: Generates a new RSA key pair with default bits (2048) in PKCS#1 format
|
|||
|
|
|
|||
|
|
### OAEP Encryption/Decryption (Recommended)
|
|||
|
|
|
|||
|
|
- `EncryptOAEP(plainText, publicKey []byte)`: Encrypts data with public key using OAEP padding (SHA-256)
|
|||
|
|
- `DecryptOAEP(cipherText, privateKey []byte)`: Decrypts data with private key using OAEP padding (SHA-256)
|
|||
|
|
- `EncryptOAEPBase64(plainText []byte, publicKeyBase64 string)`: Encrypts data with OAEP and returns base64-encoded result
|
|||
|
|
- `DecryptOAEPBase64(cipherTextBase64, privateKeyBase64 string)`: Decrypts base64-encoded OAEP data
|
|||
|
|
- `EncryptOAEPWithHash(plainText, publicKey, label []byte, hash hash.Hash)`: Encrypts with custom hash function
|
|||
|
|
- `DecryptOAEPWithHash(cipherText, privateKey, label []byte, hash hash.Hash)`: Decrypts with custom hash function
|
|||
|
|
|
|||
|
|
### General Encryption/Decryption (Legacy - PKCS#1 v1.5)
|
|||
|
|
|
|||
|
|
- `Encrypt(plainText, publicKey []byte)`: Encrypts data with public key (auto-detect format)
|
|||
|
|
- `Decrypt(cipherText, privateKey []byte)`: Decrypts data with private key (auto-detect format)
|
|||
|
|
- `EncryptBase64(plainText []byte, publicKeyBase64 string)`: Encrypts data with base64-encoded public key and returns base64-encoded result
|
|||
|
|
- `DecryptBase64(cipherTextBase64, privateKeyBase64 string)`: Decrypts base64-encoded data with base64-encoded private key
|
|||
|
|
|
|||
|
|
### PKCS#1 Specific Functions (Legacy)
|
|||
|
|
|
|||
|
|
- `EncryptPKCS1(plainText, publicKey []byte)`: Encrypts data with PKCS#1 format public key
|
|||
|
|
- `DecryptPKCS1(cipherText, privateKey []byte)`: Decrypts data with PKCS#1 format private key
|
|||
|
|
- `EncryptPKCS1Base64(plainText []byte, publicKeyBase64 string)`: Encrypts data with PKCS#1 public key and returns base64-encoded result
|
|||
|
|
- `DecryptPKCS1Base64(cipherTextBase64, privateKeyBase64 string)`: Decrypts base64-encoded data with PKCS#1 private key
|
|||
|
|
|
|||
|
|
### PKIX Specific Functions (Legacy)
|
|||
|
|
|
|||
|
|
PKIX (X.509) is the standard format for public keys, used with PKCS#8 private keys.
|
|||
|
|
|
|||
|
|
- `EncryptPKIX(plainText, publicKey []byte)`: Encrypts data with PKIX format public key
|
|||
|
|
- `EncryptPKIXBase64(plainText []byte, publicKeyBase64 string)`: Encrypts data with PKIX public key and returns base64-encoded result
|
|||
|
|
- `DecryptPKCS8(cipherText, privateKey []byte)`: Decrypts data with PKCS#8 format private key
|
|||
|
|
- `DecryptPKCS8Base64(cipherTextBase64, privateKeyBase64 string)`: Decrypts base64-encoded data with PKCS#8 private key
|
|||
|
|
|
|||
|
|
### Deprecated Functions
|
|||
|
|
|
|||
|
|
The following functions are deprecated and will be removed in future versions:
|
|||
|
|
|
|||
|
|
- `EncryptPKCS8(plainText, publicKey []byte)`: Use `EncryptPKIX` instead
|
|||
|
|
- `EncryptPKCS8Base64(plainText []byte, publicKeyBase64 string)`: Use `EncryptPKIXBase64` instead
|
|||
|
|
|
|||
|
|
### Utility Functions
|
|||
|
|
|
|||
|
|
- `GetPrivateKeyType(privateKey []byte)`: Detects the type of private key (PKCS#1 or PKCS#8)
|
|||
|
|
- `GetPrivateKeyTypeBase64(privateKeyBase64 string)`: Detects the type of base64 encoded private key
|
|||
|
|
- `ExtractPKCS1PublicKey(privateKey []byte)`: Extracts PKCS#1 public key from PKCS#1 private key
|
|||
|
|
|
|||
|
|
## Key Formats
|
|||
|
|
|
|||
|
|
The package supports two popular RSA key formats:
|
|||
|
|
|
|||
|
|
1. **PKCS#1**: Traditional RSA key format
|
|||
|
|
- Private key PEM header: `-----BEGIN RSA PRIVATE KEY-----`
|
|||
|
|
- Public key PEM header: `-----BEGIN RSA PUBLIC KEY-----`
|
|||
|
|
|
|||
|
|
2. **PKCS#8/PKIX**: More modern and flexible key format
|
|||
|
|
- Private key PEM header: `-----BEGIN PRIVATE KEY-----`
|
|||
|
|
- Public key PEM header: `-----BEGIN PUBLIC KEY-----`
|
|||
|
|
|
|||
|
|
Both formats are supported for encryption and decryption operations, with auto-detection capabilities for general functions.
|
|||
|
|
|
|||
|
|
### Technical Background: PKCS#8 vs PKIX
|
|||
|
|
|
|||
|
|
**PKCS#8** is a standard for **private keys** only, not public keys. Public keys use the **PKIX (X.509 SubjectPublicKeyInfo)** format.
|
|||
|
|
|
|||
|
|
| Format | Private Key PEM Header | Public Key PEM Header |
|
|||
|
|
|--------|------------------------|----------------------|
|
|||
|
|
| PKCS#1 | `RSA PRIVATE KEY` | `RSA PUBLIC KEY` |
|
|||
|
|
| PKCS#8/PKIX | `PRIVATE KEY` | `PUBLIC KEY` |
|
|||
|
|
|
|||
|
|
When we refer to a "PKCS#8 key pair", it actually means:
|
|||
|
|
- **Private key**: PKCS#8 format (RFC 5208)
|
|||
|
|
- **Public key**: PKIX/SubjectPublicKeyInfo format (RFC 5280, X.509)
|
|||
|
|
|
|||
|
|
This is why the Go standard library provides `x509.MarshalPKCS8PrivateKey` for private keys but `x509.MarshalPKIXPublicKey` for public keys — there is no `MarshalPKCS8PublicKey` function.
|
|||
|
|
|
|||
|
|
The deprecated `EncryptPKCS8` function was a misnomer because encryption uses public keys, and public keys are in PKIX format, not PKCS#8. The correct function name is `EncryptPKIX`.
|
|||
|
|
|
|||
|
|
## Plaintext Size Limit
|
|||
|
|
|
|||
|
|
RSA encryption has a size limit based on key size and padding scheme.
|
|||
|
|
|
|||
|
|
### PKCS#1 v1.5 Padding (Legacy)
|
|||
|
|
|
|||
|
|
- **Max plaintext size = key_size_in_bytes - 11**
|
|||
|
|
- For a 2048-bit key: max 245 bytes
|
|||
|
|
- For a 4096-bit key: max 501 bytes
|
|||
|
|
|
|||
|
|
### OAEP Padding with SHA-256 (Recommended)
|
|||
|
|
|
|||
|
|
- **Max plaintext size = key_size_in_bytes - 2 × hash_size - 2**
|
|||
|
|
- For a 2048-bit key with SHA-256: max 190 bytes
|
|||
|
|
- For a 4096-bit key with SHA-256: max 446 bytes
|
|||
|
|
|
|||
|
|
If you need to encrypt larger data, consider using hybrid encryption (RSA + AES).
|
|||
|
|
|
|||
|
|
## Error Handling
|
|||
|
|
|
|||
|
|
All functions return descriptive errors that can be handled using the GoFrame error package (`gerror`). Errors typically include:
|
|||
|
|
|
|||
|
|
- Invalid key format
|
|||
|
|
- Failed key parsing
|
|||
|
|
- Plaintext too long
|
|||
|
|
- Encryption/decryption failures
|
|||
|
|
|
|||
|
|
Always check for errors in production code to ensure robust handling of edge cases.
|
|||
|
|
|
|||
|
|
## Testing
|
|||
|
|
|
|||
|
|
Run the package tests with:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go test -v
|
|||
|
|
```
|