Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for setting digest algo #227

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions cmd/scepserver/scepserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"flag"
"fmt"
"github.com/smallstep/pkcs7"
"net/http"
"os"
"os/signal"
"path/filepath"
"strconv"
"strings"
"syscall"

"github.com/micromdm/scep/v2/csrverifier"
Expand All @@ -29,6 +33,19 @@ var (
version = "unknown"
)

var digestStringOIMap = map[string]asn1.ObjectIdentifier{
"SHA1": pkcs7.OIDDigestAlgorithmSHA1,
"SHA256": pkcs7.OIDDigestAlgorithmSHA256,
"SHA384": pkcs7.OIDDigestAlgorithmSHA384,
"SHA512": pkcs7.OIDDigestAlgorithmSHA512,
"DSA": pkcs7.OIDDigestAlgorithmDSA,
"DSASHA1": pkcs7.OIDDigestAlgorithmDSASHA1,
"ECDSASHA1": pkcs7.OIDDigestAlgorithmECDSASHA1,
"ECDSASHA256": pkcs7.OIDDigestAlgorithmECDSASHA256,
"ECDSASHA384": pkcs7.OIDDigestAlgorithmECDSASHA384,
"ECDSASHA512": pkcs7.OIDDigestAlgorithmECDSASHA512,
}

