Start implementation of revoke action
This commit is contained in:
parent
38566f35ef
commit
2de9771472
9 changed files with 739 additions and 57 deletions
|
@ -2,7 +2,9 @@ package datastructures
|
||||||
|
|
||||||
import "encoding/binary"
|
import "encoding/binary"
|
||||||
|
|
||||||
func encode24BitLength(data []byte) []byte {
|
const signerTimeFormat = "010203042006.05"
|
||||||
|
|
||||||
|
func Encode24BitLength(data []byte) []byte {
|
||||||
lengthBytes := make([]byte, 4)
|
lengthBytes := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(lengthBytes, uint32(len(data)))
|
binary.BigEndian.PutUint32(lengthBytes, uint32(len(data)))
|
||||||
return lengthBytes[1:]
|
return lengthBytes[1:]
|
||||||
|
|
|
@ -3,6 +3,7 @@ package datastructures
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
|
@ -11,12 +12,12 @@ import (
|
||||||
type SignerRequest struct {
|
type SignerRequest struct {
|
||||||
Version uint8
|
Version uint8
|
||||||
Action shared.Action
|
Action shared.Action
|
||||||
System uint8
|
System shared.CryptoSystemId
|
||||||
Root uint8
|
Root shared.CryptoSystemRootId
|
||||||
Configuration uint8
|
Profile shared.CertificateProfileId
|
||||||
Parameter1 uint8
|
MdAlgorithm shared.MessageDigestAlgorithmId
|
||||||
Parameter2 uint16
|
Days uint16
|
||||||
Parameter3 uint8
|
Spkac uint8
|
||||||
Content1 string
|
Content1 string
|
||||||
Content2 string
|
Content2 string
|
||||||
Content3 string
|
Content3 string
|
||||||
|
@ -41,40 +42,64 @@ func SignerRequestFromData(blockData []byte) (*SignerRequest, error) {
|
||||||
return &SignerRequest{
|
return &SignerRequest{
|
||||||
Version: headerBytes[0],
|
Version: headerBytes[0],
|
||||||
Action: shared.Action(headerBytes[1]),
|
Action: shared.Action(headerBytes[1]),
|
||||||
System: headerBytes[2],
|
System: shared.CryptoSystemId(headerBytes[2]),
|
||||||
Root: headerBytes[3],
|
Root: shared.CryptoSystemRootId(headerBytes[3]),
|
||||||
Configuration: headerBytes[4],
|
Profile: shared.CertificateProfileId(headerBytes[4]),
|
||||||
Parameter1: headerBytes[5],
|
MdAlgorithm: shared.MessageDigestAlgorithmId(headerBytes[5]),
|
||||||
Parameter2: binary.BigEndian.Uint16([]byte{headerBytes[6], headerBytes[7]}),
|
Days: binary.BigEndian.Uint16([]byte{headerBytes[6], headerBytes[7]}),
|
||||||
Parameter3: headerBytes[8],
|
Spkac: headerBytes[8],
|
||||||
Content1: content1,
|
Content1: content1,
|
||||||
Content2: content2,
|
Content2: content2,
|
||||||
Content3: content3,
|
Content3: content3,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r SignerRequest) Serialize() []byte {
|
func (r *SignerRequest) Serialize() []byte {
|
||||||
parameter2Bytes := make([]byte, 2)
|
parameter2Bytes := make([]byte, 2)
|
||||||
binary.BigEndian.PutUint16(parameter2Bytes, r.Parameter2)
|
binary.BigEndian.PutUint16(parameter2Bytes, r.Days)
|
||||||
headerBytes := bytes.Join([][]byte{
|
headerBytes := bytes.Join([][]byte{
|
||||||
{r.Version, byte(r.Action), r.System, r.Root, r.Configuration, r.Parameter1},
|
{
|
||||||
parameter2Bytes, {r.Parameter3}}, []byte{})
|
r.Version,
|
||||||
|
byte(r.Action),
|
||||||
|
byte(r.System),
|
||||||
|
byte(r.Root),
|
||||||
|
byte(r.Profile),
|
||||||
|
byte(r.MdAlgorithm),
|
||||||
|
},
|
||||||
|
parameter2Bytes, {r.Spkac}}, []byte{})
|
||||||
content1Bytes := []byte(r.Content1)
|
content1Bytes := []byte(r.Content1)
|
||||||
content2Bytes := []byte(r.Content2)
|
content2Bytes := []byte(r.Content2)
|
||||||
content3Bytes := []byte(r.Content3)
|
content3Bytes := []byte(r.Content3)
|
||||||
blockBytes := bytes.Join([][]byte{
|
blockBytes := bytes.Join([][]byte{
|
||||||
encode24BitLength(headerBytes), headerBytes,
|
Encode24BitLength(headerBytes), headerBytes,
|
||||||
encode24BitLength(content1Bytes), content1Bytes,
|
Encode24BitLength(content1Bytes), content1Bytes,
|
||||||
encode24BitLength(content2Bytes), content2Bytes,
|
Encode24BitLength(content2Bytes), content2Bytes,
|
||||||
encode24BitLength(content3Bytes), content3Bytes,
|
Encode24BitLength(content3Bytes), content3Bytes,
|
||||||
}, []byte{})
|
}, []byte{})
|
||||||
return bytes.Join([][]byte{encode24BitLength(blockBytes), blockBytes}, []byte{})
|
return bytes.Join([][]byte{Encode24BitLength(blockBytes), blockBytes}, []byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SignerRequest) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"v:%d %s s:%d r:%d p:%d md:%d days:%d spkac:%d '%s' '%s' '%s'",
|
||||||
|
r.Version,
|
||||||
|
r.Action,
|
||||||
|
r.System,
|
||||||
|
r.Root,
|
||||||
|
r.Profile,
|
||||||
|
r.MdAlgorithm,
|
||||||
|
r.Days,
|
||||||
|
r.Spkac,
|
||||||
|
r.Content1,
|
||||||
|
r.Content2,
|
||||||
|
r.Content3,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNulRequest() *SignerRequest {
|
func NewNulRequest() *SignerRequest {
|
||||||
return &SignerRequest{
|
return &SignerRequest{
|
||||||
Version: shared.ProtocolVersion,
|
Version: shared.ProtocolVersion,
|
||||||
Action: shared.ActionNul,
|
Action: shared.ActionNul,
|
||||||
Content1: time.Now().UTC().Format("010203042006.05"),
|
Content1: time.Now().UTC().Format(signerTimeFormat),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,10 +62,20 @@ func (r SignerResponse) Serialize() []byte {
|
||||||
content2Bytes := []byte(r.Content2)
|
content2Bytes := []byte(r.Content2)
|
||||||
content3Bytes := []byte(r.Content3)
|
content3Bytes := []byte(r.Content3)
|
||||||
blockBytes := bytes.Join([][]byte{
|
blockBytes := bytes.Join([][]byte{
|
||||||
encode24BitLength(headerBytes), headerBytes,
|
Encode24BitLength(headerBytes), headerBytes,
|
||||||
encode24BitLength(content1Bytes), content1Bytes,
|
Encode24BitLength(content1Bytes), content1Bytes,
|
||||||
encode24BitLength(content2Bytes), content2Bytes,
|
Encode24BitLength(content2Bytes), content2Bytes,
|
||||||
encode24BitLength(content3Bytes), content3Bytes,
|
Encode24BitLength(content3Bytes), content3Bytes,
|
||||||
}, []byte{})
|
}, []byte{})
|
||||||
return bytes.Join([][]byte{encode24BitLength(blockBytes), blockBytes}, []byte{})
|
return bytes.Join([][]byte{Encode24BitLength(blockBytes), blockBytes}, []byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNulResponse(version byte) *SignerResponse {
|
||||||
|
return &SignerResponse{
|
||||||
|
Version: version,
|
||||||
|
Action: shared.ActionNul,
|
||||||
|
Content1: "",
|
||||||
|
Content2: "",
|
||||||
|
Content3: "",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -5,5 +5,6 @@ go 1.15
|
||||||
require (
|
require (
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.7.0
|
||||||
go.bug.st/serial v1.1.1
|
go.bug.st/serial v1.1.1
|
||||||
|
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,7 +14,7 @@ const (
|
||||||
TrailerFieldSize = len(MagicTrailer)
|
TrailerFieldSize = len(MagicTrailer)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Action uint8
|
type Action byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ActionNul = Action(0)
|
ActionNul = Action(0)
|
||||||
|
@ -34,3 +34,11 @@ func (a Action) String() string {
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CryptoSystemRootId byte
|
||||||
|
|
||||||
|
type CertificateProfileId byte
|
||||||
|
|
||||||
|
type MessageDigestAlgorithmId byte
|
||||||
|
|
||||||
|
type CryptoSystemId byte
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
package signer
|
package signer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
@ -12,28 +23,45 @@ import (
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const crlLifetime = time.Hour * 24 * 7
|
||||||
|
|
||||||
|
var emptyDataSha1 [20]byte
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
emptyDataSha1 = sha1.Sum([]byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandProcessorSettings struct {
|
||||||
|
CABaseDir string
|
||||||
|
XDeltaPath string
|
||||||
|
OpenPGPUidEmail string
|
||||||
|
}
|
||||||
|
|
||||||
// The CommandProcessor takes parsed protocol data and executes the actual
|
// The CommandProcessor takes parsed protocol data and executes the actual
|
||||||
// functionality.
|
// functionality.
|
||||||
type CommandProcessor struct {
|
type CommandProcessor struct {
|
||||||
|
Settings *CommandProcessorSettings
|
||||||
|
CryptoSystems map[shared.CryptoSystemId]*CryptoSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the signer request
|
// Process the signer request
|
||||||
func (p *CommandProcessor) Process(command datastructures.SignerRequest) (
|
func (p *CommandProcessor) Process(command *datastructures.SignerRequest) (
|
||||||
response *datastructures.SignerResponse,
|
*datastructures.SignerResponse,
|
||||||
err error,
|
error,
|
||||||
) {
|
) {
|
||||||
log.Infof("analyze %+v", command)
|
log.Infof("process %s", command)
|
||||||
|
|
||||||
|
if command.Version != shared.ProtocolVersion {
|
||||||
|
return nil, fmt.Errorf("unsupported protocol version %d", command.Version)
|
||||||
|
}
|
||||||
|
|
||||||
switch command.Action {
|
switch command.Action {
|
||||||
case shared.ActionNul:
|
case shared.ActionNul:
|
||||||
response, err = p.handleNulAction(command)
|
return p.handleNulAction(command)
|
||||||
return
|
|
||||||
case shared.ActionSign:
|
case shared.ActionSign:
|
||||||
response, err = p.handleSignAction(command)
|
return p.handleSignAction(command)
|
||||||
return
|
|
||||||
case shared.ActionRevoke:
|
case shared.ActionRevoke:
|
||||||
response, err = p.handleRevokeAction(command)
|
return p.handleRevokeAction(command)
|
||||||
return
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(fmt.Sprintf(
|
return nil, errors.New(fmt.Sprintf(
|
||||||
"unsupported Action 0x%02x %s",
|
"unsupported Action 0x%02x %s",
|
||||||
|
@ -43,7 +71,7 @@ func (p *CommandProcessor) Process(command datastructures.SignerRequest) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*CommandProcessor) handleNulAction(command datastructures.SignerRequest) (
|
func (*CommandProcessor) handleNulAction(command *datastructures.SignerRequest) (
|
||||||
*datastructures.SignerResponse,
|
*datastructures.SignerResponse,
|
||||||
error,
|
error,
|
||||||
) {
|
) {
|
||||||
|
@ -64,13 +92,11 @@ func (*CommandProcessor) handleNulAction(command datastructures.SignerRequest) (
|
||||||
log.Errorf("could not set system time: %v", e1)
|
log.Errorf("could not set system time: %v", e1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &datastructures.SignerResponse{
|
return datastructures.NewNulResponse(command.Version), nil
|
||||||
Version: command.Version, Action: command.Action,
|
|
||||||
Content1: "", Content2: "", Content3: ""}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CommandProcessor) handleSignAction(
|
func (p *CommandProcessor) handleSignAction(
|
||||||
command datastructures.SignerRequest,
|
command *datastructures.SignerRequest,
|
||||||
) (
|
) (
|
||||||
*datastructures.SignerResponse,
|
*datastructures.SignerResponse,
|
||||||
error,
|
error,
|
||||||
|
@ -80,11 +106,239 @@ func (p *CommandProcessor) handleSignAction(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *CommandProcessor) handleRevokeAction(
|
func (p *CommandProcessor) handleRevokeAction(
|
||||||
command datastructures.SignerRequest,
|
command *datastructures.SignerRequest,
|
||||||
) (
|
) (
|
||||||
*datastructures.SignerResponse,
|
*datastructures.SignerResponse,
|
||||||
error,
|
error,
|
||||||
) {
|
) {
|
||||||
log.Debugf("handle revoke call: %v", command)
|
log.Debugf("handle revoke call: %v", command)
|
||||||
|
|
||||||
|
idSystem, err := p.checkIdentitySystem(
|
||||||
|
command.System,
|
||||||
|
command.Root,
|
||||||
|
command.Profile,
|
||||||
|
command.MdAlgorithm,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("identified id system: %+v", idSystem)
|
||||||
|
|
||||||
|
switch command.System {
|
||||||
|
case CsX509:
|
||||||
|
request := command.Content1
|
||||||
|
clientHash := command.Content3
|
||||||
|
return p.revokeX509(idSystem, request, clientHash)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("revoke not implemented for crypto system %s", idSystem.System)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdSystemParameters struct {
|
||||||
|
System *CryptoSystem
|
||||||
|
Root *RootCredentials
|
||||||
|
RootId shared.CryptoSystemRootId
|
||||||
|
Profile string
|
||||||
|
MessageDigestAlgorithm x509.SignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *CommandProcessor) checkIdentitySystem(
|
||||||
|
systemId shared.CryptoSystemId,
|
||||||
|
rootId shared.CryptoSystemRootId,
|
||||||
|
profileId shared.CertificateProfileId,
|
||||||
|
algorithmId shared.MessageDigestAlgorithmId,
|
||||||
|
) (*IdSystemParameters, error) {
|
||||||
|
cryptoSystem, ok := p.CryptoSystems[systemId]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"unsupported crypto system %d",
|
||||||
|
systemId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
root, ok := p.CryptoSystems[systemId].Roots[rootId]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"unsupported root certificate %d for crypto system %s",
|
||||||
|
rootId,
|
||||||
|
cryptoSystem,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
profile, ok := p.CryptoSystems[systemId].Profiles[profileId]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"invalid CA profile %d for crypto system %s",
|
||||||
|
profileId,
|
||||||
|
cryptoSystem,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mdAlgorithm, ok := p.CryptoSystems[systemId].DigestAlgorithms[algorithmId]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"unsupported digest algorithm %d for crypto system %s",
|
||||||
|
algorithmId,
|
||||||
|
cryptoSystem,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return &IdSystemParameters{
|
||||||
|
System: cryptoSystem,
|
||||||
|
Root: root,
|
||||||
|
RootId: rootId,
|
||||||
|
Profile: profile,
|
||||||
|
MessageDigestAlgorithm: mdAlgorithm,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *CommandProcessor) revokeX509(
|
||||||
|
system *IdSystemParameters,
|
||||||
|
request string,
|
||||||
|
clientHash string,
|
||||||
|
) (*datastructures.SignerResponse, error) {
|
||||||
|
clientHashBytes, err := hex.DecodeString(clientHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse '%s' as hex bytes", clientHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("revoke X.509 for root %s (%d)", system.Root, system.RootId)
|
||||||
|
log.Debugf("hash bytes from client %x", clientHashBytes)
|
||||||
|
|
||||||
|
crlFileName := fmt.Sprintf("revoke-root%d.crl", system.RootId)
|
||||||
|
stat, err := os.Stat(crlFileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("CRL file %s does not exist", crlFileName)
|
||||||
|
} else if stat.IsDir() {
|
||||||
|
return nil, fmt.Errorf("CRL filename %s points to a directory", crlFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentHash := emptyDataSha1
|
||||||
|
if stat != nil {
|
||||||
|
crlData, err := ioutil.ReadFile(crlFileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("could not read current CRL, assuming empty CRL")
|
||||||
|
}
|
||||||
|
currentHash = sha1.Sum(crlData)
|
||||||
|
}
|
||||||
|
log.Debugf("hash bytes on signer %x", currentHash)
|
||||||
|
|
||||||
|
crlIsCurrent := false
|
||||||
|
if bytes.Equal(clientHashBytes, currentHash[:]) {
|
||||||
|
log.Debug("client CRL hash and current CRL hash on signer match")
|
||||||
|
err := p.deleteOldCrl(system.RootId, clientHash)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("could not delete old CRLs: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(clientHashBytes, emptyDataSha1[:]) {
|
||||||
|
crlIsCurrent = true
|
||||||
|
} else {
|
||||||
|
log.Debugf("client has an empty CRL, the signer too")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(request) > 0 {
|
||||||
|
_, err = revokeCertificate(request, system.System, system.RootId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not revoke certificate / create CRL: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var crlBytes []byte
|
||||||
|
crlBytes, err = generateCrl(system.MessageDigestAlgorithm, system.System, system.RootId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not generate a new CRL for root %d: %v", system.RootId, err)
|
||||||
|
}
|
||||||
|
log.Debugf("crlIsCurrent: %v, crlBytes: %d", crlIsCurrent, len(crlBytes))
|
||||||
|
|
||||||
|
log.Tracef("New CRL\n%s", pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "X509 CRL",
|
||||||
|
Bytes: crlBytes,
|
||||||
|
}))
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(crlFileName, crlBytes, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not write new CRL to %s: %v", crlFileName, err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil, errors.New("not implemented yet")
|
return nil, errors.New("not implemented yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *CommandProcessor) deleteOldCrl(id shared.CryptoSystemRootId, hash string) error {
|
||||||
|
path.Join(p.Settings.CABaseDir, "currentcrls", fmt.Sprintf("%d", id))
|
||||||
|
// TODO: implement
|
||||||
|
return errors.New("not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCrl(
|
||||||
|
algorithm x509.SignatureAlgorithm,
|
||||||
|
system *CryptoSystem,
|
||||||
|
rootId shared.CryptoSystemRootId,
|
||||||
|
) ([]byte, error) {
|
||||||
|
caCertificate, err := system.loadCertificate(rootId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
caPrivateKey, err := system.getPrivateKey(rootId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certificatesToRevoke, err := system.loadRevokedCertificatesFromDatabase(rootId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nextCrlNumber, err := system.getNextCRLNumber(rootId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
crlTemplate := &x509.RevocationList{
|
||||||
|
SignatureAlgorithm: algorithm,
|
||||||
|
RevokedCertificates: certificatesToRevoke,
|
||||||
|
Number: nextCrlNumber,
|
||||||
|
ThisUpdate: time.Now(),
|
||||||
|
NextUpdate: time.Now().Add(crlLifetime),
|
||||||
|
ExtraExtensions: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err = system.bumpCRLNumber(rootId, nextCrlNumber); err != nil {
|
||||||
|
log.Errorf("could not bump CRL number: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return x509.CreateRevocationList(
|
||||||
|
rand.Reader,
|
||||||
|
crlTemplate,
|
||||||
|
caCertificate,
|
||||||
|
caPrivateKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func revokeCertificate(request string, system *CryptoSystem, rootId shared.CryptoSystemRootId) (*pkix.RevokedCertificate, error) {
|
||||||
|
pemBlock, _ := pem.Decode([]byte(request))
|
||||||
|
if pemBlock.Type != "CERTIFICATE" {
|
||||||
|
if pemBlock.Type != "CERTIFICATE" {
|
||||||
|
log.Warnf(
|
||||||
|
"PEM structure is probably not a certificate. PEM block has type %s",
|
||||||
|
pemBlock.Type,
|
||||||
|
)
|
||||||
|
log.Trace(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
certificate, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"could no parse certificate: %v",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return system.recordRevocation(rootId, certificate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommandProcessorSettings() *CommandProcessorSettings {
|
||||||
|
caBasedir, ok := os.LookupEnv("SIGNER_BASEDIR")
|
||||||
|
if !ok {
|
||||||
|
caBasedir = "."
|
||||||
|
}
|
||||||
|
gpgUidEmail, ok := os.LookupEnv("SIGNER_GPG_KEYRING_DIR")
|
||||||
|
if !ok {
|
||||||
|
gpgUidEmail = "gpg@cacert.org"
|
||||||
|
}
|
||||||
|
return &CommandProcessorSettings{CABaseDir: caBasedir, OpenPGPUidEmail: gpgUidEmail, XDeltaPath: "/usr/bin/xdelta"}
|
||||||
|
}
|
||||||
|
|
251
signer/crypto_system.go
Normal file
251
signer/crypto_system.go
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RootCredentials struct {
|
||||||
|
Name string
|
||||||
|
PrivateKeyFile string
|
||||||
|
PublicKeyFile string
|
||||||
|
CertificateFile string
|
||||||
|
DatabaseFile string
|
||||||
|
CRLNumber string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CryptoSystem struct {
|
||||||
|
Name string
|
||||||
|
Roots map[shared.CryptoSystemRootId]*RootCredentials
|
||||||
|
Profiles map[shared.CertificateProfileId]string
|
||||||
|
DigestAlgorithms map[shared.MessageDigestAlgorithmId]x509.SignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (system CryptoSystem) String() string {
|
||||||
|
return system.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (system CryptoSystem) loadCertificate(rootId shared.CryptoSystemRootId) (*x509.Certificate, error) {
|
||||||
|
certificateFile := system.Roots[rootId].CertificateFile
|
||||||
|
pemBytes, err := ioutil.ReadFile(certificateFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"could not load certificate %s: %v",
|
||||||
|
certificateFile,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pemBlock, _ := pem.Decode(pemBytes)
|
||||||
|
if pemBlock.Type != "CERTIFICATE" {
|
||||||
|
log.Warnf(
|
||||||
|
"PEM in %s is probably not a certificate. PEM block has type %s",
|
||||||
|
certificateFile,
|
||||||
|
pemBlock.Type,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
certificate, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"could no parse certificate from %s: %v",
|
||||||
|
certificateFile,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return certificate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (system CryptoSystem) getPrivateKey(rootId shared.CryptoSystemRootId) (crypto.Signer, error) {
|
||||||
|
privateKeyFile := system.Roots[rootId].PrivateKeyFile
|
||||||
|
pemBytes, err := ioutil.ReadFile(privateKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"could not load private key %s: %v",
|
||||||
|
privateKeyFile,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pemBlock, _ := pem.Decode(pemBytes)
|
||||||
|
if pemBlock.Type != "PRIVATE KEY" {
|
||||||
|
log.Warnf(
|
||||||
|
"PEM in %s is probably not a private key. PEM block has type %s",
|
||||||
|
privateKeyFile,
|
||||||
|
pemBlock.Type,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
privateKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"could no parse private key from %s: %v",
|
||||||
|
privateKeyFile,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return privateKey.(*rsa.PrivateKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (system *CryptoSystem) getNextCRLNumber(rootId shared.CryptoSystemRootId) (*big.Int, error) {
|
||||||
|
crlNumberFile := system.Roots[rootId].CRLNumber
|
||||||
|
_, err := os.Stat(crlNumberFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("CRL number file %s does not exist: %v", crlNumberFile, err)
|
||||||
|
return big.NewInt(1), nil
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadFile(crlNumberFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not read CRL number file %s", crlNumberFile)
|
||||||
|
}
|
||||||
|
result, err := stringAsBigInt(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse content of %s as CRL number: %v", crlNumberFile, err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (system *CryptoSystem) bumpCRLNumber(rootId shared.CryptoSystemRootId, current *big.Int) error {
|
||||||
|
serial := current.Int64() + 1
|
||||||
|
crlNumberFile := system.Roots[rootId].CRLNumber
|
||||||
|
outFile, err := ioutil.TempFile(path.Dir(crlNumberFile), "*.txt")
|
||||||
|
defer func() { _ = outFile.Close() }()
|
||||||
|
|
||||||
|
_, err = outFile.WriteString(fmt.Sprintf(
|
||||||
|
"%s\n",
|
||||||
|
strings.ToUpper(strconv.FormatInt(serial, 16)),
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not write new CRL number %d to %s: %v", serial, outFile.Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = outFile.Close(); err != nil {
|
||||||
|
return fmt.Errorf("could not close temporary file %s: %v", outFile.Name(), err)
|
||||||
|
}
|
||||||
|
if err = os.Rename(crlNumberFile, fmt.Sprintf("%s.old", crlNumberFile)); err != nil {
|
||||||
|
return fmt.Errorf("could not rename %s to %s.old: %v", crlNumberFile, crlNumberFile, err)
|
||||||
|
}
|
||||||
|
if err = os.Rename(outFile.Name(), crlNumberFile); err != nil {
|
||||||
|
return fmt.Errorf("could not rename %s to %s: %v", outFile.Name(), crlNumberFile, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringAsBigInt(data []byte) (*big.Int, error) {
|
||||||
|
dataString := strings.TrimSpace(string(data))
|
||||||
|
parseInt, err := strconv.ParseInt(dataString, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse %s as big int: %v", dataString, err)
|
||||||
|
}
|
||||||
|
return big.NewInt(parseInt), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (system *CryptoSystem) loadRevokedCertificatesFromDatabase(rootId shared.CryptoSystemRootId) ([]pkix.RevokedCertificate, error) {
|
||||||
|
databaseFile := system.Roots[rootId].DatabaseFile
|
||||||
|
_, err := os.Stat(databaseFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("openssl certificate database file %s does not exist: %v", databaseFile, err)
|
||||||
|
return []pkix.RevokedCertificate{}, nil
|
||||||
|
}
|
||||||
|
file, err := os.Open(databaseFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not open openssl certificate database file %s: %v", databaseFile, err)
|
||||||
|
}
|
||||||
|
defer func() { _ = file.Close() }()
|
||||||
|
|
||||||
|
result := make([]pkix.RevokedCertificate, 0)
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.Split(scanner.Text(), "\t")
|
||||||
|
if line[0] == "R" {
|
||||||
|
serialNumber, err := stringAsBigInt([]byte(line[3]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse serial number %s as big int: %v", line[3], err)
|
||||||
|
}
|
||||||
|
revokeTs, err := strconv.ParseInt(line[2][:len(line[2])-1], 10, 64)
|
||||||
|
result = append(result, pkix.RevokedCertificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
RevocationTime: time.Unix(revokeTs, 0),
|
||||||
|
Extensions: nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (system *CryptoSystem) recordRevocation(rootId shared.CryptoSystemRootId, certificate *x509.Certificate) (*pkix.RevokedCertificate, error) {
|
||||||
|
databaseFile := system.Roots[rootId].DatabaseFile
|
||||||
|
_, err := os.Stat(databaseFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("openssl certificate database file %s does not exist: %v", databaseFile, err)
|
||||||
|
}
|
||||||
|
inFile, err := os.Open(databaseFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not open openssl certificate database file %s: %v", databaseFile, err)
|
||||||
|
}
|
||||||
|
defer func() { _ = inFile.Close() }()
|
||||||
|
|
||||||
|
outFile, err := ioutil.TempFile(path.Dir(databaseFile), "*.txt")
|
||||||
|
defer func() { _ = outFile.Close() }()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(inFile)
|
||||||
|
writer := bufio.NewWriter(outFile)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
revocationTime := time.Now()
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
parts := strings.Split(line, "\t")
|
||||||
|
serialNumber, err := stringAsBigInt([]byte(parts[3]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse serial number %s as big int: %v", parts[3], err)
|
||||||
|
}
|
||||||
|
if serialNumber == certificate.SerialNumber {
|
||||||
|
line = strings.Join(
|
||||||
|
[]string{"R", parts[1], strconv.FormatInt(revocationTime.Unix(), 10) + "Z", parts[3], parts[4]},
|
||||||
|
"\t",
|
||||||
|
)
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
if _, err = writer.WriteString(fmt.Sprintf("%s\n", line)); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not write '%s' to %s: %v", line, outFile.Name(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = outFile.Close(); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not close temporary file %s: %v", outFile.Name(), err)
|
||||||
|
}
|
||||||
|
if err = inFile.Close(); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not close %s: %v", databaseFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.Rename(databaseFile, fmt.Sprintf("%s.old", databaseFile)); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not rename %s to %s.old: %v", databaseFile, databaseFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.Rename(outFile.Name(), databaseFile); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not rename temporary file %s to %s: %v", outFile.Name(), databaseFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
log.Warnf("entry not found in database")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pkix.RevokedCertificate{
|
||||||
|
SerialNumber: certificate.SerialNumber,
|
||||||
|
RevocationTime: revocationTime,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ func (p *PortHandler) MainLoop() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debugf("received command %v", command)
|
log.Debugf("received command %v", command)
|
||||||
response, err := p.processor.Process(*command)
|
response, err := p.processor.Process(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error processing command: %v", err)
|
log.Errorf("error processing command: %v", err)
|
||||||
close(p.commandChan)
|
close(p.commandChan)
|
||||||
|
@ -207,6 +207,6 @@ func NewSignerProcess(port io.ReadWriteCloser) *PortHandler {
|
||||||
port: port,
|
port: port,
|
||||||
errors: errorChan,
|
errors: errorChan,
|
||||||
commandChan: make(chan *datastructures.SignerRequest, 1),
|
commandChan: make(chan *datastructures.SignerRequest, 1),
|
||||||
processor: &CommandProcessor{},
|
processor: NewCommandProcessor(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
131
signer/protocol_elements.go
Normal file
131
signer/protocol_elements.go
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
|
||||||
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CsX509 shared.CryptoSystemId = 1
|
||||||
|
CsOpenPGP shared.CryptoSystemId = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
X509RootDefault shared.CryptoSystemRootId = 0
|
||||||
|
X509RootClass3 shared.CryptoSystemRootId = 1
|
||||||
|
X509RootClass3s shared.CryptoSystemRootId = 2
|
||||||
|
X509Root3 shared.CryptoSystemRootId = 3
|
||||||
|
X509Root4 shared.CryptoSystemRootId = 4
|
||||||
|
X509Root5 shared.CryptoSystemRootId = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
X509ProfileClient shared.CertificateProfileId = 0
|
||||||
|
X509ProfileClientOrg shared.CertificateProfileId = 1
|
||||||
|
X509ProfileClientCodesign shared.CertificateProfileId = 2
|
||||||
|
X509ProfileClientMachine shared.CertificateProfileId = 3
|
||||||
|
X509ProfileClientAds shared.CertificateProfileId = 4
|
||||||
|
X509ProfileServer shared.CertificateProfileId = 5
|
||||||
|
X509ProfileServerOrg shared.CertificateProfileId = 6
|
||||||
|
X509ProfileServerJabber shared.CertificateProfileId = 7
|
||||||
|
X509ProfileOCSP shared.CertificateProfileId = 8
|
||||||
|
X509ProfileTimestamp shared.CertificateProfileId = 9
|
||||||
|
X509ProfileProxy shared.CertificateProfileId = 10
|
||||||
|
X509ProfileSubCA shared.CertificateProfileId = 11
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
X509MDDefault shared.MessageDigestAlgorithmId = 0
|
||||||
|
X509MDMd5 shared.MessageDigestAlgorithmId = 1
|
||||||
|
X509MDSha1 shared.MessageDigestAlgorithmId = 2
|
||||||
|
X509MDRipeMD160 shared.MessageDigestAlgorithmId = 3
|
||||||
|
X509MDSha256 shared.MessageDigestAlgorithmId = 8
|
||||||
|
X509MDSha384 shared.MessageDigestAlgorithmId = 9
|
||||||
|
X509MDSha512 shared.MessageDigestAlgorithmId = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OpenPGPRoot0 shared.CryptoSystemRootId = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OpenPGPDefaultProfile shared.CertificateProfileId = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OpenPGPDefaultMD shared.MessageDigestAlgorithmId = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCommandProcessor() *CommandProcessor {
|
||||||
|
cryptoSystems := make(map[shared.CryptoSystemId]*CryptoSystem)
|
||||||
|
cryptoSystems[CsX509] = &CryptoSystem{
|
||||||
|
Name: "X.509",
|
||||||
|
Roots: map[shared.CryptoSystemRootId]*RootCredentials{
|
||||||
|
X509RootDefault: {
|
||||||
|
Name: "openssl",
|
||||||
|
PrivateKeyFile: "/srv/ca/CA/private/ca.key.pem",
|
||||||
|
CertificateFile: "/srv/ca/CA/ca.crt.pem",
|
||||||
|
DatabaseFile: "/srv/ca/CA/index.txt",
|
||||||
|
CRLNumber: "/srv/ca/CA/crlnumber",
|
||||||
|
},
|
||||||
|
X509RootClass3: {
|
||||||
|
Name: "class3",
|
||||||
|
PrivateKeyFile: "/srv/ca/class3/private/ca.key.pem",
|
||||||
|
CertificateFile: "/srv/ca/class3/ca.crt.pem",
|
||||||
|
DatabaseFile: "/srv/ca/class3/index.txt",
|
||||||
|
CRLNumber: "/srv/ca/class3/crlnumber",
|
||||||
|
},
|
||||||
|
X509RootClass3s: {Name: "class3s"},
|
||||||
|
X509Root3: {Name: "root3"},
|
||||||
|
X509Root4: {Name: "root4"},
|
||||||
|
X509Root5: {Name: "root5"},
|
||||||
|
},
|
||||||
|
Profiles: map[shared.CertificateProfileId]string{
|
||||||
|
X509ProfileClient: "client",
|
||||||
|
X509ProfileClientOrg: "client-org",
|
||||||
|
X509ProfileClientCodesign: "client-codesign",
|
||||||
|
X509ProfileClientMachine: "client-machine",
|
||||||
|
X509ProfileClientAds: "client-ads",
|
||||||
|
X509ProfileServer: "server",
|
||||||
|
X509ProfileServerOrg: "server-org",
|
||||||
|
X509ProfileServerJabber: "server-jabber",
|
||||||
|
X509ProfileOCSP: "ocsp",
|
||||||
|
X509ProfileTimestamp: "timestamp",
|
||||||
|
X509ProfileProxy: "proxy",
|
||||||
|
X509ProfileSubCA: "subca",
|
||||||
|
},
|
||||||
|
// constants for openssl invocations. Should be replaced with
|
||||||
|
// something more useful
|
||||||
|
DigestAlgorithms: map[shared.MessageDigestAlgorithmId]x509.SignatureAlgorithm{
|
||||||
|
X509MDDefault: x509.SHA256WithRSA,
|
||||||
|
X509MDMd5: x509.MD5WithRSA,
|
||||||
|
X509MDSha1: x509.SHA1WithRSA,
|
||||||
|
X509MDRipeMD160: x509.UnknownSignatureAlgorithm,
|
||||||
|
X509MDSha256: x509.SHA256WithRSA,
|
||||||
|
X509MDSha384: x509.SHA384WithRSA,
|
||||||
|
X509MDSha512: x509.SHA512WithRSA,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptoSystems[CsOpenPGP] = &CryptoSystem{
|
||||||
|
Name: "OpenPGP",
|
||||||
|
Roots: map[shared.CryptoSystemRootId]*RootCredentials{
|
||||||
|
OpenPGPRoot0: {
|
||||||
|
Name: "OpenPGP Root",
|
||||||
|
PrivateKeyFile: "secring0.gpg",
|
||||||
|
PublicKeyFile: "pubring0.gpg",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Profiles: map[shared.CertificateProfileId]string{
|
||||||
|
OpenPGPDefaultProfile: "default",
|
||||||
|
},
|
||||||
|
// constants for gnupg cert-digest-algo parameter. Should be replaced with
|
||||||
|
// something more useful
|
||||||
|
DigestAlgorithms: map[shared.MessageDigestAlgorithmId]x509.SignatureAlgorithm{
|
||||||
|
OpenPGPDefaultMD: x509.SHA256WithRSA,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CommandProcessor{CryptoSystems: cryptoSystems, Settings: NewCommandProcessorSettings()}
|
||||||
|
}
|
Loading…
Reference in a new issue