func addLog(t *testing.T, pub *PublisherImpl, port int, pubKey *ecdsa.PublicKey) { verifier, err := ct.NewSignatureVerifier(pubKey) test.AssertNotError(t, err, "Couldn't create signature verifier") pub.ctLogs = append(pub.ctLogs, &Log{ client: ctClient.New(fmt.Sprintf("http://localhost:%d", port)), verifier: verifier, }) }
func main() { flag.Parse() logClient := client.New(*logUri) matcher, err := createMatcherFromFlags() if err != nil { log.Fatal(err) } opts := scanner.ScannerOptions{ Matcher: matcher, BatchSize: *batchSize, NumWorkers: *numWorkers, ParallelFetch: *parallelFetch, StartIndex: *startIndex, Quiet: *quiet, } scanner := scanner.NewScanner(logClient, opts) scanner.Scan(logCertInfo, logPrecertInfo) }
// NewLog returns an initialized Log struct func NewLog(uri, b64PK string) (*Log, error) { if strings.HasSuffix(uri, "/") { uri = uri[0 : len(uri)-2] } client := ctClient.New(uri) pkBytes, err := base64.StdEncoding.DecodeString(b64PK) if err != nil { return nil, fmt.Errorf("Failed to decode base64 log public key") } pk, err := x509.ParsePKIXPublicKey(pkBytes) if err != nil { return nil, fmt.Errorf("Failed to parse log public key") } verifier, err := ct.NewSignatureVerifier(pk) if err != nil { return nil, err } return &Log{client, verifier}, nil }
// 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 } } 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) } } } if profile.ClientProvidesSerialNumbers { if req.Serial == nil { fmt.Printf("xx %#v\n", profile) return nil, cferr.New(cferr.CertificateError, cferr.MissingSerial) } safeTemplate.SerialNumber = req.Serial } else { serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err) } safeTemplate.SerialNumber = 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) } return s.sign(&certTBS, profile) }
// 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 { fmt.Printf("xx %#v\n", profile) 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 }
func main() { flag.Parse() var sctFileWriter io.Writer var err error if *sctInputFile != "" { sctFileWriter, err = os.Create(*sctInputFile) if err != nil { log.Fatal(err) } } else { sctFileWriter = ioutil.Discard } sctWriter := zlib.NewWriter(sctFileWriter) defer func() { err := sctWriter.Close() if err != nil { log.Fatal(err) } }() fetchLogClient := client.New(*sourceLogUri) matcher, err := createMatcher() if err != nil { log.Fatal(err) } opts := scanner.ScannerOptions{ Matcher: matcher, BatchSize: *batchSize, NumWorkers: *numWorkers, ParallelFetch: *parallelFetch, StartIndex: *startIndex, Quiet: *quiet, } scanner := scanner.NewScanner(fetchLogClient, opts) certs := make(chan *ct.LogEntry, *batchSize**parallelFetch) precerts := make(chan *ct.LogEntry, *batchSize**parallelFetch) addedCerts := make(chan *preload.AddedCert, *batchSize**parallelFetch) var sctWriterWG sync.WaitGroup sctWriterWG.Add(1) go sctWriterJob(addedCerts, sctWriter, &sctWriterWG) submitLogClient := client.New(*targetLogUri) var submitterWG sync.WaitGroup for w := 0; w < *parallelSubmit; w++ { submitterWG.Add(2) go certSubmitterJob(addedCerts, submitLogClient, certs, &submitterWG) go precertSubmitterJob(addedCerts, submitLogClient, precerts, &submitterWG) } addChainFunc := func(entry *ct.LogEntry) { certs <- entry } addPreChainFunc := func(entry *ct.LogEntry) { precerts <- entry } scanner.Scan(addChainFunc, addPreChainFunc) close(certs) close(precerts) submitterWG.Wait() close(addedCerts) sctWriterWG.Wait() }