Beispiel #1
0
// OverrideHosts fills template's IPAddresses, EmailAddresses, and DNSNames with the
// content of hosts, if it is not nil.
func OverrideHosts(template *x509.Certificate, hosts []string) {
	if hosts != nil {
		template.IPAddresses = []net.IP{}
		template.EmailAddresses = []string{}
		template.DNSNames = []string{}
	}

	for i := range hosts {
		if ip := net.ParseIP(hosts[i]); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else if email, err := mail.ParseAddress(hosts[i]); err == nil && email != nil {
			template.EmailAddresses = append(template.EmailAddresses, email.Address)
		} else {
			template.DNSNames = append(template.DNSNames, hosts[i])
		}
	}

}
Beispiel #2
0
func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) {
	var distPoints = template.CRLDistributionPoints
	err = signer.FillTemplate(template, s.policy.Default, profile)
	if distPoints != nil && len(distPoints) > 0 {
		template.CRLDistributionPoints = distPoints
	}
	if err != nil {
		return
	}

	var initRoot bool
	if s.ca == nil {
		if !template.IsCA {
			err = cferr.New(cferr.PolicyError, cferr.InvalidRequest)
			return
		}
		template.DNSNames = nil
		template.EmailAddresses = nil
		s.ca = template
		initRoot = true
	} else if template.IsCA {
		template.DNSNames = nil
		template.EmailAddresses = nil
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, template, s.ca, template.PublicKey, s.priv)
	if err != nil {
		return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err)
	}
	if initRoot {
		s.ca, err = x509.ParseCertificate(derBytes)
		if err != nil {
			return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
		}
	}

	cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	log.Infof("signed certificate with serial number %d", template.SerialNumber)
	return
}
Beispiel #3
0
func GenerateFakeX509Certificate(certType string) (string, string) {
	priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	if err != nil {
		logging.GetLogger().Fatal("ECDSA GenerateKey failed : " + err.Error())
	}
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		logging.GetLogger().Fatal("Serial number generation error : " + err.Error())
	}
	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Skydive Co"},
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(1 * time.Hour),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		BasicConstraintsValid: true,
	}
	if certType == "server" {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	} else {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
	}

	hosts := strings.Split("127.0.0.1,::1", ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}
	template.DNSNames = append(template.DNSNames, "localhost")
	template.EmailAddresses = append(template.EmailAddresses, "*****@*****.**")
	/* Generate CA */
	template.IsCA = true
	template.KeyUsage |= x509.KeyUsageCertSign

	/* Certificate */
	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
	if err != nil {
		logging.GetLogger().Fatalf("Failed to create certificate: %s", err)
	}

	basedir, err := ioutil.TempDir("", "skydive-keys")
	if err != nil {
		logging.GetLogger().Fatal("can't create tempdir skydive-keys")
	}
	certFilename := filepath.Join(basedir, "cert.pem")
	certOut, err := os.Create(certFilename)
	if err != nil {
		logging.GetLogger().Fatalf("failed to open %s for writing: %s", certFilename, err)
	}
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	certOut.Close()

	/* Private Key */
	privKeyFilename := filepath.Join(basedir, "key.pem")
	keyOut, err := os.OpenFile(privKeyFilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		logging.GetLogger().Fatalf("failed to open %s for writing: %s", privKeyFilename, err)
	}
	pem.Encode(keyOut, pemBlockForKey(priv))
	keyOut.Close()

	return certFilename, privKeyFilename
}
Beispiel #4
0
// Sign signs a new certificate based on the PEM-encoded client
// certificate or certificate request with the signing profile,
// specified by profileName.
func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
	profile, err := signer.Profile(s, req.Profile)
	if err != nil {
		return
	}

	block, _ := pem.Decode([]byte(req.Request))
	if block == nil {
		return nil, cferr.New(cferr.CSRError, cferr.DecodeFailed)
	}

	if block.Type != "CERTIFICATE REQUEST" {
		return nil, cferr.Wrap(cferr.CSRError,
			cferr.BadRequest, errors.New("not a certificate or csr"))
	}

	csrTemplate, err := signer.ParseCertificateRequest(s, block.Bytes)
	if err != nil {
		return nil, err
	}

	// Copy out only the fields from the CSR authorized by policy.
	safeTemplate := x509.Certificate{}
	// If the profile contains no explicit whitelist, assume that all fields
	// should be copied from the CSR.
	if profile.CSRWhitelist == nil {
		safeTemplate = *csrTemplate
	} else {
		if profile.CSRWhitelist.Subject {
			safeTemplate.Subject = csrTemplate.Subject
		}
		if profile.CSRWhitelist.PublicKeyAlgorithm {
			safeTemplate.PublicKeyAlgorithm = csrTemplate.PublicKeyAlgorithm
		}
		if profile.CSRWhitelist.PublicKey {
			safeTemplate.PublicKey = csrTemplate.PublicKey
		}
		if profile.CSRWhitelist.SignatureAlgorithm {
			safeTemplate.SignatureAlgorithm = csrTemplate.SignatureAlgorithm
		}
		if profile.CSRWhitelist.DNSNames {
			safeTemplate.DNSNames = csrTemplate.DNSNames
		}
		if profile.CSRWhitelist.IPAddresses {
			safeTemplate.IPAddresses = csrTemplate.IPAddresses
		}
		if profile.CSRWhitelist.EmailAddresses {
			safeTemplate.EmailAddresses = csrTemplate.EmailAddresses
		}
	}

	OverrideHosts(&safeTemplate, req.Hosts)
	safeTemplate.Subject = PopulateSubjectFromCSR(req.Subject, safeTemplate.Subject)

	// If there is a whitelist, ensure that both the Common Name and SAN DNSNames match
	if profile.NameWhitelist != nil {
		if safeTemplate.Subject.CommonName != "" {
			if profile.NameWhitelist.Find([]byte(safeTemplate.Subject.CommonName)) == nil {
				return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
			}
		}
		for _, name := range safeTemplate.DNSNames {
			if profile.NameWhitelist.Find([]byte(name)) == nil {
				return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
			}
		}
		for _, name := range safeTemplate.EmailAddresses {
			if profile.NameWhitelist.Find([]byte(name)) == nil {
				return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
			}
		}
	}

	if profile.ClientProvidesSerialNumbers {
		if req.Serial == nil {
			return nil, cferr.New(cferr.CertificateError, cferr.MissingSerial)
		}
		safeTemplate.SerialNumber = req.Serial
	} else {
		// RFC 5280 4.1.2.2:
		// Certificate users MUST be able to handle serialNumber
		// values up to 20 octets.  Conforming CAs MUST NOT use
		// serialNumber values longer than 20 octets.
		//
		// If CFSSL is providing the serial numbers, it makes
		// sense to use the max supported size.
		serialNumber := make([]byte, 20)
		_, err = io.ReadFull(rand.Reader, serialNumber)
		if err != nil {
			return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err)
		}

		// SetBytes interprets buf as the bytes of a big-endian
		// unsigned integer. The leading byte should be masked
		// off to ensure it isn't negative.
		serialNumber[0] &= 0x7F

		safeTemplate.SerialNumber = new(big.Int).SetBytes(serialNumber)
	}

	if len(req.Extensions) > 0 {
		for _, ext := range req.Extensions {
			oid := asn1.ObjectIdentifier(ext.ID)
			if !profile.ExtensionWhitelist[oid.String()] {
				return nil, cferr.New(cferr.CertificateError, cferr.InvalidRequest)
			}

			rawValue, err := hex.DecodeString(ext.Value)
			if err != nil {
				return nil, cferr.Wrap(cferr.CertificateError, cferr.InvalidRequest, err)
			}

			safeTemplate.ExtraExtensions = append(safeTemplate.ExtraExtensions, pkix.Extension{
				Id:       oid,
				Critical: ext.Critical,
				Value:    rawValue,
			})
		}
	}

	var certTBS = safeTemplate

	if len(profile.CTLogServers) > 0 {
		// Add a poison extension which prevents validation
		var poisonExtension = pkix.Extension{Id: signer.CTPoisonOID, Critical: true, Value: []byte{0x05, 0x00}}
		var poisonedPreCert = certTBS
		poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension)
		cert, err = s.sign(&poisonedPreCert, profile)
		if err != nil {
			return
		}

		derCert, _ := pem.Decode(cert)
		prechain := []ct.ASN1Cert{derCert.Bytes, s.ca.Raw}
		var sctList []ct.SignedCertificateTimestamp

		for _, server := range profile.CTLogServers {
			log.Infof("submitting poisoned precertificate to %s", server)
			var ctclient = client.New(server)
			var resp *ct.SignedCertificateTimestamp
			resp, err = ctclient.AddPreChain(prechain)
			if err != nil {
				return nil, cferr.Wrap(cferr.CTError, cferr.PrecertSubmissionFailed, err)
			}
			sctList = append(sctList, *resp)
		}

		var serializedSCTList []byte
		serializedSCTList, err = serializeSCTList(sctList)
		if err != nil {
			return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
		}

		// Serialize again as an octet string before embedding
		serializedSCTList, err = asn1.Marshal(serializedSCTList)
		if err != nil {
			return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
		}

		var SCTListExtension = pkix.Extension{Id: signer.SCTListOID, Critical: false, Value: serializedSCTList}
		certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension)
	}
	var signedCert []byte
	signedCert, err = s.sign(&certTBS, profile)
	if err != nil {
		return nil, err
	}

	if s.dbAccessor != nil {
		var certRecord = &certdb.CertificateRecord{
			Serial:  certTBS.SerialNumber.String(),
			CALabel: req.Label,
			Status:  "good",
			Expiry:  certTBS.NotAfter,
			PEM:     string(signedCert),
		}

		err = s.dbAccessor.InsertCertificate(certRecord)
		if err != nil {
			return nil, err
		}
		log.Debug("saved certificate with serial number ", certTBS.SerialNumber)
	}

	return signedCert, nil
}
Beispiel #5
0
func main() {
	flag.Parse()

	if *printVersion {
		fmt.Println("Quickcert v" + version)
		fmt.Println("https://github.com/andmarios/quickcert")
		os.Exit(0)
	}

	if len(*host) == 0 && !*isCA {
		fmt.Println("If you are not creating a CA pair, you need to set the -hosts parameter. Use -h for help.")
		os.Exit(1)
	}

	var cacert *x509.Certificate
	var cacertpem *pem.Block
	var cakey interface{}
	var err error

	// If not CA, read the CA key and cert
	if !*isCA {
		// Read CAcert
		log.Println("Reading CA certificate")
		data, err := readDecodePemFile(*CAcertFile)
		checkError("Could not read ca key file: ", err)
		cacert, err = x509.ParseCertificate(data.Bytes)
		checkError("Could not parse CA certificate: ", err)
		cacertpem = data

		// Read CAkey
		log.Println("Reading CA private key")
		data, err = readDecodePemFile(*CAkeyFile)
		checkError("Could not read ca key file: ", err)

		// If encrypted, decrypt it
		if x509.IsEncryptedPEMBlock(data) {
			password, err := readPassword("CA key is encrypted\nEnter password: "******"Error reading CA private key password: "******"Could not decrypt CA private key: ", err)
		}

		// Detect type and parse key
		if data.Type == "RSA PRIVATE KEY" {
			cakey, err = x509.ParsePKCS1PrivateKey(data.Bytes)
			checkError("Could not parse CA RSA private key: ", err)
		} else if data.Type == "EC PRIVATE KEY" {
			cakey, err = x509.ParseECPrivateKey(data.Bytes)
			checkError("Could not parse CA ECDSA key: ", err)
		} else {
			log.Fatalf("Could not find a compatible private key type (%s), only RSA and ECDSA are accepted", data.Type)
		}
	}

	// Create new key
	log.Println("Generating private key. This may take some time, depending on type and length.")
	var privkey interface{}
	switch *ecdsaCurve {
	case "":
		if *rsaBits < 2048 && !*isCA {
			log.Println("Consider upgrading your key to 2048 bits or better.")
		} else if *rsaBits < 4096 && *isCA {
			log.Println("Consider upgrading your CA key 4096 bits.")
		}
		privkey, err = rsa.GenerateKey(rand.Reader, *rsaBits)
		// I disabled P224 curve because Redhat patched their golang to
		// not support this curve due to patent law reasons.
		// I could leave it, but then quickcert won't compile on centos, rhel and fedora
		//	case "P224":
		//		privkey, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
	case "P256":
		privkey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	case "P384":
		privkey, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
	case "P521":
		privkey, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	default:
		log.Fatalf("Unrecognized elliptic curve: %q", *ecdsaCurve)
	}
	checkError("Failed to generate private key: ", err)

	// Create certificate
	log.Println("Generating certificate.")
	var notBefore time.Time
	if len(*validFrom) == 0 {
		notBefore = time.Now()
	} else {
		notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
		checkError("Failed to parse creation date: ", err)
	}

	// time.Duration takes nanoseconds    |--these are nsecs of a day--|
	duration := time.Duration(*validFor * 24 * 3600 * 1000 * 1000 * 1000)
	notAfter := notBefore.Add(duration)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	checkError("Failed to generate serial number: ", err)

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Country:            []string{},
			Organization:       []string{},
			OrganizationalUnit: []string{},
			CommonName:         "",
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
	}
	if len(*cnAttr) > 0 {
		template.Subject.CommonName = *cnAttr
	}
	if len(*cAttr) > 0 {
		template.Subject.Country = append(template.Subject.Country, *cAttr)
	}
	if len(*oAttr) > 0 {
		template.Subject.Organization = append(template.Subject.Organization, *oAttr)
	}
	if len(*ouAttr) > 0 {
		template.Subject.OrganizationalUnit = append(template.Subject.OrganizationalUnit, *ouAttr)
	}

	hosts := strings.Split(*host, ",")
	for _, h := range hosts {
		if ip := net.ParseIP(h); ip != nil {
			template.IPAddresses = append(template.IPAddresses, ip)
		} else {
			template.DNSNames = append(template.DNSNames, h)
		}
	}
	if len(*email) > 0 {
		emails := strings.Split(*email, ",")
		for _, e := range emails {
			template.EmailAddresses = append(template.EmailAddresses, e)
		}
	}

	if *isCA {
		cakey = privkey
		cacert = &template
		template.IsCA = true
		template.KeyUsage |= x509.KeyUsageCertSign
	}

	// Sign certificate
	log.Println("Signing certificate")
	derBytes, err := x509.CreateCertificate(rand.Reader, &template, cacert, publicKey(privkey), cakey)
	checkError("Failed to create certificate: ", err)

	// Check if files to be written exist
	outCrt := *outFile + "crt.pem"
	outKey := *outFile + "key.pem"
	if _, err := os.Stat(outCrt); err == nil {
		checkError("Certificate file exists: ",
			userConfirmation("Certificate file ("+outCrt+") exists. Overwrite? [Yn]: "))
	}
	if _, err := os.Stat(outKey); err == nil {
		checkError("Key file exists: ",
			userConfirmation("Key file ("+outKey+") exists. Overwrite? [Yn]: "))
	}

	// Save certificate to file
	log.Println("Writing certificate file: ", outCrt)
	certOut, err := os.Create(outCrt)
	checkError("Failed to open "+outCrt+" for writing: ", err)
	pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	if *chain {
		pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cacertpem.Bytes})
	}
	certOut.Close()

	// Save private key to file
	log.Println("Writing key file: ", outKey)
	keyOut, err := os.OpenFile(outKey, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	checkError("Failed to open key.pem for writing:", err)

	keyPemBlock := pemBlockForKey(privkey)
	if *encryptKey {
	ASK_KEY:
		pass1, err := readPassword("Enter password for private key: ")
		checkError("Error reading private key password, attempt 1: ", err)
		pass2, err := readPassword("Please re-enter password for private key: ")
		checkError("Error reading private key password, attempt 2: ", err)
		if string(pass1) == string(pass2) {
			keyPemBlock, err = x509.EncryptPEMBlock(rand.Reader, keyPemBlock.Type, keyPemBlock.Bytes, pass1, x509.PEMCipher3DES)
		} else {
			fmt.Println("Passwords mismatch. Try again.")
			goto ASK_KEY
		}
	}
	pem.Encode(keyOut, keyPemBlock)
	keyOut.Close()

	log.Println("Files written succesfully. Exiting.")
}
Beispiel #6
0
// FillTemplate is a utility function that tries to load as much of
// the certificate template as possible from the profiles and current
// template. It fills in the key uses, expiration, revocation URLs
// and SKI.
func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile) error {
	ski, err := ComputeSKI(template)

	var (
		eku             []x509.ExtKeyUsage
		ku              x509.KeyUsage
		backdate        time.Duration
		expiry          time.Duration
		notBefore       time.Time
		notAfter        time.Time
		crlURL, ocspURL string
		issuerURL       = profile.IssuerURL
	)

	// The third value returned from Usages is a list of unknown key usages.
	// This should be used when validating the profile at load, and isn't used
	// here.
	ku, eku, _ = profile.Usages()
	if profile.IssuerURL == nil {
		issuerURL = defaultProfile.IssuerURL
	}

	if ku == 0 && len(eku) == 0 {
		return cferr.New(cferr.PolicyError, cferr.NoKeyUsages)
	}

	if expiry = profile.Expiry; expiry == 0 {
		expiry = defaultProfile.Expiry
	}

	if crlURL = profile.CRL; crlURL == "" {
		crlURL = defaultProfile.CRL
	}
	if ocspURL = profile.OCSP; ocspURL == "" {
		ocspURL = defaultProfile.OCSP
	}
	if backdate = profile.Backdate; backdate == 0 {
		backdate = -5 * time.Minute
	} else {
		backdate = -1 * profile.Backdate
	}

	if !profile.NotBefore.IsZero() {
		notBefore = profile.NotBefore.UTC()
	} else {
		notBefore = time.Now().Round(time.Minute).Add(backdate).UTC()
	}

	if !profile.NotAfter.IsZero() {
		notAfter = profile.NotAfter.UTC()
	} else {
		notAfter = notBefore.Add(expiry).UTC()
	}

	template.NotBefore = notBefore
	template.NotAfter = notAfter
	template.KeyUsage = ku
	template.ExtKeyUsage = eku
	template.BasicConstraintsValid = true
	template.IsCA = profile.CAConstraint.IsCA
	if template.IsCA {
		template.MaxPathLen = profile.CAConstraint.MaxPathLen
		if template.MaxPathLen == 0 {
			template.MaxPathLenZero = profile.CAConstraint.MaxPathLenZero
		}
		template.DNSNames = nil
		template.EmailAddresses = nil
	}
	template.SubjectKeyId = ski

	if ocspURL != "" {
		template.OCSPServer = []string{ocspURL}
	}
	if crlURL != "" {
		template.CRLDistributionPoints = []string{crlURL}
	}

	if len(issuerURL) != 0 {
		template.IssuingCertificateURL = issuerURL
	}
	if len(profile.Policies) != 0 {
		err = addPolicies(template, profile.Policies)
		if err != nil {
			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
		}
	}
	if profile.OCSPNoCheck {
		ocspNoCheckExtension := pkix.Extension{
			Id:       asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5},
			Critical: false,
			Value:    []byte{0x05, 0x00},
		}
		template.ExtraExtensions = append(template.ExtraExtensions, ocspNoCheckExtension)
	}

	return nil
}