Redesign authentication protocol
This commit is contained in:
33
cmd/keygen/README.md
Normal file
33
cmd/keygen/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# keygen
|
||||
|
||||
Utilitaire de génération de jetons d'authentifications.
|
||||
|
||||
## Usage
|
||||
|
||||
### Créer une nouvelle clé privée
|
||||
|
||||
```
|
||||
bin/keygen -create-key
|
||||
```
|
||||
|
||||
### Récupérer la clé publique associée à une clé privée précedemment créée
|
||||
|
||||
```
|
||||
bin/keygen -get-public-key -key chemin/vers/clé/privée
|
||||
```
|
||||
|
||||
### Générer un jeton d'authentification à partir d'une clé privée
|
||||
|
||||
```
|
||||
bin/keygen -create-token -key chemin/vers/clé/privée
|
||||
```
|
||||
|
||||
### Afficher l'aide
|
||||
|
||||
```
|
||||
bin/keygen -help
|
||||
```
|
||||
|
||||
## Mode sans interaction
|
||||
|
||||
Les commandes nécessitant l'entrée d'une phrase de passe peuvent utiliser la variable d'environnement `KEY_PASSPHRASE` pour fonctionner sans interaction.
|
23
cmd/keygen/create_key.go
Normal file
23
cmd/keygen/create_key.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"forge.cadoles.com/wpetit/go-http-peering/crypto"
|
||||
)
|
||||
|
||||
func createKey() {
|
||||
passphrase, err := getPassphrase()
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
}
|
||||
key, err := crypto.CreateRSAKey(keySize)
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
}
|
||||
privatePEM, err := crypto.EncodePrivateKeyToEncryptedPEM(key, passphrase)
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
}
|
||||
fmt.Print(string(privatePEM))
|
||||
}
|
21
cmd/keygen/create_token.go
Normal file
21
cmd/keygen/create_token.go
Normal file
@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"forge.cadoles.com/wpetit/go-http-peering/crypto"
|
||||
|
||||
peering "forge.cadoles.com/wpetit/go-http-peering"
|
||||
)
|
||||
|
||||
func createToken() {
|
||||
privateKey, err := loadPrivateKey()
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
}
|
||||
token, err := crypto.CreateServerToken(privateKey, tokenIssuer, peering.PeerID(tokenPeerID))
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
}
|
||||
fmt.Println(token)
|
||||
}
|
19
cmd/keygen/get_public_key.go
Normal file
19
cmd/keygen/get_public_key.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"forge.cadoles.com/wpetit/go-http-peering/crypto"
|
||||
)
|
||||
|
||||
func getPublicKey() {
|
||||
privateKey, err := loadPrivateKey()
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
}
|
||||
publicPEM, err := crypto.EncodePublicKeyToPEM(privateKey.Public())
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
}
|
||||
fmt.Print(string(publicPEM))
|
||||
}
|
54
cmd/keygen/main.go
Normal file
54
cmd/keygen/main.go
Normal file
@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
)
|
||||
|
||||
// nolint:gochecknoglobals
|
||||
var (
|
||||
createKeyCmd = false
|
||||
getPublicKeyCmd = false
|
||||
createTokenCmd = false
|
||||
debug = false
|
||||
keyFile string
|
||||
tokenIssuer string
|
||||
tokenPeerID = uuid.New()
|
||||
keySize = 2048
|
||||
)
|
||||
|
||||
// nolint:gochecknoinits
|
||||
func init() {
|
||||
flag.BoolVar(
|
||||
&createKeyCmd, "create-key", createKeyCmd,
|
||||
"Create a new encrypted PEM private key to sign authentication tokens",
|
||||
)
|
||||
flag.BoolVar(
|
||||
&createTokenCmd, "create-token", createTokenCmd,
|
||||
"Create a new signed authentication token",
|
||||
)
|
||||
flag.BoolVar(
|
||||
&getPublicKeyCmd, "get-public-key", getPublicKeyCmd,
|
||||
"Get the PEM encoded public key associated with the private key",
|
||||
)
|
||||
flag.BoolVar(&debug, "debug", debug, "Debug mode")
|
||||
flag.StringVar(&keyFile, "key", keyFile, "Path to the encrypted PEM encoded key")
|
||||
flag.StringVar(&tokenIssuer, "token-issuer", tokenIssuer, "Token issuer")
|
||||
flag.StringVar(&tokenPeerID, "token-peer-id", tokenPeerID, "Token peer ID")
|
||||
flag.IntVar(&keySize, "key-size", keySize, "Size of the private key")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
switch {
|
||||
case createKeyCmd:
|
||||
createKey()
|
||||
case getPublicKeyCmd:
|
||||
getPublicKey()
|
||||
case createTokenCmd:
|
||||
createToken()
|
||||
default:
|
||||
flag.Usage()
|
||||
}
|
||||
}
|
96
cmd/keygen/util.go
Normal file
96
cmd/keygen/util.go
Normal file
@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"forge.cadoles.com/wpetit/go-http-peering/crypto"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
func getPassphrase() ([]byte, error) {
|
||||
passphrase := os.Getenv("KEY_PASSPHRASE")
|
||||
if passphrase != "" {
|
||||
return []byte(passphrase), nil
|
||||
}
|
||||
return askPassphrase()
|
||||
}
|
||||
|
||||
func askPassphrase() ([]byte, error) {
|
||||
fmt.Print("Passphrase: ")
|
||||
passphrase, err := terminal.ReadPassword(syscall.Stdin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
fmt.Print("Confirm passphrase: ")
|
||||
passphraseConfirmation, err := terminal.ReadPassword(syscall.Stdin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
if !bytes.Equal(passphrase, passphraseConfirmation) {
|
||||
return nil, errors.New("passphrases does not match")
|
||||
}
|
||||
|
||||
return passphrase, nil
|
||||
}
|
||||
|
||||
func privateKeyToEncryptedPEM(key *rsa.PrivateKey, passphrase []byte) ([]byte, error) {
|
||||
|
||||
if passphrase == nil {
|
||||
return nil, errors.New("passphrase cannot be empty")
|
||||
}
|
||||
|
||||
// Convert it to pem
|
||||
block := &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||
}
|
||||
|
||||
block, err := x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, passphrase, x509.PEMCipherAES256)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(block), nil
|
||||
}
|
||||
|
||||
func loadPrivateKey() (*rsa.PrivateKey, error) {
|
||||
if keyFile == "" {
|
||||
return nil, errors.New("you must specify a key file to load")
|
||||
}
|
||||
pem, err := ioutil.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
passphrase, err := getPassphrase()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privateKey, err := crypto.DecodePEMEncryptedPrivateKey(pem, passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
func handleError(err error) {
|
||||
if !debug {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
Reference in New Issue
Block a user