feat: external signer pkcs#11 works
This commit is contained in:
@@ -8,6 +8,11 @@ Cross compile:
|
||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC="zig cc -target x86_64-linux" CXX="zig c++ -target x86_64-linux" go build -o external-signer-pkcs11-linux-x86_64
|
||||
```
|
||||
|
||||
List private objects:
|
||||
```shell
|
||||
pkcs11-tool --module /usr/local/lib/libykcs11.dylib --login --list-objects --type privkey
|
||||
```
|
||||
|
||||
|
||||
```shell
|
||||
external-signer-pkcs11 external_spec
|
||||
|
||||
140
main.go
140
main.go
@@ -2,14 +2,20 @@ package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ThalesGroup/crypto11"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -83,11 +89,14 @@ func innerMain() error {
|
||||
Name: "external-signer-pkc11",
|
||||
Usage: "External signer PKCS#11",
|
||||
Commands: []*cli.Command{
|
||||
buildParameterDecodeCommand(),
|
||||
buildParameterEncodeCommand(),
|
||||
buildExternalSpecCommand(),
|
||||
buildExternalPublicKeyCommand(),
|
||||
buildExternalSignCommand(),
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
fmt.Println("External signer PKCS#11, specification: https://openwebstandard.org/rfc1")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -98,6 +107,92 @@ func innerMain() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildParameterEncodeCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "parameter_encode",
|
||||
Usage: "Encode parameter, show PKCS#11 URIs: pkcs11-tool --module /usr/local/lib/libykcs11.dylib --login --list-objects --type privkey",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "uri",
|
||||
Usage: "PKCS#11 URI",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "library",
|
||||
Usage: "Library",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pin",
|
||||
Usage: "PIN",
|
||||
},
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
uri := ctx.String("uri")
|
||||
library := ctx.String("library")
|
||||
pin := ctx.String("pin")
|
||||
if !strings.HasPrefix(uri, "pkcs11:") {
|
||||
return fmt.Errorf("invalid uri: %s", uri)
|
||||
}
|
||||
if library == "" {
|
||||
library = "/usr/local/lib/libykcs11.dylib"
|
||||
}
|
||||
uriMap := parseUri(uri)
|
||||
pkcs11Key := Pkcs11Key{
|
||||
Library: library,
|
||||
Pin: pin,
|
||||
TokenLabel: uriMap["token"],
|
||||
KeyLabel: uriMap["object"],
|
||||
}
|
||||
parameter, err := json.Marshal(&pkcs11Key)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
fmt.Println(base64.StdEncoding.EncodeToString(parameter))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func parseUri(uri string) map[string]string {
|
||||
uriMap := map[string]string{}
|
||||
pkcs11UriLeftPart := strings.TrimPrefix(uri, "pkcs11:")
|
||||
keyValus := strings.Split(pkcs11UriLeftPart, ";")
|
||||
for _, kv := range keyValus {
|
||||
keyAndValue := strings.Split(kv, "=")
|
||||
if len(keyAndValue) == 2 {
|
||||
decodedValue, err := url.QueryUnescape(keyAndValue[1])
|
||||
if err == nil {
|
||||
uriMap[keyAndValue[0]] = decodedValue
|
||||
} else {
|
||||
uriMap[keyAndValue[0]] = keyAndValue[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
return uriMap
|
||||
}
|
||||
|
||||
func buildParameterDecodeCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "parameter_decode",
|
||||
Usage: "Decode parameter",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "parameter",
|
||||
Usage: "Parameter",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
parameter := ctx.String("parameter")
|
||||
pkcs11Key, err := parseParameter(parameter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printResponse(pkcs11Key)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildExternalSpecCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "external_spec",
|
||||
@@ -173,15 +268,54 @@ func buildExternalSignCommand() *cli.Command {
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
parameter := ctx.String("parameter")
|
||||
alg := ctx.String("alg")
|
||||
messageBase64 := ctx.String("message-base64")
|
||||
// TODO check --alg
|
||||
message, err := base64.StdEncoding.DecodeString(messageBase64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshal --message-base64 failed: %v", err)
|
||||
}
|
||||
return tryWithPkcs11(parameter, true, func(privateKey crypto11.Signer) error {
|
||||
hashed := sha256.Sum256(message)
|
||||
signature, err := privateKey.Sign(rand.Reader, hashed[:], crypto.SHA256)
|
||||
publicKey := privateKey.Public()
|
||||
|
||||
if ecdsaPublicKey, ok := publicKey.(*ecdsa.PublicKey); ok {
|
||||
ecName := ecdsaPublicKey.Curve.Params().Name
|
||||
es256Matches := alg == "ES256" && ecName == "P-256"
|
||||
es384Matches := alg == "ES384" && ecName == "P-384"
|
||||
es512Matches := alg == "ES512" && ecName == "P-521"
|
||||
|
||||
esMatches := es256Matches || es384Matches || es512Matches
|
||||
|
||||
if !esMatches {
|
||||
return fmt.Errorf("algorithm mismatch %s vs %s", alg, ecName)
|
||||
}
|
||||
} else if _, ok := publicKey.(*rsa.PublicKey); ok {
|
||||
rsValid := alg == "RS256" || alg == "RS384" || alg == "RS512"
|
||||
if !rsValid {
|
||||
return fmt.Errorf("rsa algorithm invalid %s", alg)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("invalid public key type: %+v", reflect.TypeOf(publicKey))
|
||||
}
|
||||
|
||||
var digest []byte
|
||||
var hash crypto.Hash
|
||||
if alg == "ES256" || alg == "RS256" {
|
||||
digest32 := sha256.Sum256(message)
|
||||
digest = digest32[:]
|
||||
hash = crypto.SHA256
|
||||
} else if alg == "ES384" || alg == "RS384" {
|
||||
sha384 := crypto.SHA384.New()
|
||||
digest = sha384.Sum(message)
|
||||
hash = crypto.SHA384
|
||||
} else if alg == "ES512" || alg == "RS512" {
|
||||
digest64 := sha512.Sum512(message)
|
||||
digest = digest64[:]
|
||||
hash = crypto.SHA512
|
||||
} else {
|
||||
return fmt.Errorf("invalid algorithm: %s", alg)
|
||||
}
|
||||
|
||||
signature, err := privateKey.Sign(rand.Reader, digest, hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sign with private key failed: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user