diff --git a/Dockerfile-server b/Dockerfile-server index 28bca29..4677db6 100644 --- a/Dockerfile-server +++ b/Dockerfile-server @@ -80,6 +80,7 @@ COPY --from=builder /opt/app-root/src/go/src/github.com/ibm-messaging/mq-contain COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt # Copy web XML files COPY web /etc/mqm/web +COPY etc/mqm/*.tpl /etc/mqm/ RUN chmod ug+x /usr/local/bin/runmqserver \ && chown mqm:mqm /usr/local/bin/*mq* \ && chmod ug+xs /usr/local/bin/chkmq* \ diff --git a/cmd/runmqdevserver/main.go b/cmd/runmqdevserver/main.go index ca5b17d..b2d9c0f 100644 --- a/cmd/runmqdevserver/main.go +++ b/cmd/runmqdevserver/main.go @@ -153,14 +153,6 @@ func doMain() error { logTerminationf("Error getting queue manager name: %v", err) return err } - ks, set := os.LookupEnv("MQ_TLS_KEYSTORE") - if set { - err = configureTLS(name, ks, os.Getenv("MQ_TLS_PASSPHRASE")) - if err != nil { - logTerminationf("Error configuring TLS: %v", err) - return err - } - } err = configureWeb(name) if err != nil { diff --git a/cmd/runmqdevserver/tls.go b/cmd/runmqdevserver/tls.go deleted file mode 100644 index 69f7a1b..0000000 --- a/cmd/runmqdevserver/tls.go +++ /dev/null @@ -1,166 +0,0 @@ -/* -© Copyright IBM Corporation 2018, 2019 - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package main - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/ibm-messaging/mq-container/internal/command" - "github.com/ibm-messaging/mq-container/internal/keystore" - "github.com/ibm-messaging/mq-container/internal/mqtemplate" -) - -func configureWebTLS(cms *keystore.KeyStore) error { - dir := "/run/runmqdevserver/tls" - ks := keystore.NewJKSKeyStore(filepath.Join(dir, "key.jks"), cms.Password) - ts := keystore.NewJKSKeyStore(filepath.Join(dir, "trust.jks"), cms.Password) - - log.Debug("Creating key store") - err := ks.Create(log) - if err != nil { - return err - } - log.Debug("Creating trust store") - err = ts.Create(log) - if err != nil { - return err - } - log.Debug("Importing keys") - err = ks.Import(cms.Filename, cms.Password) - if err != nil { - return err - } - - webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb" - tlsConfig := filepath.Join(webConfigDir, "tls.xml") - newTLSConfig := filepath.Join(webConfigDir, "tls-dev.xml") - err = os.Remove(tlsConfig) - if err != nil { - return err - } - // we symlink here to prevent issues on restart - err = os.Symlink(newTLSConfig, tlsConfig) - if err != nil { - return err - } - mqmUID, mqmGID, err := command.LookupMQM() - if err != nil { - log.Error(err) - return err - } - err = os.Chown(tlsConfig, mqmUID, mqmGID) - if err != nil { - log.Error(err) - return err - } - - return nil -} - -func configureTLS(qmName string, inputFile string, passPhrase string) error { - log.Debug("Configuring TLS") - - _, err := os.Stat(inputFile) - if err != nil { - return err - } - - // TODO: Use a persisted file (on the volume) instead? - dir := "/run/runmqdevserver/tls" - keyFile := filepath.Join(dir, "key.kdb") - - _, err = os.Stat(dir) - if err != nil { - if os.IsNotExist(err) { - // #nosec G301 - err = os.MkdirAll(dir, 0770) - if err != nil { - return err - } - mqmUID, mqmGID, err := command.LookupMQM() - if err != nil { - log.Error(err) - return err - } - err = os.Chown(dir, mqmUID, mqmGID) - if err != nil { - log.Error(err) - return err - } - } else { - return err - } - } - - cms := keystore.NewCMSKeyStore(keyFile, passPhrase) - - err = cms.Create(log) - if err != nil { - return err - } - - err = cms.CreateStash(log) - if err != nil { - return err - } - - err = cms.Import(inputFile, passPhrase) - if err != nil { - return err - } - - labels, err := cms.GetCertificateLabels() - if err != nil { - return err - } - if len(labels) == 0 { - return fmt.Errorf("unable to find certificate label") - } - log.Debugf("Renaming certificate from %v", labels[0]) - const newLabel string = "devcert" - err = cms.RenameCertificate(labels[0], newLabel) - if err != nil { - return err - } - - var sslCipherSpec string - if os.Getenv("MQ_DEV") == "true" { - sslCipherSpec = "TLS_RSA_WITH_AES_128_CBC_SHA256" - } else { - sslCipherSpec = "' '" - } - - const mqsc string = "/etc/mqm/20-dev-tls.mqsc" - const mqscTemplate string = mqsc + ".tpl" - - err = mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{ - "SSLKeyR": filepath.Join(dir, "key"), - "CertificateLabel": newLabel, - "SSLCipherSpec": sslCipherSpec, - }, log) - if err != nil { - return err - } - - err = configureWebTLS(cms) - if err != nil { - return err - } - - return nil -} diff --git a/cmd/runmqserver/main.go b/cmd/runmqserver/main.go index 00ce9ec..d1b918a 100644 --- a/cmd/runmqserver/main.go +++ b/cmd/runmqserver/main.go @@ -147,6 +147,18 @@ func doMain() error { // Print out versioning information logVersionInfo() + err = ConfigureTLSKeystores() + if err != nil { + logTermination(err) + return err + } + + err = ConfigureTLS(*devFlag) + if err != nil { + logTermination(err) + return err + } + err = postInit(name) if err != nil { logTermination(err) diff --git a/cmd/runmqserver/post_init.go b/cmd/runmqserver/post_init.go index c4c992a..201270c 100644 --- a/cmd/runmqserver/post_init.go +++ b/cmd/runmqserver/post_init.go @@ -23,6 +23,13 @@ import ( func postInit(name string) error { enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER") if enableWebServer == "true" || enableWebServer == "1" { + // Configure TLS for Web Console first + if webkeyStoreName != "" { + err := ConfigureWebTLS() + if err != nil { + return err + } + } // Configure Single-Sign-On for the web server (if enabled) enableSSO := os.Getenv("MQ_BETA_ENABLE_SSO") diff --git a/cmd/runmqserver/tls.go b/cmd/runmqserver/tls.go new file mode 100644 index 0000000..b2757c6 --- /dev/null +++ b/cmd/runmqserver/tls.go @@ -0,0 +1,776 @@ +/* +© Copyright IBM Corporation 2018, 2019 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "bufio" + "fmt" + "io/ioutil" + pwr "math/rand" + "os" + "path/filepath" + "strings" + "time" + + "crypto/rand" + "crypto/sha1" + "crypto/x509" + "encoding/pem" + + "github.com/ibm-messaging/mq-container/internal/command" + "github.com/ibm-messaging/mq-container/internal/keystore" + "github.com/ibm-messaging/mq-container/internal/mqtemplate" + pkcs "software.sslmate.com/src/go-pkcs12" +) + +const keystoreDir = "/run/runmqserver/tls/" + +// CIPDefaultLabel is the default certificate label used by Cloud Integration Platform +const CIPDefaultLabel = "default" + +// P12TrustStoreName is the name of the PKCS#12 truststore used by the webconsole +const P12TrustStoreName = "trust.p12" + +// CMSKeyStoreName is the name of the CMS Keystore used by the queue manager +const CMSKeyStoreName = "key.kdb" + +// KeyStorePasswords The password of the keystores. Should never be printed! +var keyStorePasswords string + +// KeyDir is the location of the certificate keys to import +const KeyDir = "/etc/mqm/pki/keys" + +// TrustDir is the location of the Certifates to add +const TrustDir = "/etc/mqm/pki/trust" + +// Used to track certificates and keys we've found to add +var p12TrustCerts []*pem.Block +var cmsTrustCerts []*pem.Block +var p12TrustKnownFingerPrints []string +var cmsTrustKnownFingerPrints []string +var cmsKeyLabels []string + +// The keystore objects +var p12TrustStore *keystore.KeyStore +var cmsKeyStore *keystore.KeyStore + +// Variables for storing the label of the certificate to use +var webkeyStoreName string +var qmKeyLabel string + +// Tracks whether the keystores have been configured +var keystoresConfigured bool = false + +func getCertFingerPrint(block *pem.Block) (string, error) { + // Add to future truststore and known certs (if not already there) + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return "", fmt.Errorf("could not parse x509 certificate: %v", err) + } + sha1Sum := sha1.Sum(cert.Raw) + sha1str := string(sha1Sum[:]) + + return sha1str, nil +} + +// Add to future pkcs#12 Truststore and known certs (if not already there) +func addCertToP12TrustStoreNoDups(block *pem.Block) error { + sha1str, err := getCertFingerPrint(block) + if err != nil { + return err + } + known := false + for _, t := range p12TrustKnownFingerPrints { + if t == sha1str { + known = true + break + } + } + + if !known { + p12TrustCerts = append(p12TrustCerts, block) + p12TrustKnownFingerPrints = append(p12TrustKnownFingerPrints, sha1str) + } + return nil +} + +// Add to CMS Keystore known certs (if not already there) and add to the +// CMS Keystore if "addToKeystore" is true. +func addCertToCMSKeystoreNoDups(block *pem.Block, addToKeystore bool) error { + sha1str, err := getCertFingerPrint(block) + if err != nil { + return err + } + known := false + for _, t := range cmsTrustKnownFingerPrints { + if t == sha1str { + known = true + break + } + } + + if !known { + // Sometimes we don't want to add to the CMS keystore trust here. + // For example if it will be imported with the key later. + if addToKeystore { + cmsTrustCerts = append(cmsTrustCerts, block) + } + cmsTrustKnownFingerPrints = append(cmsTrustKnownFingerPrints, sha1str) + } + return nil +} + +// Generates a random 12 character password from the characters a-z, A-Z, 0-9. +func generateRandomPassword() { + pwr.Seed(time.Now().Unix()) + validChars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + validcharArray := []byte(validChars) + password := "" + for i := 0; i < 12; i++ { + password = password + string(validcharArray[pwr.Intn(len(validcharArray))]) + } + + keyStorePasswords = password +} + +// Creates the PKCS#12 Truststore and the CMS Keystore. +func generateAllStores() error { + generateRandomPassword() + + // Create the keystore Directory (if it doesn't already exist) + os.MkdirAll(keystoreDir, 0775) + + p12TrustStore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, P12TrustStoreName), keyStorePasswords) + err := p12TrustStore.Create(log) + if err != nil { + return fmt.Errorf("Failed to create PKCS#12 TrustStore: %v", err) + } + + cmsKeyStore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, CMSKeyStoreName), keyStorePasswords) + err = cmsKeyStore.Create(log) + if err != nil { + return fmt.Errorf("Failed to create CMS KeyStore: %v", err) + } + + return nil +} + +// processKeys walks through the KeyDir directory and imports any keys it finds to individual PKCS#12 keystores +// and the CMS KeyStore. The label it uses is the name of the directory if finds the keys in. +func processKeys() error { + keyList, err := ioutil.ReadDir(KeyDir) + if err == nil && len(keyList) > 0 { + // Found some keys, verify the contents + for _, key := range keyList { + keys, _ := ioutil.ReadDir(filepath.Join(KeyDir, key.Name())) + keyLabel := key.Name() + keyfilename := "" + var keyfile interface{} + var certFile *x509.Certificate + var caFile []*x509.Certificate + + // find the keyfile name + for _, a := range keys { + if strings.HasSuffix(a.Name(), ".key") { + keyFile, err := ioutil.ReadFile(filepath.Join(KeyDir, key.Name(), a.Name())) + if err != nil { + return fmt.Errorf("Could not read keyfile %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err) + } + block, _ := pem.Decode(keyFile) + if block == nil { + return fmt.Errorf("Could not decode keyfile %s: pem.Decode returned nil", filepath.Join(KeyDir, key.Name(), a.Name())) + } + + //Test whether it is PKCS1 + keyfile, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + // Before we fail check whether it is PKCS8 + keyfile, err = x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + fmt.Printf("key %s ParsePKCS1/8PrivateKey ERR: %v\n", filepath.Join(KeyDir, key.Name(), a.Name()), err) + return err + } + //It was PKCS8 afterall + } + keyfilename = a.Name() + } + } + if keyfile == nil { + break + } + + // Find out what the keyfile was called without the extension + prefix := keyfilename[0 : len(keyfilename)-len(filepath.Ext(keyfilename))] + + for _, a := range keys { + if strings.HasSuffix(a.Name(), ".key") { + continue + } + if strings.HasPrefix(a.Name(), prefix) && strings.HasSuffix(a.Name(), ".crt") { + cert, err := ioutil.ReadFile(filepath.Join(KeyDir, key.Name(), a.Name())) + if err != nil { + return fmt.Errorf("Could not read file %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err) + } + block, _ := pem.Decode(cert) + if block == nil { + return fmt.Errorf("Could not decode certificate %s: pem.Decode returned nil", filepath.Join(KeyDir, key.Name(), a.Name())) + } + certFile, err = x509.ParseCertificate(block.Bytes) + if err != nil { + return fmt.Errorf("Could not parse certificate %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err) + } + // Add to the dup list for the CMS keystore but not the PKCS#12 Truststore + addCertToCMSKeystoreNoDups(block, false) + + } else if strings.HasSuffix(a.Name(), ".crt") { + remainder, err := ioutil.ReadFile(filepath.Join(KeyDir, key.Name(), a.Name())) + if err != nil { + return fmt.Errorf("Could not read file %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err) + } + + for string(remainder) != "" { + var block *pem.Block + block, remainder = pem.Decode(remainder) + // If we can't decode the CA certificate then just exit. + if block == nil { + break + } + + // Add to the dup list for the CMS keystore + addCertToCMSKeystoreNoDups(block, false) + + // Add to the p12 truststore + addCertToP12TrustStoreNoDups(block) + + caCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return fmt.Errorf("Could not parse CA certificate %s: %v", filepath.Join(KeyDir, key.Name(), a.Name()), err) + } + + caFile = append(caFile, caCert) + } + } + } + + // Create p12 keystore + file, err := pkcs.Encode(rand.Reader, keyfile, certFile, caFile, keyStorePasswords) + if err != nil { + return fmt.Errorf("Could not encode PKCS#12 Keystore %s: %v", keyLabel+".p12", err) + } + + err = ioutil.WriteFile(filepath.Join(keystoreDir, keyLabel+".p12"), file, 0644) + if err != nil { + return fmt.Errorf("Could not write PKCS#12 Keystore %s: %v", filepath.Join(keystoreDir, keyLabel+".p12"), err) + } + + // Add to the CMS keystore + err = cmsKeyStore.Import(filepath.Join(keystoreDir, keyLabel+".p12"), keyStorePasswords) + if err != nil { + return fmt.Errorf("Could not import keys from %s into CMS Keystore: %v", filepath.Join(keystoreDir, keyLabel+".p12"), err) + } + + // Relabel it + allLabels, err := cmsKeyStore.GetCertificateLabels() + if err != nil { + return fmt.Errorf("Could not list keys in CMS Keystore: %v", err) + } + relabelled := false + for _, cl := range allLabels { + found := false + for _, kl := range cmsKeyLabels { + if strings.Trim(cl, "\"") == kl { + found = true + break + } + } + if !found { + // This is the one to rename + err = cmsKeyStore.RenameCertificate(strings.Trim(cl, "\""), keyLabel) + if err != nil { + return err + } + relabelled = true + cmsKeyLabels = append(cmsKeyLabels, keyLabel) + break + } + } + + if !relabelled { + return fmt.Errorf("Unable to find the added key for %s in CMS keystore", keyLabel) + } + + // First keystore so mark as the one to use with web console. + if webkeyStoreName == "" { + webkeyStoreName = keyLabel + ".p12" + } + // First key added to CMS Keystore so mark it as the one to use with the queue manager. + if qmKeyLabel == "" { + qmKeyLabel = keyLabel + } + } + } + return nil +} + +// processTrustCertificates walks through the TrustDir directory and adds any certificates it finds +// to the PKCS#12 Truststore and the CMS KeyStore as long as has not already been added. +func processTrustCertificates() error { + certList, err := ioutil.ReadDir(TrustDir) + if err == nil && len(certList) > 0 { + // Found some keys, verify the contents + for _, cert := range certList { + certs, _ := ioutil.ReadDir(filepath.Join(TrustDir, cert.Name())) + for _, a := range certs { + if strings.HasSuffix(a.Name(), ".crt") { + remainder, err := ioutil.ReadFile(filepath.Join(TrustDir, cert.Name(), a.Name())) + if err != nil { + return fmt.Errorf("Could not read file %s: %v", filepath.Join(TrustDir, cert.Name(), a.Name()), err) + } + + for string(remainder) != "" { + var block *pem.Block + block, remainder = pem.Decode(remainder) + if block == nil { + break + } + + // Add to the CMS keystore + addCertToCMSKeystoreNoDups(block, true) + + // Add to the p12 truststore + addCertToP12TrustStoreNoDups(block) + } + } + } + } + } + // We've potentially created two lists of certificates to import. Add them both to relevant Truststores + if len(p12TrustCerts) > 0 { + // Do P12 TrustStore first + temporaryPemFile := filepath.Join("/tmp", "trust.pem") + + err := writeCertsToFile(temporaryPemFile, p12TrustCerts) + if err != nil { + return err + } + + err = p12TrustStore.AddNoLabel(temporaryPemFile) + if err != nil { + return fmt.Errorf("Could not add certificates to PKCS#12 Truststore: %v", err) + } + } + + if len(cmsTrustCerts) > 0 { + // Now the CMS Keystore + temporaryPemFile := filepath.Join("/tmp", "cmsTrust.pem") + + err = writeCertsToFile(temporaryPemFile, cmsTrustCerts) + if err != nil { + return err + } + + err = cmsKeyStore.AddNoLabel(temporaryPemFile) + if err != nil { + return fmt.Errorf("Could not add certificates to CMS keystore: %v", err) + } + } + return nil +} + +// Writes a given list of certificates to a file. +func writeCertsToFile(file string, certs []*pem.Block) error { + f, err := os.Create(file) + if err != nil { + return fmt.Errorf("writeCertsToFile: Could not create file %s: %v", file, err) + } + defer f.Close() + + w := bufio.NewWriter(f) + + for i, c := range certs { + err := pem.Encode(w, c) + if err != nil { + return fmt.Errorf("writeCertsToFile: Could not encode certificate number %d: %v", i, err) + } + w.Flush() + } + return nil +} + +// ConfigureTLSKeystores sets up the TLS Trust and Keystores for use +func ConfigureTLSKeystores() error { + err := generateAllStores() + if err != nil { + return err + } + + err = handleCIPGeneratedCerts() + if err != nil { + return err + } + + err = expandOldTLSVarible() + if err != nil { + return err + } + + err = processKeys() + if err != nil { + return err + } + + err = processTrustCertificates() + if err != nil { + return err + } + + // set that the keystores have been configured + keystoresConfigured = true + + return nil +} + +// ConfigureWebTLS configures TLS for Web Console +func ConfigureWebTLS() error { + if !keystoresConfigured { + err := ConfigureTLSKeystores() + if err != nil { + return err + } + } + + const webConfigDir string = "/etc/mqm/web/installations/Installation1/servers/mqweb" + const tls string = "tls.xml" + const tlsTemplate string = tls + ".tpl" + + tlsConfig := filepath.Join(webConfigDir, tls) + newTLSConfig := filepath.Join(webConfigDir, tlsTemplate) + err := os.Remove(tlsConfig) + if err != nil { + return fmt.Errorf("Could not delete file %s: %v", tlsConfig, err) + } + // we symlink here to prevent issues on restart + err = os.Symlink(newTLSConfig, tlsConfig) + if err != nil { + return fmt.Errorf("Could not create symlink %s->%s: %v", newTLSConfig, tlsConfig, err) + } + mqmUID, mqmGID, err := command.LookupMQM() + if err != nil { + return fmt.Errorf("Could not find mqm user or group: %v", err) + } + err = os.Chown(tlsConfig, mqmUID, mqmGID) + if err != nil { + return fmt.Errorf("Could change ownership of %s to mqm: %v", tlsConfig, err) + } + + return nil +} + +// ConfigureTLSDev configures TLS for developer defaults +func ConfigureTLSDev() error { + if !keystoresConfigured { + err := ConfigureTLSKeystores() + if err != nil { + return err + } + } + if qmKeyLabel == "" { + // We haven't set a key to use so don't set the QM to use TLS + return nil + } + const mqsc string = "/etc/mqm/20-dev-tls.mqsc" + const mqscTemplate string = mqsc + ".tpl" + const sslCipherSpec string = "TLS_RSA_WITH_AES_128_CBC_SHA256" + + if os.Getenv("MQ_DEV") == "true" { + err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{ + "SSLCipherSpec": sslCipherSpec, + }, log) + if err != nil { + return err + } + } else { + _, err := os.Stat(mqsc) + if !os.IsNotExist(err) { + err = os.Remove(mqsc) + if err != nil { + log.Errorf("Error removing file %s: %v", mqsc, err) + return err + } + } + } + + return nil +} + +// ConfigureTLS configures TLS for queue manager +func ConfigureTLS(devmode bool) error { + if !keystoresConfigured { + err := ConfigureTLSKeystores() + if err != nil { + return err + } + } + log.Debug("Configuring TLS") + keyFile := filepath.Join(keystoreDir, CMSKeyStoreName) + + const mqsc string = "/etc/mqm/15-tls.mqsc" + const mqscTemplate string = mqsc + ".tpl" + + err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{ + "SSLKeyR": strings.TrimSuffix(keyFile, ".kdb"), + "CertificateLabel": qmKeyLabel, + }, log) + if err != nil { + return err + } + + if devmode { + err = ConfigureTLSDev() + if err != nil { + return err + } + } + + return nil +} + +// ConfigureSSOTLS configures TLS for the Cloud Integration Platform Single Sign-On +func ConfigureSSOTLS() error { + if !keystoresConfigured { + err := ConfigureTLSKeystores() + if err != nil { + return err + } + } + + // TODO find way to supply this + // Override the webstore variables to hard coded defaults + webkeyStoreName = CIPDefaultLabel + ".p12" + + // Check keystore exists + ks := filepath.Join(keystoreDir, webkeyStoreName) + _, err := os.Stat(ks) + if err != nil { + return fmt.Errorf("Failed to find existing keystore %s: %v", ks, err) + } + + // Check truststore exists + ts := filepath.Join(keystoreDir, P12TrustStoreName) + _, err = os.Stat(ts) + if err != nil { + return fmt.Errorf("Failed to find existing truststore %s: %v", ts, err) + } + + // Add OIDC cert to the truststore + err = p12TrustStore.Add(os.Getenv("MQ_OIDC_CERTIFICATE"), "OIDC") + if err != nil { + return err + } + + return nil +} + +// This function supports the old mechanism of importing certificates supplied for +// Cloud Integration platform +func handleCIPGeneratedCerts() error { + dir := "/mnt/tls" + outputdir := filepath.Join(KeyDir, CIPDefaultLabel) + keyfile := "tls.key" + crtfile := "tls.crt" + + // check that the files exist, if not just quietly leave as there's nothing to import + _, err := os.Stat(filepath.Join(dir, keyfile)) + if err != nil { + return nil + } + + _, err = os.Stat(filepath.Join(dir, crtfile)) + if err != nil { + return nil + } + + // Check the destination directory DOES not exist ahead of time + _, err = os.Stat(outputdir) + if err == nil { + return fmt.Errorf("Found CIP certificates to import but a TLS secret called %s is already present", CIPDefaultLabel) + } else if !os.IsNotExist(err) { + return fmt.Errorf("Failed to check that %s did not exist: %v", outputdir, err) + } + + // We have certificate to import and no duplicates + err = os.MkdirAll(outputdir, 0775) + if err != nil { + return fmt.Errorf("Could not create %s: %v", outputdir, err) + } + + err = CopyFileMode(filepath.Join(dir, keyfile), filepath.Join(outputdir, keyfile), 0644) + if err != nil { + return fmt.Errorf("Could not copy %s: %v", keyfile, err) + } + + err = CopyFileMode(filepath.Join(dir, crtfile), filepath.Join(outputdir, crtfile), 0644) + if err != nil { + return fmt.Errorf("Could not copy %s: %v", keyfile, err) + } + + // With certificates copied into place the rest of the TLS handling code will import them into the correct place + return nil +} + +// This function supports the old mechanism of importing certificates supplied by the MQ_TLS_KEYSTORE envvar +func expandOldTLSVarible() error { + // TODO: Change this or find a way to set it + outputDirName := "acopiedcertificate" + + // Check whether the old variable is set. If not exit quietly + keyfile := os.Getenv("MQ_TLS_KEYSTORE") + if keyfile == "" { + return nil + } + + // There is a file to read and process + keyfilepw := os.Getenv("MQ_TLS_PASSPHRASE") + + if !strings.HasSuffix(keyfile, ".p12") { + return fmt.Errorf("MQ_TLS_KEYSTORE (%s) does not point to a PKCS#12 file ending with the suffix .p12", keyfile) + } + + _, err := os.Stat(keyfile) + if err != nil { + return fmt.Errorf("File %s referenced by MQ_TLS_KEYSTORE does not exist", keyfile) + } + + readkey, err := ioutil.ReadFile(keyfile) + if err != nil { + return fmt.Errorf("Failed to read %s: %v", keyfile, err) + } + + // File has been checked and read, decode it. + pk, cert, cas, err := pkcs.DecodeChain(readkey, keyfilepw) + if err != nil { + return fmt.Errorf("Failed to decode %s: %v", keyfile, err) + } + + // Find a directory name that doesn't exist + for { + _, err := os.Stat(filepath.Join(KeyDir, outputDirName)) + if err == nil { + outputDirName = outputDirName + "0" + } else { + break + } + } + + //Bceause they supplied this certificate using the old method we should use this for qm & webconsole + webkeyStoreName = outputDirName + ".p12" + qmKeyLabel = outputDirName + + // Write out the certificate for the private key + if cert != nil { + block := pem.Block{ + Type: "CERTIFICATE", + Headers: nil, + Bytes: cert.Raw, + } + err = addCertToCMSKeystoreNoDups(&block, false) + if err != nil { + return fmt.Errorf("expandOldTLSVarible: Failed to add cert to CMS Keystore duplicate list: %v", err) + } + err = addCertToP12TrustStoreNoDups(&block) + if err != nil { + return fmt.Errorf("expandOldTLSVarible: Failed to add cert to P12 Truststore duplicate list: %v", err) + } + } + + // now write out all the ca certificates + if cas != nil || len(cas) > 0 { + for i, c := range cas { + block := pem.Block{ + Type: "CERTIFICATE", + Headers: nil, + Bytes: c.Raw, + } + + // Add to the dup list for the CMS keystore + err = addCertToCMSKeystoreNoDups(&block, false) + if err != nil { + return fmt.Errorf("expandOldTLSVarible: Failed to add CA cert %d to CMS Keystore duplicate list: %v", i, err) + } + + // Add to the p12 truststore + err = addCertToP12TrustStoreNoDups(&block) + if err != nil { + return fmt.Errorf("expandOldTLSVarible: Failed to add CA cert %d to P12 Truststore duplicate list: %v", i, err) + } + } + } + + // Now we've handled the certificates copy the keystore into place + destination := filepath.Join(keystoreDir, outputDirName+".p12") + + // Create p12 keystore + file, err := pkcs.Encode(rand.Reader, pk, cert, cas, keyStorePasswords) + if err != nil { + return fmt.Errorf("Failed to re-encode p12 keystore: %v", err) + } + + err = ioutil.WriteFile(destination, file, 0644) + if err != nil { + return fmt.Errorf("Failed to write p12 keystore: %v", err) + } + + // Add to the CMS keystore + err = cmsKeyStore.Import(destination, keyStorePasswords) + if err != nil { + return fmt.Errorf("Failed to import p12 keystore %s: %v", destination, err) + } + + if pk != nil { + // Relabel the key + allLabels, err := cmsKeyStore.GetCertificateLabels() + if err != nil { + fmt.Printf("cms GetCertificateLabels: %v\n", err) + return err + } + relabelled := false + for _, cl := range allLabels { + found := false + for _, kl := range cmsKeyLabels { + if strings.Trim(cl, "\"") == kl { + found = true + break + } + } + if !found { + // This is the one to rename + err = cmsKeyStore.RenameCertificate(strings.Trim(cl, "\""), outputDirName) + if err != nil { + return err + } + relabelled = true + cmsKeyLabels = append(cmsKeyLabels, outputDirName) + break + } + } + + if !relabelled { + return fmt.Errorf("Unable to find the added key in CMS keystore") + } + } + + return nil +} diff --git a/cmd/runmqserver/webserver.go b/cmd/runmqserver/webserver.go index ae6f24a..d1e1aa3 100644 --- a/cmd/runmqserver/webserver.go +++ b/cmd/runmqserver/webserver.go @@ -27,7 +27,6 @@ import ( "syscall" "github.com/ibm-messaging/mq-container/internal/command" - "github.com/ibm-messaging/mq-container/internal/keystore" "github.com/ibm-messaging/mq-container/internal/mqtemplate" ) @@ -44,7 +43,16 @@ func startWebServer() error { if !set { // Take all current environment variables, and add the app password cmd.Env = append(os.Environ(), "MQ_APP_PASSWORD=passw0rd") + } else { + cmd.Env = os.Environ() } + + // TLS enabled + if webkeyStoreName != "" { + cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+webkeyStoreName) + cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+keyStorePasswords) + } + uid, gid, err := command.LookupMQM() if err != nil { return err @@ -70,17 +78,18 @@ func startWebServer() error { log.Println("Started web server") return nil } - -// CopyFile copies the specified file -func CopyFile(src, dest string) error { +func CopyFileMode(src, dest string, perm os.FileMode) error { log.Debugf("Copying file %v to %v", src, dest) in, err := os.Open(src) if err != nil { - return err + return fmt.Errorf("failed to open %s for copy: %v", src, err) } defer in.Close() - out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0770) + out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, perm) + if err != nil { + return fmt.Errorf("failed to open %s for copy: %v", dest, err) + } defer out.Close() _, err = io.Copy(out, in) @@ -91,8 +100,12 @@ func CopyFile(src, dest string) error { return err } -func configureSSO() error { +// CopyFile copies the specified file +func CopyFile(src, dest string) error { + return CopyFileMode(src, dest, 0770) +} +func configureSSO() error { // Ensure all required environment variables are set for SSO requiredEnvVars := []string{ "MQ_WEB_ADMIN_USERS", @@ -129,63 +142,7 @@ func configureSSO() error { } // Configure SSO TLS - return configureSSO_TLS() -} - -func configureSSO_TLS() error { - - // Create tls directory - dir := "/run/tls" - mntdir := "/mnt/tls/" - _, err := os.Stat(dir) - if err != nil { - if os.IsNotExist(err) { - err = os.MkdirAll(dir, 0770) - if err != nil { - return err - } - mqmUID, mqmGID, err := command.LookupMQM() - if err != nil { - log.Error(err) - return err - } - err = os.Chown(dir, mqmUID, mqmGID) - if err != nil { - log.Error(err) - return err - } - } else { - return err - } - } - - // Setup key store & trust store - ks := keystore.NewJKSKeyStore(filepath.Join(dir, "key.jks"), "password") - ts := keystore.NewJKSKeyStore(filepath.Join(dir, "trust.jks"), "password") - - log.Debug("Creating key store") - err = ks.Create(log) - if err != nil { - return err - } - log.Debug("Creating trust store") - err = ts.Create(log) - if err != nil { - return err - } - log.Debug("Generating PKCS12 file") - err = ks.GeneratePKCS12(filepath.Join(mntdir, "tls.key"), filepath.Join(mntdir, "tls.crt"), filepath.Join(dir, "tls.p12"), "default", "password") - if err != nil { - return err - } - log.Debug("Importing certificate into key store") - err = ks.Import(filepath.Join(dir, "tls.p12"), "password") - if err != nil { - return err - } - log.Debug("Adding OIDC certificate to trust store") - err = ts.Add(os.Getenv("MQ_OIDC_CERTIFICATE"), "OIDC") - return err + return ConfigureSSOTLS() } func configureWebServer() error { diff --git a/etc/mqm/15-tls.mqsc.tpl b/etc/mqm/15-tls.mqsc.tpl new file mode 100644 index 0000000..12915e4 --- /dev/null +++ b/etc/mqm/15-tls.mqsc.tpl @@ -0,0 +1,19 @@ +* © Copyright IBM Corporation 2019 +* +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + +* Set the keystore location for the queue manager +ALTER QMGR SSLKEYR('{{ .SSLKeyR }}') +ALTER QMGR CERTLABL('{{ .CertificateLabel }}') + diff --git a/glide.lock b/glide.lock index 2e5ead6..716e277 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: b02555ebf3957ece0ae5ecf132fa4e415a4f66a7f4c27a82d484f4fb78f56e41 -updated: 2018-07-13T08:50:32.923040349+01:00 +hash: 6ebd5fb1c39729378c7256da6f312e9699bff1ddff9941d3c8c1ba785e22acfd +updated: 2019-05-21T10:38:01.227081+01:00 imports: - name: github.com/beorn7/perks version: 3a771d992973f24aa725d07868b467d1ddfceafb @@ -50,4 +50,8 @@ imports: version: 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 subpackages: - unix +- name: software.sslmate.com/src/go-pkcs12 + version: 6e380ad96778cc63c6ea17649a9b74224bceafe9 + subpackages: + - internal/rc2 testImports: [] diff --git a/glide.yaml b/glide.yaml index aa710d3..179a79c 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2017 +# © Copyright IBM Corporation 2017, 2019 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,4 +25,6 @@ import: - package: github.com/ibm-messaging/mq-golang version: 2.0.0 - package: github.com/genuinetools/amicontained - version: 0.4.0 \ No newline at end of file + version: 0.4.0 +- package: software.sslmate.com/src/go-pkcs12 + commit: 6e380ad96778cc63c6ea17649a9b74224bceafe9 \ No newline at end of file diff --git a/incubating/mqadvanced-server-dev/20-dev-tls.mqsc.tpl b/incubating/mqadvanced-server-dev/20-dev-tls.mqsc.tpl index 34b6b3d..b2539b2 100644 --- a/incubating/mqadvanced-server-dev/20-dev-tls.mqsc.tpl +++ b/incubating/mqadvanced-server-dev/20-dev-tls.mqsc.tpl @@ -1,4 +1,4 @@ -* © Copyright IBM Corporation 2018 +* © Copyright IBM Corporation 2018, 2019 * * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. -* Set the keystore location for the queue manager -ALTER QMGR SSLKEYR('{{ .SSLKeyR }}') -ALTER QMGR CERTLABL('{{ .CertificateLabel }}') - * Set the cipherspec for dev channels ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH({{ .SSLCipherSpec }}) SSLCAUTH(OPTIONAL) ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH({{ .SSLCipherSpec }}) SSLCAUTH(OPTIONAL) diff --git a/incubating/mqadvanced-server-dev/web/installations/Installation1/servers/mqweb/tls-dev.xml b/incubating/mqadvanced-server-dev/web/installations/Installation1/servers/mqweb/tls-dev.xml deleted file mode 100644 index 903656c..0000000 --- a/incubating/mqadvanced-server-dev/web/installations/Installation1/servers/mqweb/tls-dev.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/internal/keystore/keystore.go b/internal/keystore/keystore.go index 296477e..8142304 100644 --- a/internal/keystore/keystore.go +++ b/internal/keystore/keystore.go @@ -56,6 +56,16 @@ func NewCMSKeyStore(filename, password string) *KeyStore { } } +// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command +func NewPKCS12KeyStore(filename, password string) *KeyStore { + return &KeyStore{ + Filename: filename, + Password: password, + keyStoreType: "p12", + command: "/opt/mqm/bin/runmqakm", + } +} + // Create a key store, if it doesn't already exist func (ks *KeyStore) Create(log *logger.Logger) error { _, err := os.Stat(ks.Filename) @@ -177,6 +187,15 @@ func (ks *KeyStore) Add(inputFile, label string) error { return nil } +// Add adds a CA certificate to the keystore +func (ks *KeyStore) AddNoLabel(inputFile string) error { + out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile) + if err != nil { + return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out) + } + return nil +} + // GetCertificateLabels returns the labels of all certificates in the key store func (ks *KeyStore) GetCertificateLabels() ([]string, error) { out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) @@ -207,3 +226,25 @@ func (ks *KeyStore) RenameCertificate(from, to string) error { } return nil } + +// ListCertificates Lists all certificates in hte keystore +func (ks *KeyStore) ListAllCertificates() ([]string, error) { + out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password) + if err != nil { + return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out) + } + scanner := bufio.NewScanner(strings.NewReader(out)) + var labels []string + for scanner.Scan() { + s := scanner.Text() + if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") { + s := strings.TrimLeft(s, "-*!") + labels = append(labels, strings.TrimSpace(s)) + } + } + err = scanner.Err() + if err != nil { + return nil, err + } + return labels, nil +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/.gitattributes b/vendor/software.sslmate.com/src/go-pkcs12/.gitattributes new file mode 100644 index 0000000..d2f212e --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/.gitattributes @@ -0,0 +1,10 @@ +# Treat all files in this repo as binary, with no git magic updating +# line endings. Windows users contributing to Go will need to use a +# modern version of git and editors capable of LF line endings. +# +# We'll prevent accidental CRLF line endings from entering the repo +# via the git-review gofmt checks. +# +# See golang.org/issue/9281 + +* -text diff --git a/vendor/software.sslmate.com/src/go-pkcs12/.gitignore b/vendor/software.sslmate.com/src/go-pkcs12/.gitignore new file mode 100644 index 0000000..8339fd6 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/.gitignore @@ -0,0 +1,2 @@ +# Add no patterns to .hgignore except for files generated by the build. +last-change diff --git a/vendor/software.sslmate.com/src/go-pkcs12/LICENSE b/vendor/software.sslmate.com/src/go-pkcs12/LICENSE new file mode 100644 index 0000000..bcecd3d --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/software.sslmate.com/src/go-pkcs12/README.md b/vendor/software.sslmate.com/src/go-pkcs12/README.md new file mode 100644 index 0000000..f10f9f1 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/README.md @@ -0,0 +1,35 @@ +# package pkcs12 + +[![GoDoc](https://godoc.org/software.sslmate.com/src/go-pkcs12?status.svg)](https://godoc.org/software.sslmate.com/src/go-pkcs12) + + import "software.sslmate.com/src/go-pkcs12" + +Package pkcs12 implements some of PKCS#12 (also known as P12 or PFX). +It is intended for decoding P12/PFX files for use with the `crypto/tls` +package, and for encoding P12/PFX files for use by legacy applications which +do not support newer formats. Since PKCS#12 uses weak encryption +primitives, it SHOULD NOT be used for new applications. + +This package is forked from `golang.org/x/crypto/pkcs12`, which is frozen. +The implementation is distilled from https://tools.ietf.org/html/rfc7292 +and referenced documents. + +This repository holds supplementary Go cryptography libraries. + +## Import Path + +Note that although the source code and issue tracker for this package are hosted +on GitHub, the import path is: + + software.sslmate.com/src/go-pkcs12 + +Please be sure to use this path when you `go get` and `import` this package. + +## Download/Install + +The easiest way to install is to run `go get -u software.sslmate.com/src/go-pkcs12`. You +can also manually git clone the repository to `$GOPATH/src/software.sslmate.com/src/go-pkcs12`. + +## Report Issues / Send Patches + +Open an issue or PR at https://github.com/SSLMate/go-pkcs12 diff --git a/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go b/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go new file mode 100644 index 0000000..233b8b6 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go @@ -0,0 +1,50 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "errors" + "unicode/utf16" +) + +// bmpString returns s encoded in UCS-2 with a zero terminator. +func bmpString(s string) ([]byte, error) { + // References: + // https://tools.ietf.org/html/rfc7292#appendix-B.1 + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes + // EncodeRune returns 0xfffd if the rune does not need special encoding + // - the above RFC provides the info that BMPStrings are NULL terminated. + + ret := make([]byte, 0, 2*len(s)+2) + + for _, r := range s { + if t, _ := utf16.EncodeRune(r); t != 0xfffd { + return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") + } + ret = append(ret, byte(r/256), byte(r%256)) + } + + return append(ret, 0, 0), nil +} + +func decodeBMPString(bmpString []byte) (string, error) { + if len(bmpString)%2 != 0 { + return "", errors.New("pkcs12: odd-length BMP string") + } + + // strip terminator if present + if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { + bmpString = bmpString[:l-2] + } + + s := make([]uint16, 0, len(bmpString)/2) + for len(bmpString) > 0 { + s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1])) + bmpString = bmpString[2:] + } + + return string(utf16.Decode(s)), nil +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/bmp-string_test.go b/vendor/software.sslmate.com/src/go-pkcs12/bmp-string_test.go new file mode 100644 index 0000000..7fca55f --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/bmp-string_test.go @@ -0,0 +1,63 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "encoding/hex" + "testing" +) + +var bmpStringTests = []struct { + in string + expectedHex string + shouldFail bool +}{ + {"", "0000", false}, + // Example from https://tools.ietf.org/html/rfc7292#appendix-B. + {"Beavis", "0042006500610076006900730000", false}, + // Some characters from the "Letterlike Symbols Unicode block". + {"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000", false}, + // any character outside the BMP should trigger an error. + {"\U0001f000 East wind (Mahjong)", "", true}, +} + +func TestBMPString(t *testing.T) { + for i, test := range bmpStringTests { + expected, err := hex.DecodeString(test.expectedHex) + if err != nil { + t.Fatalf("#%d: failed to decode expectation", i) + } + + out, err := bmpString(test.in) + if err == nil && test.shouldFail { + t.Errorf("#%d: expected to fail, but produced %x", i, out) + continue + } + + if err != nil && !test.shouldFail { + t.Errorf("#%d: failed unexpectedly: %s", i, err) + continue + } + + if !test.shouldFail { + if !bytes.Equal(out, expected) { + t.Errorf("#%d: expected %s, got %x", i, test.expectedHex, out) + continue + } + + roundTrip, err := decodeBMPString(out) + if err != nil { + t.Errorf("#%d: decoding output gave an error: %s", i, err) + continue + } + + if roundTrip != test.in { + t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, roundTrip, test.in) + continue + } + } + } +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/crypto.go b/vendor/software.sslmate.com/src/go-pkcs12/crypto.go new file mode 100644 index 0000000..16178f5 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/crypto.go @@ -0,0 +1,173 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/cipher" + "crypto/des" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + + "software.sslmate.com/src/go-pkcs12/internal/rc2" +) + +var ( + oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6}) +) + +// pbeCipher is an abstraction of a PKCS#12 cipher. +type pbeCipher interface { + // create returns a cipher.Block given a key. + create(key []byte) (cipher.Block, error) + // deriveKey returns a key derived from the given password and salt. + deriveKey(salt, password []byte, iterations int) []byte + // deriveKey returns an IV derived from the given password and salt. + deriveIV(salt, password []byte, iterations int) []byte +} + +type shaWithTripleDESCBC struct{} + +func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) { + return des.NewTripleDESCipher(key) +} + +func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) +} + +func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type shaWith40BitRC2CBC struct{} + +func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) { + return rc2.New(key, len(key)*8) +} + +func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) +} + +func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type pbeParams struct { + Salt []byte + Iterations int +} + +func pbeCipherFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.Block, []byte, error) { + var cipherType pbeCipher + + switch { + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC): + cipherType = shaWithTripleDESCBC{} + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC): + cipherType = shaWith40BitRC2CBC{} + default: + return nil, nil, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported") + } + + var params pbeParams + if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { + return nil, nil, err + } + + key := cipherType.deriveKey(params.Salt, password, params.Iterations) + iv := cipherType.deriveIV(params.Salt, password, params.Iterations) + + block, err := cipherType.create(key) + if err != nil { + return nil, nil, err + } + + return block, iv, nil +} + +func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + block, iv, err := pbeCipherFor(algorithm, password) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil +} + +func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) { + cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password) + if err != nil { + return nil, err + } + + encrypted := info.Data() + if len(encrypted) == 0 { + return nil, errors.New("pkcs12: empty encrypted data") + } + if len(encrypted)%blockSize != 0 { + return nil, errors.New("pkcs12: input is not a multiple of the block size") + } + decrypted = make([]byte, len(encrypted)) + cbc.CryptBlocks(decrypted, encrypted) + + psLen := int(decrypted[len(decrypted)-1]) + if psLen == 0 || psLen > blockSize { + return nil, ErrDecryption + } + + if len(decrypted) < psLen { + return nil, ErrDecryption + } + ps := decrypted[len(decrypted)-psLen:] + decrypted = decrypted[:len(decrypted)-psLen] + if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 { + return nil, ErrDecryption + } + + return +} + +// decryptable abstracts an object that contains ciphertext. +type decryptable interface { + Algorithm() pkix.AlgorithmIdentifier + Data() []byte +} + +func pbEncrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + block, iv, err := pbeCipherFor(algorithm, password) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCEncrypter(block, iv), block.BlockSize(), nil +} + +func pbEncrypt(info encryptable, decrypted []byte, password []byte) error { + cbc, blockSize, err := pbEncrypterFor(info.Algorithm(), password) + if err != nil { + return err + } + + psLen := blockSize - len(decrypted)%blockSize + encrypted := make([]byte, len(decrypted)+psLen) + copy(encrypted[:len(decrypted)], decrypted) + copy(encrypted[len(decrypted):], bytes.Repeat([]byte{byte(psLen)}, psLen)) + cbc.CryptBlocks(encrypted, encrypted) + + info.SetData(encrypted) + + return nil +} + +// encryptable abstracts a object that contains ciphertext. +type encryptable interface { + Algorithm() pkix.AlgorithmIdentifier + SetData([]byte) +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/crypto_test.go b/vendor/software.sslmate.com/src/go-pkcs12/crypto_test.go new file mode 100644 index 0000000..1104155 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/crypto_test.go @@ -0,0 +1,194 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/x509/pkix" + "encoding/asn1" + "testing" +) + +var sha1WithTripleDES = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + +func TestPbDecrypterFor(t *testing.T) { + params, _ := asn1.Marshal(pbeParams{ + Salt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Iterations: 2048, + }) + alg := pkix.AlgorithmIdentifier{ + Algorithm: asn1.ObjectIdentifier([]int{1, 2, 3}), + Parameters: asn1.RawValue{ + FullBytes: params, + }, + } + + pass, _ := bmpString("Sesame open") + + _, _, err := pbDecrypterFor(alg, pass) + if _, ok := err.(NotImplementedError); !ok { + t.Errorf("expected not implemented error, got: %T %s", err, err) + } + + alg.Algorithm = sha1WithTripleDES + cbc, blockSize, err := pbDecrypterFor(alg, pass) + if err != nil { + t.Errorf("unexpected error from pbDecrypterFor %v", err) + } + if blockSize != 8 { + t.Errorf("unexpected block size %d, wanted 8", blockSize) + } + + plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8} + expectedCiphertext := []byte{185, 73, 135, 249, 137, 1, 122, 247} + ciphertext := make([]byte, len(plaintext)) + cbc.CryptBlocks(ciphertext, plaintext) + + if bytes.Compare(ciphertext, expectedCiphertext) != 0 { + t.Errorf("bad ciphertext, got %x but wanted %x", ciphertext, expectedCiphertext) + } +} + +func TestPbEncrypterFor(t *testing.T) { + params, _ := asn1.Marshal(pbeParams{ + Salt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Iterations: 2048, + }) + alg := pkix.AlgorithmIdentifier{ + Algorithm: asn1.ObjectIdentifier([]int{1, 2, 3}), + Parameters: asn1.RawValue{ + FullBytes: params, + }, + } + + pass, _ := bmpString("Sesame open") + + _, _, err := pbEncrypterFor(alg, pass) + if _, ok := err.(NotImplementedError); !ok { + t.Errorf("expected not implemented error, got: %T %s", err, err) + } + + alg.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + cbc, _, err := pbEncrypterFor(alg, pass) + if err != nil { + t.Errorf("err: %v", err) + } + + expectedM := []byte{1, 2, 3, 4, 5, 6, 7, 8} + M := []byte{185, 73, 135, 249, 137, 1, 122, 247} + cbc.CryptBlocks(M, M) + + if bytes.Compare(M, expectedM) != 0 { + t.Errorf("expected M to be '%d', but found '%d", expectedM, M) + } +} + +var pbDecryptTests = []struct { + in []byte + expected []byte + expectedError error +}{ + { + []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\xa0\x9a\xdf\x5a\x58\xa0\xea\x46"), // 7 padding bytes + []byte("A secret!"), + nil, + }, + { + []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes + []byte("A secret"), + nil, + }, + { + []byte("\x35\x0c\xc0\x8d\xab\xa9\x5d\x30\x7f\x9a\xec\x6a\xd8\x9b\x9c\xd9"), // 9 padding bytes, incorrect + nil, + ErrDecryption, + }, + { + []byte("\xb2\xf9\x6e\x06\x60\xae\x20\xcf\x08\xa0\x7b\xd9\x6b\x20\xef\x41"), // incorrect padding bytes: [ ... 0x04 0x02 ] + nil, + ErrDecryption, + }, +} + +func TestPbDecrypt(t *testing.T) { + for i, test := range pbDecryptTests { + decryptable := testDecryptable{ + data: test.in, + algorithm: pkix.AlgorithmIdentifier{ + Algorithm: sha1WithTripleDES, + Parameters: pbeParams{ + Salt: []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"), + Iterations: 4096, + }.RawASN1(), + }, + } + password, _ := bmpString("sesame") + + plaintext, err := pbDecrypt(decryptable, password) + if err != test.expectedError { + t.Errorf("#%d: got error %q, but wanted %q", i, err, test.expectedError) + continue + } + + if !bytes.Equal(plaintext, test.expected) { + t.Errorf("#%d: got %x, but wanted %x", i, plaintext, test.expected) + } + } +} + +func TestPbEncrypt(t *testing.T) { + tests := [][]byte{ + []byte("A secret!"), + []byte("A secret"), + } + expected := [][]byte{ + []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\xa0\x9a\xdf\x5a\x58\xa0\xea\x46"), // 7 padding bytes + []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes + } + + for i, c := range tests { + td := testDecryptable{ + algorithm: pkix.AlgorithmIdentifier{ + Algorithm: asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}), // SHA1/3TDES + Parameters: pbeParams{ + Salt: []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"), + Iterations: 4096, + }.RawASN1(), + }, + } + p, _ := bmpString("sesame") + + err := pbEncrypt(&td, c, p) + if err != nil { + t.Errorf("error encrypting %d: %v", c, err) + } + + if bytes.Compare(td.data, expected[i]) != 0 { + t.Errorf("expected %d to be encrypted to %d, but found %d", c, expected[i], td.data) + } + } +} + +type testDecryptable struct { + data []byte + algorithm pkix.AlgorithmIdentifier +} + +func (d testDecryptable) Algorithm() pkix.AlgorithmIdentifier { return d.algorithm } +func (d testDecryptable) Data() []byte { return d.data } +func (d *testDecryptable) SetData(data []byte) { d.data = data } + +func (params pbeParams) RawASN1() (raw asn1.RawValue) { + asn1Bytes, err := asn1.Marshal(params) + if err != nil { + panic(err) + } + _, err = asn1.Unmarshal(asn1Bytes, &raw) + if err != nil { + panic(err) + } + return +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/errors.go b/vendor/software.sslmate.com/src/go-pkcs12/errors.go new file mode 100644 index 0000000..7377ce6 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/errors.go @@ -0,0 +1,23 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import "errors" + +var ( + // ErrDecryption represents a failure to decrypt the input. + ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding") + + // ErrIncorrectPassword is returned when an incorrect password is detected. + // Usually, P12/PFX data is signed to be able to verify the password. + ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect") +) + +// NotImplementedError indicates that the input is not currently supported. +type NotImplementedError string + +func (e NotImplementedError) Error() string { + return "pkcs12: " + string(e) +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/bench_test.go b/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/bench_test.go new file mode 100644 index 0000000..3347f33 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/bench_test.go @@ -0,0 +1,27 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rc2 + +import ( + "testing" +) + +func BenchmarkEncrypt(b *testing.B) { + r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) + b.ResetTimer() + var src [8]byte + for i := 0; i < b.N; i++ { + r.Encrypt(src[:], src[:]) + } +} + +func BenchmarkDecrypt(b *testing.B) { + r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) + b.ResetTimer() + var src [8]byte + for i := 0; i < b.N; i++ { + r.Decrypt(src[:], src[:]) + } +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go b/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go new file mode 100644 index 0000000..7499e3f --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2.go @@ -0,0 +1,271 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rc2 implements the RC2 cipher +/* +https://www.ietf.org/rfc/rfc2268.txt +http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf + +This code is licensed under the MIT license. +*/ +package rc2 + +import ( + "crypto/cipher" + "encoding/binary" +) + +// The rc2 block size in bytes +const BlockSize = 8 + +type rc2Cipher struct { + k [64]uint16 +} + +// New returns a new rc2 cipher with the given key and effective key length t1 +func New(key []byte, t1 int) (cipher.Block, error) { + // TODO(dgryski): error checking for key length + return &rc2Cipher{ + k: expandKey(key, t1), + }, nil +} + +func (*rc2Cipher) BlockSize() int { return BlockSize } + +var piTable = [256]byte{ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, +} + +func expandKey(key []byte, t1 int) [64]uint16 { + + l := make([]byte, 128) + copy(l, key) + + var t = len(key) + var t8 = (t1 + 7) / 8 + var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8)))) + + for i := len(key); i < 128; i++ { + l[i] = piTable[l[i-1]+l[uint8(i-t)]] + } + + l[128-t8] = piTable[l[128-t8]&tm] + + for i := 127 - t8; i >= 0; i-- { + l[i] = piTable[l[i+1]^l[i+t8]] + } + + var k [64]uint16 + + for i := range k { + k[i] = uint16(l[2*i]) + uint16(l[2*i+1])*256 + } + + return k +} + +func rotl16(x uint16, b uint) uint16 { + return (x >> (16 - b)) | (x << b) +} + +func (c *rc2Cipher) Encrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + var j int + + for j <= 16 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 40 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 60 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} + +func (c *rc2Cipher) Decrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + j := 63 + + for j >= 44 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 20 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 0 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2_test.go b/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2_test.go new file mode 100644 index 0000000..51a7efe --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/internal/rc2/rc2_test.go @@ -0,0 +1,92 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rc2 + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func TestEncryptDecrypt(t *testing.T) { + // TODO(dgryski): add the rest of the test vectors from the RFC + var tests = []struct { + key string + plain string + cipher string + t1 int + }{ + { + "0000000000000000", + "0000000000000000", + "ebb773f993278eff", + 63, + }, + { + "ffffffffffffffff", + "ffffffffffffffff", + "278b27e42e2f0d49", + 64, + }, + { + "3000000000000000", + "1000000000000001", + "30649edf9be7d2c2", + 64, + }, + { + "88", + "0000000000000000", + "61a8a244adacccf0", + 64, + }, + { + "88bca90e90875a", + "0000000000000000", + "6ccf4308974c267f", + 64, + }, + { + "88bca90e90875a7f0f79c384627bafb2", + "0000000000000000", + "1a807d272bbe5db1", + 64, + }, + { + "88bca90e90875a7f0f79c384627bafb2", + "0000000000000000", + "2269552ab0f85ca6", + 128, + }, + { + "88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e", + "0000000000000000", + "5b78d3a43dfff1f1", + 129, + }, + } + + for _, tt := range tests { + k, _ := hex.DecodeString(tt.key) + p, _ := hex.DecodeString(tt.plain) + c, _ := hex.DecodeString(tt.cipher) + + b, _ := New(k, tt.t1) + + var dst [8]byte + + b.Encrypt(dst[:], p) + + if !bytes.Equal(dst[:], c) { + t.Errorf("encrypt failed: got % 2x wanted % 2x\n", dst, c) + } + + b.Decrypt(dst[:], c) + + if !bytes.Equal(dst[:], p) { + t.Errorf("decrypt failed: got % 2x wanted % 2x\n", dst, p) + } + } +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/mac.go b/vendor/software.sslmate.com/src/go-pkcs12/mac.go new file mode 100644 index 0000000..b7b05de --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/mac.go @@ -0,0 +1,60 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/x509/pkix" + "encoding/asn1" +) + +type macData struct { + Mac digestInfo + MacSalt []byte + Iterations int `asn1:"optional,default:1"` +} + +// from PKCS#7: +type digestInfo struct { + Algorithm pkix.AlgorithmIdentifier + Digest []byte +} + +var ( + oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) +) + +func verifyMac(macData *macData, message, password []byte) error { + if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) + + mac := hmac.New(sha1.New, key) + mac.Write(message) + expectedMAC := mac.Sum(nil) + + if !hmac.Equal(macData.Mac.Digest, expectedMAC) { + return ErrIncorrectPassword + } + return nil +} + +func computeMac(macData *macData, message, password []byte) error { + if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) + + mac := hmac.New(sha1.New, key) + mac.Write(message) + macData.Mac.Digest = mac.Sum(nil) + + return nil +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/mac_test.go b/vendor/software.sslmate.com/src/go-pkcs12/mac_test.go new file mode 100644 index 0000000..3e6a80b --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/mac_test.go @@ -0,0 +1,73 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "encoding/asn1" + "testing" +) + +func TestVerifyMac(t *testing.T) { + td := macData{ + Mac: digestInfo{ + Digest: []byte{0x18, 0x20, 0x3d, 0xff, 0x1e, 0x16, 0xf4, 0x92, 0xf2, 0xaf, 0xc8, 0x91, 0xa9, 0xba, 0xd6, 0xca, 0x9d, 0xee, 0x51, 0x93}, + }, + MacSalt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Iterations: 2048, + } + + message := []byte{11, 12, 13, 14, 15} + password, _ := bmpString("") + + td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 3}) + err := verifyMac(&td, message, password) + if _, ok := err.(NotImplementedError); !ok { + t.Errorf("err: %v", err) + } + + td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) + err = verifyMac(&td, message, password) + if err != ErrIncorrectPassword { + t.Errorf("Expected incorrect password, got err: %v", err) + } + + password, _ = bmpString("Sesame open") + err = verifyMac(&td, message, password) + if err != nil { + t.Errorf("err: %v", err) + } + +} + +func TestComputeMac(t *testing.T) { + td := macData{ + MacSalt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Iterations: 2048, + } + + message := []byte{11, 12, 13, 14, 15} + password, _ := bmpString("Sesame open") + + td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 3}) + err := computeMac(&td, message, password) + if _, ok := err.(NotImplementedError); !ok { + t.Errorf("err: %v", err) + } + + td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) + err = computeMac(&td, message, password) + if err != nil { + t.Errorf("err: %v", err) + } + + expectedDigest := []byte{0x18, 0x20, 0x3d, 0xff, 0x1e, 0x16, 0xf4, 0x92, 0xf2, 0xaf, 0xc8, 0x91, 0xa9, 0xba, 0xd6, 0xca, 0x9d, 0xee, 0x51, 0x93} + + if bytes.Compare(td.Mac.Digest, expectedDigest) != 0 { + t.Errorf("Computed incorrect MAC; expected MAC to be '%d' but got '%d'", expectedDigest, td.Mac.Digest) + } + +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go b/vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go new file mode 100644 index 0000000..5c419d4 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/pbkdf.go @@ -0,0 +1,170 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "crypto/sha1" + "math/big" +) + +var ( + one = big.NewInt(1) +) + +// sha1Sum returns the SHA-1 hash of in. +func sha1Sum(in []byte) []byte { + sum := sha1.Sum(in) + return sum[:] +} + +// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of +// repeats of pattern. +func fillWithRepeats(pattern []byte, v int) []byte { + if len(pattern) == 0 { + return nil + } + outputLen := v * ((len(pattern) + v - 1) / v) + return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen] +} + +func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { + // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments + + // Let H be a hash function built around a compression function f: + + // Z_2^u x Z_2^v -> Z_2^u + + // (that is, H has a chaining variable and output of length u bits, and + // the message input to the compression function of H is v bits). The + // values for u and v are as follows: + + // HASH FUNCTION VALUE u VALUE v + // MD2, MD5 128 512 + // SHA-1 160 512 + // SHA-224 224 512 + // SHA-256 256 512 + // SHA-384 384 1024 + // SHA-512 512 1024 + // SHA-512/224 224 1024 + // SHA-512/256 256 1024 + + // Furthermore, let r be the iteration count. + + // We assume here that u and v are both multiples of 8, as are the + // lengths of the password and salt strings (which we denote by p and s, + // respectively) and the number n of pseudorandom bits required. In + // addition, u and v are of course non-zero. + + // For information on security considerations for MD5 [19], see [25] and + // [1], and on those for MD2, see [18]. + + // The following procedure can be used to produce pseudorandom bits for + // a particular "purpose" that is identified by a byte called "ID". + // This standard specifies 3 different values for the ID byte: + + // 1. If ID=1, then the pseudorandom bits being produced are to be used + // as key material for performing encryption or decryption. + + // 2. If ID=2, then the pseudorandom bits being produced are to be used + // as an IV (Initial Value) for encryption or decryption. + + // 3. If ID=3, then the pseudorandom bits being produced are to be used + // as an integrity key for MACing. + + // 1. Construct a string, D (the "diversifier"), by concatenating v/8 + // copies of ID. + var D []byte + for i := 0; i < v; i++ { + D = append(D, ID) + } + + // 2. Concatenate copies of the salt together to create a string S of + // length v(ceiling(s/v)) bits (the final copy of the salt may be + // truncated to create S). Note that if the salt is the empty + // string, then so is S. + + S := fillWithRepeats(salt, v) + + // 3. Concatenate copies of the password together to create a string P + // of length v(ceiling(p/v)) bits (the final copy of the password + // may be truncated to create P). Note that if the password is the + // empty string, then so is P. + + P := fillWithRepeats(password, v) + + // 4. Set I=S||P to be the concatenation of S and P. + I := append(S, P...) + + // 5. Set c=ceiling(n/u). + c := (size + u - 1) / u + + // 6. For i=1, 2, ..., c, do the following: + A := make([]byte, c*20) + var IjBuf []byte + for i := 0; i < c; i++ { + // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, + // H(H(H(... H(D||I)))) + Ai := hash(append(D, I...)) + for j := 1; j < r; j++ { + Ai = hash(Ai) + } + copy(A[i*20:], Ai[:]) + + if i < c-1 { // skip on last iteration + // B. Concatenate copies of Ai to create a string B of length v + // bits (the final copy of Ai may be truncated to create B). + var B []byte + for len(B) < v { + B = append(B, Ai[:]...) + } + B = B[:v] + + // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit + // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by + // setting I_j=(I_j+B+1) mod 2^v for each j. + { + Bbi := new(big.Int).SetBytes(B) + Ij := new(big.Int) + + for j := 0; j < len(I)/v; j++ { + Ij.SetBytes(I[j*v : (j+1)*v]) + Ij.Add(Ij, Bbi) + Ij.Add(Ij, one) + Ijb := Ij.Bytes() + // We expect Ijb to be exactly v bytes, + // if it is longer or shorter we must + // adjust it accordingly. + if len(Ijb) > v { + Ijb = Ijb[len(Ijb)-v:] + } + if len(Ijb) < v { + if IjBuf == nil { + IjBuf = make([]byte, v) + } + bytesShort := v - len(Ijb) + for i := 0; i < bytesShort; i++ { + IjBuf[i] = 0 + } + copy(IjBuf[bytesShort:], Ijb) + Ijb = IjBuf + } + copy(I[j*v:(j+1)*v], Ijb) + } + } + } + } + // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom + // bit string, A. + + // 8. Use the first n bits of A as the output of this entire process. + return A[:size] + + // If the above process is being used to generate a DES key, the process + // should be used to create 64 random bits, and the key's parity bits + // should be set after the 64 bits have been produced. Similar concerns + // hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any + // similar keys with parity bits "built into them". +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/pbkdf_test.go b/vendor/software.sslmate.com/src/go-pkcs12/pbkdf_test.go new file mode 100644 index 0000000..262037d --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/pbkdf_test.go @@ -0,0 +1,34 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "bytes" + "testing" +) + +func TestThatPBKDFWorksCorrectlyForLongKeys(t *testing.T) { + cipherInfo := shaWithTripleDESCBC{} + + salt := []byte("\xff\xff\xff\xff\xff\xff\xff\xff") + password, _ := bmpString("sesame") + key := cipherInfo.deriveKey(salt, password, 2048) + + if expected := []byte("\x7c\xd9\xfd\x3e\x2b\x3b\xe7\x69\x1a\x44\xe3\xbe\xf0\xf9\xea\x0f\xb9\xb8\x97\xd4\xe3\x25\xd9\xd1"); bytes.Compare(key, expected) != 0 { + t.Fatalf("expected key '%x', but found '%x'", expected, key) + } +} + +func TestThatPBKDFHandlesLeadingZeros(t *testing.T) { + // This test triggers a case where I_j (in step 6C) ends up with leading zero + // byte, meaning that len(Ijb) < v (leading zeros get stripped by big.Int). + // This was previously causing bug whereby certain inputs would break the + // derivation and produce the wrong output. + key := pbkdf(sha1Sum, 20, 64, []byte("\xf3\x7e\x05\xb5\x18\x32\x4b\x4b"), []byte("\x00\x00"), 2048, 1, 24) + expected := []byte("\x00\xf7\x59\xff\x47\xd1\x4d\xd0\x36\x65\xd5\x94\x3c\xb3\xc4\xa3\x9a\x25\x55\xc0\x2a\xed\x66\xe1") + if bytes.Compare(key, expected) != 0 { + t.Fatalf("expected key '%x', but found '%x'", expected, key) + } +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go b/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go new file mode 100644 index 0000000..9b0ddeb --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go @@ -0,0 +1,540 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pkcs12 implements some of PKCS#12 (also known as P12 or PFX). +// It is intended for decoding P12/PFX files for use with the crypto/tls +// package, and for encoding P12/PFX files for use by legacy applications which +// do not support newer formats. Since PKCS#12 uses weak encryption +// primitives, it SHOULD NOT be used for new applications. +// +// This package is forked from golang.org/x/crypto/pkcs12, which is frozen. +// The implementation is distilled from https://tools.ietf.org/html/rfc7292 +// and referenced documents. +package pkcs12 // import "software.sslmate.com/src/go-pkcs12" + +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "encoding/pem" + "errors" + "io" +) + +// DefaultPassword is the string "changeit", a commonly-used password for +// PKCS#12 files. Due to the weak encryption used by PKCS#12, it is +// RECOMMENDED that you use DefaultPassword when encoding PKCS#12 files, +// and protect the PKCS#12 files using other means. +const DefaultPassword = "changeit" + +var ( + oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1}) + oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6}) + + oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20}) + oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) + oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) +) + +type pfxPdu struct { + Version int + AuthSafe contentInfo + MacData macData `asn1:"optional"` +} + +type contentInfo struct { + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"tag:0,explicit,optional"` +} + +type encryptedData struct { + Version int + EncryptedContentInfo encryptedContentInfo +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent []byte `asn1:"tag:0,optional"` +} + +func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.ContentEncryptionAlgorithm +} + +func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent } + +func (i *encryptedContentInfo) SetData(data []byte) { i.EncryptedContent = data } + +type safeBag struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"tag:0,explicit"` + Attributes []pkcs12Attribute `asn1:"set,optional"` +} + +type pkcs12Attribute struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"set"` +} + +type encryptedPrivateKeyInfo struct { + AlgorithmIdentifier pkix.AlgorithmIdentifier + EncryptedData []byte +} + +func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.AlgorithmIdentifier +} + +func (i encryptedPrivateKeyInfo) Data() []byte { + return i.EncryptedData +} + +func (i *encryptedPrivateKeyInfo) SetData(data []byte) { + i.EncryptedData = data +} + +// PEM block types +const ( + certificateType = "CERTIFICATE" + privateKeyType = "PRIVATE KEY" +) + +// unmarshal calls asn1.Unmarshal, but also returns an error if there is any +// trailing data after unmarshaling. +func unmarshal(in []byte, out interface{}) error { + trailing, err := asn1.Unmarshal(in, out) + if err != nil { + return err + } + if len(trailing) != 0 { + return errors.New("pkcs12: trailing data found") + } + return nil +} + +// ToPEM converts all "safe bags" contained in pfxData to PEM blocks. +// DO NOT USE THIS FUNCTION. ToPEM creates invalid PEM blocks; private keys +// are encoded as raw RSA or EC private keys rather than PKCS#8 despite being +// labeled "PRIVATE KEY". To decode a PKCS#12 file, use DecodeChain instead, +// and use the encoding/pem package to convert to PEM if necessary. +func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, ErrIncorrectPassword + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + + if err != nil { + return nil, err + } + + blocks := make([]*pem.Block, 0, len(bags)) + for _, bag := range bags { + block, err := convertBag(&bag, encodedPassword) + if err != nil { + return nil, err + } + blocks = append(blocks, block) + } + + return blocks, nil +} + +func convertBag(bag *safeBag, password []byte) (*pem.Block, error) { + block := &pem.Block{ + Headers: make(map[string]string), + } + + for _, attribute := range bag.Attributes { + k, v, err := convertAttribute(&attribute) + if err != nil { + return nil, err + } + block.Headers[k] = v + } + + switch { + case bag.Id.Equal(oidCertBag): + block.Type = certificateType + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, err + } + block.Bytes = certsData + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + block.Type = privateKeyType + + key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password) + if err != nil { + return nil, err + } + + switch key := key.(type) { + case *rsa.PrivateKey: + block.Bytes = x509.MarshalPKCS1PrivateKey(key) + case *ecdsa.PrivateKey: + block.Bytes, err = x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + default: + return nil, errors.New("found unknown private key type in PKCS#8 wrapping") + } + default: + return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String()) + } + return block, nil +} + +func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) { + isString := false + + switch { + case attribute.Id.Equal(oidFriendlyName): + key = "friendlyName" + isString = true + case attribute.Id.Equal(oidLocalKeyID): + key = "localKeyId" + case attribute.Id.Equal(oidMicrosoftCSPName): + // This key is chosen to match OpenSSL. + key = "Microsoft CSP Name" + isString = true + default: + return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String()) + } + + if isString { + if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil { + return "", "", err + } + if value, err = decodeBMPString(attribute.Value.Bytes); err != nil { + return "", "", err + } + } else { + var id []byte + if err := unmarshal(attribute.Value.Bytes, &id); err != nil { + return "", "", err + } + value = hex.EncodeToString(id) + } + + return key, value, nil +} + +// Decode extracts a certificate and private key from pfxData. This function +// assumes that there is only one certificate and only one private key in the +// pfxData. Since PKCS#12 files often contain more than one certificate, you +// probably want to use DecodeChain instead. +func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) { + var caCerts []*x509.Certificate + privateKey, certificate, caCerts, err = DecodeChain(pfxData, password) + if len(caCerts) != 0 { + err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU") + } + return +} + +// DecodeChain extracts a certificate, a CA certificate chain, and private key +// from pfxData. This function assumes that there is at least one certificate +// and only one private key in the pfxData. The first certificate is assumed to +// be the leaf certificate, and subsequent certificates, if any, are assumed to +// comprise the CA certificate chain. +func DecodeChain(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, nil, nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + if err != nil { + return nil, nil, nil, err + } + + for _, bag := range bags { + switch { + case bag.Id.Equal(oidCertBag): + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, nil, nil, err + } + certs, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, nil, nil, err + } + if len(certs) != 1 { + err = errors.New("pkcs12: expected exactly one certificate in the certBag") + return nil, nil, nil, err + } + if certificate == nil { + certificate = certs[0] + } else { + caCerts = append(caCerts, certs[0]) + } + + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + if privateKey != nil { + err = errors.New("pkcs12: expected exactly one key bag") + return nil, nil, nil, err + } + + if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil { + return nil, nil, nil, err + } + } + } + + if certificate == nil { + return nil, nil, nil, errors.New("pkcs12: certificate missing") + } + if privateKey == nil { + return nil, nil, nil, errors.New("pkcs12: private key missing") + } + + return +} + +func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) { + pfx := new(pfxPdu) + if err := unmarshal(p12Data, pfx); err != nil { + return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error()) + } + + if pfx.Version != 3 { + return nil, nil, NotImplementedError("can only decode v3 PFX PDU's") + } + + if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) { + return nil, nil, NotImplementedError("only password-protected PFX is implemented") + } + + // unmarshal the explicit bytes in the content for type 'data' + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil { + return nil, nil, err + } + + if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 { + return nil, nil, errors.New("pkcs12: no MAC in data") + } + + if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { + if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 { + // some implementations use an empty byte array + // for the empty string password try one more + // time with empty-empty password + password = nil + err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password) + } + if err != nil { + return nil, nil, err + } + } + + var authenticatedSafe []contentInfo + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil { + return nil, nil, err + } + + if len(authenticatedSafe) != 2 { + return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe") + } + + for _, ci := range authenticatedSafe { + var data []byte + + switch { + case ci.ContentType.Equal(oidDataContentType): + if err := unmarshal(ci.Content.Bytes, &data); err != nil { + return nil, nil, err + } + case ci.ContentType.Equal(oidEncryptedDataContentType): + var encryptedData encryptedData + if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil { + return nil, nil, err + } + if encryptedData.Version != 0 { + return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported") + } + if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil { + return nil, nil, err + } + default: + return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe") + } + + var safeContents []safeBag + if err := unmarshal(data, &safeContents); err != nil { + return nil, nil, err + } + bags = append(bags, safeContents...) + } + + return bags, password, nil +} + +// Encode produces pfxData containing one private key (privateKey), an +// end-entity certificate (certificate), and any number of CA certificates +// (caCerts). +// +// The private key is encrypted with the provided password, but due to the +// weak encryption primitives used by PKCS#12, it is RECOMMENDED that you +// specify a hard-coded password (such as pkcs12.DefaultPassword) and protect +// the resulting pfxData using other means. +// +// The rand argument is used to provide entropy for the encryption, and +// can be set to rand.Reader from the crypto/rand package. +// +// Encode emulates the behavior of OpenSSL's PKCS12_create: it creates two +// SafeContents: one that's encrypted with RC2 and contains the certificates, +// and another that is unencrypted and contains the private key shrouded with +// 3DES The private key bag and the end-entity certificate bag have the +// LocalKeyId attribute set to the SHA-1 fingerprint of the end-entity +// certificate. +func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, err + } + + var pfx pfxPdu + pfx.Version = 3 + + var certFingerprint = sha1.Sum(certificate.Raw) + var localKeyIdAttr pkcs12Attribute + localKeyIdAttr.Id = oidLocalKeyID + localKeyIdAttr.Value.Class = 0 + localKeyIdAttr.Value.Tag = 17 + localKeyIdAttr.Value.IsCompound = true + if localKeyIdAttr.Value.Bytes, err = asn1.Marshal(certFingerprint[:]); err != nil { + return nil, err + } + + var certBags []safeBag + var certBag *safeBag + if certBag, err = makeCertBag(certificate.Raw, []pkcs12Attribute{localKeyIdAttr}); err != nil { + return nil, err + } + certBags = append(certBags, *certBag) + + for _, cert := range caCerts { + if certBag, err = makeCertBag(cert.Raw, []pkcs12Attribute{}); err != nil { + return nil, err + } + certBags = append(certBags, *certBag) + } + + var keyBag safeBag + keyBag.Id = oidPKCS8ShroundedKeyBag + keyBag.Value.Class = 2 + keyBag.Value.Tag = 0 + keyBag.Value.IsCompound = true + if keyBag.Value.Bytes, err = encodePkcs8ShroudedKeyBag(rand, privateKey, encodedPassword); err != nil { + return nil, err + } + keyBag.Attributes = append(keyBag.Attributes, localKeyIdAttr) + + // Construct an authenticated safe with two SafeContents. + // The first SafeContents is encrypted and contains the cert bags. + // The second SafeContents is unencrypted and contains the shrouded key bag. + var authenticatedSafe [2]contentInfo + if authenticatedSafe[0], err = makeSafeContents(rand, certBags, encodedPassword); err != nil { + return nil, err + } + if authenticatedSafe[1], err = makeSafeContents(rand, []safeBag{keyBag}, nil); err != nil { + return nil, err + } + + var authenticatedSafeBytes []byte + if authenticatedSafeBytes, err = asn1.Marshal(authenticatedSafe[:]); err != nil { + return nil, err + } + + // compute the MAC + pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1 + pfx.MacData.MacSalt = make([]byte, 8) + if _, err = rand.Read(pfx.MacData.MacSalt); err != nil { + return nil, err + } + pfx.MacData.Iterations = 1 + if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil { + return nil, err + } + + pfx.AuthSafe.ContentType = oidDataContentType + pfx.AuthSafe.Content.Class = 2 + pfx.AuthSafe.Content.Tag = 0 + pfx.AuthSafe.Content.IsCompound = true + if pfx.AuthSafe.Content.Bytes, err = asn1.Marshal(authenticatedSafeBytes); err != nil { + return nil, err + } + + if pfxData, err = asn1.Marshal(pfx); err != nil { + return nil, errors.New("pkcs12: error writing P12 data: " + err.Error()) + } + return +} + +func makeCertBag(certBytes []byte, attributes []pkcs12Attribute) (certBag *safeBag, err error) { + certBag = new(safeBag) + certBag.Id = oidCertBag + certBag.Value.Class = 2 + certBag.Value.Tag = 0 + certBag.Value.IsCompound = true + if certBag.Value.Bytes, err = encodeCertBag(certBytes); err != nil { + return nil, err + } + certBag.Attributes = attributes + return +} + +func makeSafeContents(rand io.Reader, bags []safeBag, password []byte) (ci contentInfo, err error) { + var data []byte + if data, err = asn1.Marshal(bags); err != nil { + return + } + + if password == nil { + ci.ContentType = oidDataContentType + ci.Content.Class = 2 + ci.Content.Tag = 0 + ci.Content.IsCompound = true + if ci.Content.Bytes, err = asn1.Marshal(data); err != nil { + return + } + } else { + randomSalt := make([]byte, 8) + if _, err = rand.Read(randomSalt); err != nil { + return + } + + var algo pkix.AlgorithmIdentifier + algo.Algorithm = oidPBEWithSHAAnd40BitRC2CBC + if algo.Parameters.FullBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil { + return + } + + var encryptedData encryptedData + encryptedData.Version = 0 + encryptedData.EncryptedContentInfo.ContentType = oidDataContentType + encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm = algo + if err = pbEncrypt(&encryptedData.EncryptedContentInfo, data, password); err != nil { + return + } + + ci.ContentType = oidEncryptedDataContentType + ci.Content.Class = 2 + ci.Content.Tag = 0 + ci.Content.IsCompound = true + if ci.Content.Bytes, err = asn1.Marshal(encryptedData); err != nil { + return + } + } + return +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/pkcs12_test.go b/vendor/software.sslmate.com/src/go-pkcs12/pkcs12_test.go new file mode 100644 index 0000000..14dd2a6 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/pkcs12_test.go @@ -0,0 +1,138 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/rsa" + "crypto/tls" + "encoding/base64" + "encoding/pem" + "testing" +) + +func TestPfx(t *testing.T) { + for commonName, base64P12 := range testdata { + p12, _ := base64.StdEncoding.DecodeString(base64P12) + + priv, cert, err := Decode(p12, "") + if err != nil { + t.Fatal(err) + } + + if err := priv.(*rsa.PrivateKey).Validate(); err != nil { + t.Errorf("error while validating private key: %v", err) + } + + if cert.Subject.CommonName != commonName { + t.Errorf("expected common name to be %q, but found %q", commonName, cert.Subject.CommonName) + } + } +} + +func TestPEM(t *testing.T) { + for commonName, base64P12 := range testdata { + p12, _ := base64.StdEncoding.DecodeString(base64P12) + + blocks, err := ToPEM(p12, "") + if err != nil { + t.Fatalf("error while converting to PEM: %s", err) + } + + var pemData []byte + for _, b := range blocks { + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + + cert, err := tls.X509KeyPair(pemData, pemData) + if err != nil { + t.Errorf("err while converting to key pair: %v", err) + } + config := tls.Config{ + Certificates: []tls.Certificate{cert}, + } + config.BuildNameToCertificate() + + if _, exists := config.NameToCertificate[commonName]; !exists { + t.Errorf("did not find our cert in PEM?: %v", config.NameToCertificate) + } + } +} + +func ExampleToPEM() { + p12, _ := base64.StdEncoding.DecodeString(`MIIJzgIBAzCCCZQGCS ... CA+gwggPk==`) + + blocks, err := ToPEM(p12, "password") + if err != nil { + panic(err) + } + + var pemData []byte + for _, b := range blocks { + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + + // then use PEM data for tls to construct tls certificate: + cert, err := tls.X509KeyPair(pemData, pemData) + if err != nil { + panic(err) + } + + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + _ = config +} + +var testdata = map[string]string{ + // 'null' password test case + "Windows Azure Tools": `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`, + // empty string password test case + "testing@example.com": `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 +HQ8CAggA`, +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/safebags.go b/vendor/software.sslmate.com/src/go-pkcs12/safebags.go new file mode 100644 index 0000000..be83a49 --- /dev/null +++ b/vendor/software.sslmate.com/src/go-pkcs12/safebags.go @@ -0,0 +1,99 @@ +// Copyright 2015, 2018, 2019 Opsmate, Inc. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkcs12 + +import ( + "crypto/x509" + "encoding/asn1" + "errors" + "io" +) + +var ( + // see https://tools.ietf.org/html/rfc7292#appendix-D + oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1}) + oidPKCS8ShroundedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2}) + oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3}) +) + +type certBag struct { + Id asn1.ObjectIdentifier + Data []byte `asn1:"tag:0,explicit"` +} + +func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) { + pkinfo := new(encryptedPrivateKeyInfo) + if err = unmarshal(asn1Data, pkinfo); err != nil { + return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error()) + } + + pkData, err := pbDecrypt(pkinfo, password) + if err != nil { + return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + ret := new(asn1.RawValue) + if err = unmarshal(pkData, ret); err != nil { + return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error()) + } + + if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { + return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error()) + } + + return privateKey, nil +} + +func encodePkcs8ShroudedKeyBag(rand io.Reader, privateKey interface{}, password []byte) (asn1Data []byte, err error) { + var pkData []byte + if pkData, err = x509.MarshalPKCS8PrivateKey(privateKey); err != nil { + return nil, errors.New("pkcs12: error encoding PKCS#8 private key: " + err.Error()) + } + + randomSalt := make([]byte, 8) + if _, err = rand.Read(randomSalt); err != nil { + return nil, errors.New("pkcs12: error reading random salt: " + err.Error()) + } + var paramBytes []byte + if paramBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil { + return nil, errors.New("pkcs12: error encoding params: " + err.Error()) + } + + var pkinfo encryptedPrivateKeyInfo + pkinfo.AlgorithmIdentifier.Algorithm = oidPBEWithSHAAnd3KeyTripleDESCBC + pkinfo.AlgorithmIdentifier.Parameters.FullBytes = paramBytes + + if err = pbEncrypt(&pkinfo, pkData, password); err != nil { + return nil, errors.New("pkcs12: error encrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + if asn1Data, err = asn1.Marshal(pkinfo); err != nil { + return nil, errors.New("pkcs12: error encoding PKCS#8 shrouded key bag: " + err.Error()) + } + + return asn1Data, nil +} + +func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) { + bag := new(certBag) + if err := unmarshal(asn1Data, bag); err != nil { + return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error()) + } + if !bag.Id.Equal(oidCertTypeX509Certificate) { + return nil, NotImplementedError("only X509 certificates are supported") + } + return bag.Data, nil +} + +func encodeCertBag(x509Certificates []byte) (asn1Data []byte, err error) { + var bag certBag + bag.Id = oidCertTypeX509Certificate + bag.Data = x509Certificates + if asn1Data, err = asn1.Marshal(bag); err != nil { + return nil, errors.New("pkcs12: error encoding cert bag: " + err.Error()) + } + return asn1Data, nil +} diff --git a/web/installations/Installation1/servers/mqweb/mqwebuser.xml.tpl b/web/installations/Installation1/servers/mqweb/mqwebuser.xml.tpl index 5cebb71..d03a46c 100644 --- a/web/installations/Installation1/servers/mqweb/mqwebuser.xml.tpl +++ b/web/installations/Installation1/servers/mqweb/mqwebuser.xml.tpl @@ -39,9 +39,6 @@ - - - - + diff --git a/web/installations/Installation1/servers/mqweb/tls.xml.tpl b/web/installations/Installation1/servers/mqweb/tls.xml.tpl new file mode 100644 index 0000000..dd9b7cb --- /dev/null +++ b/web/installations/Installation1/servers/mqweb/tls.xml.tpl @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file