From 4b867f1d044eca6454c9714513bc808adb249b44 Mon Sep 17 00:00:00 2001 From: Jesse Peterson Date: Wed, 16 Nov 2016 15:38:45 -0800 Subject: [PATCH] Allow passing in PEM MDM cert and key (#60) closes #55 --- app/config.go | 2 +- app/services.go | 91 +++++++++++++++++++++++++++++++++++++----- enroll/certificates.go | 29 -------------- enroll/service.go | 8 +--- 4 files changed, 83 insertions(+), 47 deletions(-) delete mode 100644 enroll/certificates.go diff --git a/app/config.go b/app/config.go index 6772e28d..a925d042 100644 --- a/app/config.go +++ b/app/config.go @@ -53,7 +53,7 @@ func loadConfig() (*Config, error) { // APNS config. Can be either two files or a combined .p12 (like the one exported from keychain access") flPushCert = flag.String("push-cert", envString("MICROMDM_PUSH_CERT", ""), "path to push certificate") flPushPass = flag.String("push-password", envString("MICROMDM_PUSH_PASSWORD", ""), "push certificate password") - flPushKey = flag.String("push-key", envString("MICROMDM_PUSH_KEY", ""), "path to push certificate private key(if not using a single .p12 file)") + flPushKey = flag.String("push-key", envString("MICROMDM_PUSH_KEY", ""), "path to push certificate private key (if not using a single .p12 file)") ) flag.Parse() diff --git a/app/services.go b/app/services.go index 479b79f3..ae4ae386 100644 --- a/app/services.go +++ b/app/services.go @@ -1,6 +1,11 @@ package app import ( + "crypto/rsa" + "crypto/x509" + "encoding/asn1" + "encoding/pem" + "errors" "io/ioutil" pushcertificate "github.com/RobotsAndPencils/buford/certificate" @@ -20,6 +25,8 @@ import ( "github.com/micromdm/micromdm/enroll" "github.com/micromdm/micromdm/management" "github.com/micromdm/micromdm/workflow" + + "golang.org/x/crypto/pkcs12" ) // setupServices uses the values from the config to set up the various components @@ -33,6 +40,7 @@ func setupServices(config *Config, logger log.Logger) (*serviceManager, error) { sm.setupWorkflowDatastore() sm.setupCertificateDatastore() + sm.loadPushCerts() sm.setupPushService() sm.setupCommandService() @@ -55,6 +63,7 @@ type serviceManager struct { ApplicationDatastore application.Datastore PushService *push.Service + pushServiceCert CommandService command.Service ManagementService management.Service @@ -68,15 +77,80 @@ type serviceManager struct { err error } +type pushServiceCert struct { + *x509.Certificate + PrivateKey interface{} +} + +func (s *serviceManager) loadPushCerts() { + if s.err != nil { + return + } + + if s.APNS.PrivateKeyPath == "" { + var pkcs12Data []byte + pkcs12Data, s.err = ioutil.ReadFile(s.APNS.CertificatePath) + if s.err != nil { + return + } + s.pushServiceCert.PrivateKey, s.pushServiceCert.Certificate, s.err = pkcs12.Decode(pkcs12Data, s.APNS.PrivateKeyPass) + return + } + + var pemData []byte + pemData, s.err = ioutil.ReadFile(s.APNS.CertificatePath) + if s.err != nil { + return + } + + pemBlock, _ := pem.Decode(pemData) + if pemBlock == nil { + s.err = errors.New("invalid PEM data for cert") + return + } + s.pushServiceCert.Certificate, s.err = x509.ParseCertificate(pemBlock.Bytes) + if s.err != nil { + return + } + + pemData, s.err = ioutil.ReadFile(s.APNS.PrivateKeyPath) + if s.err != nil { + return + } + + pemBlock, _ = pem.Decode(pemData) + if pemBlock == nil { + s.err = errors.New("invalid PEM data for privkey") + return + } + s.pushServiceCert.PrivateKey, s.err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes) +} + +var oidASN1UserID = asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 1} + +func topicFromCert(cert *x509.Certificate) (string, error) { + for _, v := range cert.Subject.Names { + if v.Type.Equal(oidASN1UserID) { + return v.Value.(string), nil + } + } + + return "", errors.New("Could not find Push Topic (UserID OID) in certificate") +} + func (s *serviceManager) setupEnrollmentService() { if s.err != nil { return } + pushTopic, err := topicFromCert(s.pushServiceCert.Certificate) + if err != nil { + s.err = err + return + } // TODO: clean up order of inputs. Maybe pass *SCEPConfig as an arg? // but if you do, the packages are coupled, better not. s.EnrollmentService, s.err = enroll.NewService( - s.APNS.CertificatePath, - s.APNS.PrivateKeyPass, + pushTopic, s.Enrollment.CACertPath, s.SCEP.RemoteURL, s.SCEP.Challenge, @@ -155,15 +229,10 @@ func (s *serviceManager) setupPushService() { if s.err != nil { return } - cert, key, err := pushcertificate.Load( - s.APNS.CertificatePath, - s.APNS.PrivateKeyPass, - ) - if err != nil { - s.err = err - return - } - client, err := push.NewClient(pushcertificate.TLS(cert, key)) + client, err := push.NewClient(pushcertificate.TLS( + s.pushServiceCert.Certificate, + s.pushServiceCert.PrivateKey.(*rsa.PrivateKey), + )) if err != nil { s.err = err return diff --git a/enroll/certificates.go b/enroll/certificates.go deleted file mode 100644 index 0cf3c756..00000000 --- a/enroll/certificates.go +++ /dev/null @@ -1,29 +0,0 @@ -package enroll - -import ( - "errors" - "golang.org/x/crypto/pkcs12" - "io/ioutil" -) - -const PushTopicASN1 string = "0.9.2342.19200300.100.1.1" - -func GetPushTopicFromPKCS12(certPath string, certPass string) (string, error) { - certData, err := ioutil.ReadFile(certPath) - if err != nil { - return "", err - } - - _, cert, err := pkcs12.Decode(certData, certPass) - if err != nil { - return "", err - } - - for _, v := range cert.Subject.Names { - if v.Type.String() == PushTopicASN1 { - return v.Value.(string), nil - } - } - - return "", errors.New("Could not find Push Topic in the provided pkcs12 bundle.") -} diff --git a/enroll/service.go b/enroll/service.go index 8e41b7fb..c23e1ac2 100644 --- a/enroll/service.go +++ b/enroll/service.go @@ -10,13 +10,9 @@ type Service interface { Enroll(ctx context.Context) (Profile, error) } -func NewService(pushCertPath, pushCertPass, caCertPath, scepURL, scepChallenge, url, tlsCertPath, scepSubject string) (Service, error) { - pushTopic, err := GetPushTopicFromPKCS12(pushCertPath, pushCertPass) - if err != nil { - return nil, err - } - +func NewService(pushTopic, caCertPath, scepURL, scepChallenge, url, tlsCertPath, scepSubject string) (Service, error) { var caCert, tlsCert []byte + var err error if caCertPath != "" { caCert, err = ioutil.ReadFile(caCertPath)