func main() {
var caCMD = flag.NewFlagSet("ca", flag.ExitOnError)
{
Expand All @@ -40,7 +57,6 @@ func main() {
}
}

//main flags
var (
flVersion = flag.Bool("version", false, "prints version information")
flHTTPAddr = flag.String("http-addr", envString("SCEP_HTTP_ADDR", ""), "http listen address. defaults to \":8080\"")
Expand All @@ -51,6 +67,7 @@ func main() {
flClAllowRenewal = flag.String("allowrenew", envString("SCEP_CERT_RENEW", "14"), "do not allow renewal until n days before expiry, set to 0 to always allow")
flChallengePassword = flag.String("challenge", envString("SCEP_CHALLENGE_PASSWORD", ""), "enforce a challenge password")
flCSRVerifierExec = flag.String("csrverifierexec", envString("SCEP_CSR_VERIFIER_EXEC", ""), "will be passed the CSRs for verification")
flDigestAlgo = flag.String("digest-algo", envString("SCEP_DIGEST_ALGO", "SHA256"), "digest algorithm for pkcs7")
flDebug = flag.Bool("debug", envBool("SCEP_LOG_DEBUG"), "enable debug logging")
flLogJSON = flag.Bool("log-json", envBool("SCEP_LOG_JSON"), "output JSON logs")
flSignServerAttrs = flag.Bool("sign-server-attrs", envBool("SCEP_SIGN_SERVER_ATTRS"), "sign cert attrs for server usage")
Expand Down Expand Up @@ -83,6 +100,17 @@ func main() {
httpAddr = ":" + *flPort
}

var digestAlgo asn1.ObjectIdentifier
digestSet := setByUser("digest-algo", "SCEP_DIGEST_ALGO")
if digestSet {
d, err := parseUserDefinedDigestAlgo(*flDigestAlgo)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
digestAlgo = d
}

var logger log.Logger
{

Expand Down Expand Up @@ -154,7 +182,7 @@ func main() {
if csrVerifier != nil {
signer = csrverifier.Middleware(csrVerifier, signer)
}
svc, err = scepserver.NewService(crts[0], key, signer, scepserver.WithLogger(logger))
svc, err = scepserver.NewService(crts[0], key, signer, scepserver.WithLogger(logger), scepserver.WithDigestAlgo(digestAlgo))
if err != nil {
lginfo.Log("err", err)
os.Exit(1)
Expand Down Expand Up @@ -317,3 +345,14 @@ func setByUser(flagName, envName string) bool {
_, envSet := os.LookupEnv(envName)
return flagSet || envSet
}

func parseUserDefinedDigestAlgo(s string) (asn1.ObjectIdentifier, error) {
if s == "" {
//no value is fine, it will default to SHA256
return nil, nil
}
if v, ok := digestStringOIMap[strings.ToUpper(s)]; ok {
return v, nil
}
return nil, errors.New("invalid value for digest algo")
}
41 changes: 36 additions & 5 deletions scep/scep.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,25 @@ func WithCertsSelector(selector CertsSelector) Option {
}
}

// WithDigestAlgorithm sets the PKCS #7 digest algorithm. Note that
// in go versions >=1.18 setting the algo as SHA1 will cause x509
// verify function to failed due to unsecure algo.
// This option is effective when used with NewCSRRequest function. In
// this case, the PKCS #7 digest algo will be set to the specified value
func WithDigestAlgorithm(identifier asn1.ObjectIdentifier) Option {
return func(c *config) {
c.digestAlgorithm = identifier
}
}

// Option specifies custom configuration for SCEP.
type Option func(*config)

type config struct {
logger log.Logger
caCerts []*x509.Certificate // specified if CA certificates have already been retrieved
certsSelector CertsSelector
logger log.Logger
caCerts []*x509.Certificate // specified if CA certificates have already been retrieved
certsSelector CertsSelector
digestAlgorithm asn1.ObjectIdentifier
}

// PKIMessage defines the possible SCEP message types
Expand Down Expand Up @@ -387,7 +399,7 @@ func (msg *PKIMessage) DecryptPKIEnvelope(cert *x509.Certificate, key *rsa.Priva
}
}

func (msg *PKIMessage) Fail(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, info FailInfo) (*PKIMessage, error) {
func (msg *PKIMessage) Fail(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, info FailInfo, digestAlgo asn1.ObjectIdentifier) (*PKIMessage, error) {
config := pkcs7.SignerInfoConfig{
ExtraSignedAttributes: []pkcs7.Attribute{
{
Expand Down Expand Up @@ -418,6 +430,12 @@ func (msg *PKIMessage) Fail(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey,
}

sd, err := pkcs7.NewSignedData(nil)
if digestAlgo != nil && len(digestAlgo) > 0 {
sd.SetDigestAlgorithm(digestAlgo)
} else {
//default to sha256 since golang 1.18 prohibits sha1
sd.SetDigestAlgorithm(pkcs7.OIDDigestAlgorithmSHA256)
}
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -451,7 +469,7 @@ func (msg *PKIMessage) Fail(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey,
}

// Success returns a new PKIMessage with CertRep data using an already-issued certificate
func (msg *PKIMessage) Success(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, crt *x509.Certificate) (*PKIMessage, error) {
func (msg *PKIMessage) Success(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKey, crt *x509.Certificate, digestAlgo asn1.ObjectIdentifier) (*PKIMessage, error) {
// check if CSRReqMessage has already been decrypted
if msg.CSRReqMessage.CSR == nil {
if err := msg.DecryptPKIEnvelope(crtAuth, keyAuth); err != nil {
Expand Down Expand Up @@ -498,6 +516,12 @@ func (msg *PKIMessage) Success(crtAuth *x509.Certificate, keyAuth *rsa.PrivateKe
}

signedData, err := pkcs7.NewSignedData(e7)
if digestAlgo != nil && len(digestAlgo) > 0 {
signedData.SetDigestAlgorithm(digestAlgo)
} else {
//default to sha256 since golang 1.18 prohibits sha1
signedData.SetDigestAlgorithm(pkcs7.OIDDigestAlgorithmSHA256)
}
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -571,12 +595,19 @@ func NewCSRRequest(csr *x509.CertificateRequest, tmpl *PKIMessage, opts ...Optio
}
return nil, errors.New("no CA/RA recipients")
}

e7, err := pkcs7.Encrypt(derBytes, recipients)
if err != nil {
return nil, err
}

signedData, err := pkcs7.NewSignedData(e7)
if conf.digestAlgorithm != nil && len(conf.digestAlgorithm) > 0 {
signedData.SetDigestAlgorithm(conf.digestAlgorithm)
} else {
//default to sha256 since golang 1.18 prohibits sha1
signedData.SetDigestAlgorithm(pkcs7.OIDDigestAlgorithmSHA256)
}
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion scep/scep_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func TestSignCSR(t *testing.T) {
if err != nil {
t.Fatal(err)
}
certRep, err := msg.Success(cacert, cakey, crt)
certRep, err := msg.Success(cacert, cakey, crt, nil)
if err != nil {
t.Fatal(err)
}
Expand Down
15 changes: 13 additions & 2 deletions server/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"errors"

"github.com/micromdm/scep/v2/scep"
Expand Down Expand Up @@ -51,6 +52,9 @@ type service struct {

/// info logging is implemented in the service middleware layer.
debugLogger log.Logger

//disgest algo
digestAlgo asn1.ObjectIdentifier
}

func (svc *service) GetCACaps(ctx context.Context) ([]byte, error) {
Expand Down Expand Up @@ -86,11 +90,11 @@ func (svc *service) PKIOperation(ctx context.Context, data []byte) ([]byte, erro
}
if err != nil {
svc.debugLogger.Log("msg", "failed to sign CSR", "err", err)
certRep, err := msg.Fail(svc.crt, svc.key, scep.BadRequest)
certRep, err := msg.Fail(svc.crt, svc.key, scep.BadRequest, svc.digestAlgo)
return certRep.Raw, err
}

certRep, err := msg.Success(svc.crt, svc.key, crt)
certRep, err := msg.Success(svc.crt, svc.key, crt, svc.digestAlgo)
return certRep.Raw, err
}

Expand All @@ -110,6 +114,13 @@ func WithLogger(logger log.Logger) ServiceOption {
}
}

func WithDigestAlgo(identifier asn1.ObjectIdentifier) ServiceOption {
return func(s *service) error {
s.digestAlgo = identifier
return nil
}
}

// WithAddlCA appends an additional certificate to the slice of CA certs
func WithAddlCA(ca *x509.Certificate) ServiceOption {
return func(s *service) error {
Expand Down