Redesign authentication protocol

This commit is contained in:
2019-02-22 17:35:49 +01:00
parent 4a69555578
commit 19732daaf5
33 changed files with 791 additions and 413 deletions

33
cmd/keygen/README.md Normal file
View 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
View 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))
}

View 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)
}

View 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
View 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
View 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)
}