feat: works
This commit is contained in:
8
go.mod
8
go.mod
@@ -2,10 +2,16 @@ module git.hatter.ink/external-signer-pkcs11
|
|||||||
|
|
||||||
go 1.24.1
|
go 1.24.1
|
||||||
|
|
||||||
require github.com/ThalesGroup/crypto11 v1.4.1
|
require (
|
||||||
|
github.com/ThalesGroup/crypto11 v1.4.1
|
||||||
|
github.com/urfave/cli/v2 v2.27.6
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/thales-e-security/pool v0.0.2 // indirect
|
github.com/thales-e-security/pool v0.0.2 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -1,5 +1,7 @@
|
|||||||
github.com/ThalesGroup/crypto11 v1.4.1 h1:6YR6aVL8LI8akReXKTEgxf+k0+b8wlV8Ra7tZnCG9y4=
|
github.com/ThalesGroup/crypto11 v1.4.1 h1:6YR6aVL8LI8akReXKTEgxf+k0+b8wlV8Ra7tZnCG9y4=
|
||||||
github.com/ThalesGroup/crypto11 v1.4.1/go.mod h1:vggvBwlVrqePDrooq/B32dMXlfEsdsFY+6YlSD7VOy0=
|
github.com/ThalesGroup/crypto11 v1.4.1/go.mod h1:vggvBwlVrqePDrooq/B32dMXlfEsdsFY+6YlSD7VOy0=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||||
@@ -8,7 +10,13 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
|
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
|
||||||
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
|
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
|
||||||
|
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
|
||||||
|
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||||
|
|||||||
248
main.go
248
main.go
@@ -12,6 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ThalesGroup/crypto11"
|
"github.com/ThalesGroup/crypto11"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -25,61 +26,185 @@ type Pkcs11Key struct {
|
|||||||
KeyLabel string `json:"key_label"`
|
KeyLabel string `json:"key_label"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
type ErrorResponse struct {
|
||||||
parameter := "ewogICJsaWJyYXJ5IjogIi91c3IvbG9jYWwvbGliL2xpYnlrY3MxMS5keWxpYiIsCiAgInRva2VuX2xhYmVsIjogIll1YmlLZXkgUElWICM1MDEwMjIwIiwKICAicGluIjogIiIsCiAgImtleV9sYWJlbCI6ICJQcml2YXRlIGtleSBmb3IgUElWIEF1dGhlbnRpY2F0aW9uIgp9Cg=="
|
Success bool `json:"success"`
|
||||||
message := "message"
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExternalSpecResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Agent string `json:"agent"`
|
||||||
|
Specification string `json:"specification"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExternalPublicKeyResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
PublicKeyBase64 string `json:"public_key_base64"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExternalSignatureResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
SignatureBase64 string `json:"signature_base64"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func printResponse(response any) error {
|
||||||
|
errorMessageJsonBytes, jsonErr := json.Marshal(response)
|
||||||
|
if jsonErr != nil {
|
||||||
|
return jsonErr
|
||||||
|
} else {
|
||||||
|
fmt.Println(string(errorMessageJsonBytes))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printResponseSlient(response any) {
|
||||||
|
if err := printResponse(response); err != nil {
|
||||||
|
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter sample:
|
||||||
|
// "ewogICJsaWJyYXJ5IjogIi91c3IvbG9jYWwvbGliL2xpYnlrY3MxMS5keWxpYiIsCiAgInRva2VuX2xhYmVsIjogIll1YmlLZXkgUElWICM1MDEwMjIwIiwKICAicGluIjogIiIsCiAgImtleV9sYWJlbCI6ICJQcml2YXRlIGtleSBmb3IgUElWIEF1dGhlbnRpY2F0aW9uIgp9Cg=="
|
||||||
|
func main() {
|
||||||
|
err := innerMain()
|
||||||
|
if err != nil {
|
||||||
|
errorMessage := &ErrorResponse{
|
||||||
|
Success: false,
|
||||||
|
Error: fmt.Sprintf("%v", err),
|
||||||
|
}
|
||||||
|
if printResponse(errorMessage) != nil {
|
||||||
|
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func innerMain() error {
|
||||||
|
app := cli.App{
|
||||||
|
Name: "external-signer-pkc11",
|
||||||
|
Usage: "External signer PKCS#11",
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
buildExternalSpecCommand(),
|
||||||
|
buildExternalPublicKeyCommand(),
|
||||||
|
buildExternalSignCommand(),
|
||||||
|
},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildExternalSpecCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "external_spec",
|
||||||
|
Usage: "External specification",
|
||||||
|
Flags: []cli.Flag{},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
externalSpecResponse := &ExternalSpecResponse{
|
||||||
|
Success: true,
|
||||||
|
Agent: "external-signer-pkc11/0.1.0",
|
||||||
|
Specification: "External/1.0.0-alpha",
|
||||||
|
}
|
||||||
|
printResponseSlient(externalSpecResponse)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildExternalPublicKeyCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "external_public_key",
|
||||||
|
Usage: "External public key",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "parameter",
|
||||||
|
Usage: "Parameter",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
parameter := ctx.String("parameter")
|
||||||
|
return tryWithPkcs11(parameter, false, func(privateKey crypto11.Signer) error {
|
||||||
|
publicKey := privateKey.Public()
|
||||||
|
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshal public key failed: %v", err)
|
||||||
|
}
|
||||||
|
externalPublicKeyResponse := &ExternalPublicKeyResponse{
|
||||||
|
Success: true,
|
||||||
|
PublicKeyBase64: base64.StdEncoding.EncodeToString(publicKeyBytes),
|
||||||
|
}
|
||||||
|
|
||||||
|
printResponseSlient(externalPublicKeyResponse)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildExternalSignCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "external_sign",
|
||||||
|
Usage: "External sign",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "parameter",
|
||||||
|
Usage: "Parameter",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "alg",
|
||||||
|
Usage: "Algorithm",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "message-base64",
|
||||||
|
Usage: "Message base64 encoded",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
parameter := ctx.String("parameter")
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("sign with private key failed: %v", err)
|
||||||
|
}
|
||||||
|
externalSignatureResponse := ExternalSignatureResponse{
|
||||||
|
Success: true,
|
||||||
|
SignatureBase64: base64.StdEncoding.EncodeToString(signature),
|
||||||
|
}
|
||||||
|
|
||||||
|
printResponseSlient(externalSignatureResponse)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseParameter(parameter string) (*Pkcs11Key, error) {
|
||||||
parameterBytes, err := base64.StdEncoding.DecodeString(parameter)
|
parameterBytes, err := base64.StdEncoding.DecodeString(parameter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("1", err.Error())
|
return nil, fmt.Errorf("parse --parameter base64 failed: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
var pkcs11Key Pkcs11Key
|
var pkcs11Key Pkcs11Key
|
||||||
err = json.Unmarshal(parameterBytes, &pkcs11Key)
|
err = json.Unmarshal(parameterBytes, &pkcs11Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("2", err.Error())
|
return nil, fmt.Errorf("parse --parameter base64 failed: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return &pkcs11Key, nil
|
||||||
pin, err := getPin(pkcs11Key.Pin)
|
|
||||||
if err != nil {
|
|
||||||
println("3", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
config := &crypto11.Config{
|
|
||||||
Path: pkcs11Key.Library,
|
|
||||||
TokenLabel: pkcs11Key.TokenLabel,
|
|
||||||
Pin: pin,
|
|
||||||
}
|
|
||||||
keyLabel := pkcs11Key.KeyLabel
|
|
||||||
|
|
||||||
ctx, err := crypto11.Configure(config)
|
|
||||||
if err != nil {
|
|
||||||
println("4", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer ctx.Close()
|
|
||||||
|
|
||||||
privateKey, err := ctx.FindKeyPair(nil, []byte(keyLabel))
|
|
||||||
if err != nil {
|
|
||||||
println("5", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
publicKey := privateKey.Public()
|
|
||||||
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
|
|
||||||
if err != nil {
|
|
||||||
println("6", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
println(base64.StdEncoding.EncodeToString(publicKeyBytes))
|
|
||||||
|
|
||||||
hashed := sha256.Sum256([]byte(message))
|
|
||||||
signature, err := privateKey.Sign(rand.Reader, hashed[:], crypto.SHA256)
|
|
||||||
if err != nil {
|
|
||||||
println("7", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Printf("%x\n", signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPin(pin string) (string, error) {
|
func getPin(pin string) (string, error) {
|
||||||
@@ -92,3 +217,32 @@ func getPin(pin string) (string, error) {
|
|||||||
}
|
}
|
||||||
return "", errors.New("PIN is not set, set PIN: " + EnvPkcs11Pin)
|
return "", errors.New("PIN is not set, set PIN: " + EnvPkcs11Pin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tryWithPkcs11(parameter string, requirePin bool, callback func(crypto11.Signer) error) error {
|
||||||
|
pkcs11Key, err := parseParameter(parameter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pin, err := getPin(pkcs11Key.Pin)
|
||||||
|
if requirePin && err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config := &crypto11.Config{
|
||||||
|
Path: pkcs11Key.Library,
|
||||||
|
TokenLabel: pkcs11Key.TokenLabel,
|
||||||
|
Pin: pin,
|
||||||
|
}
|
||||||
|
keyLabel := pkcs11Key.KeyLabel
|
||||||
|
|
||||||
|
crypto11Ctx, err := crypto11.Configure(config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("config crypto11 failed: %v", err)
|
||||||
|
}
|
||||||
|
defer crypto11Ctx.Close()
|
||||||
|
|
||||||
|
privateKey, err := crypto11Ctx.FindKeyPair(nil, []byte(keyLabel))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("find private key failed: %v", err)
|
||||||
|
}
|
||||||
|
return callback(privateKey)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user