func getResponse(cert, issuer *x509.Certificate) ([]byte, error) { if len(cert.OCSPServer) == 0 { return nil, errors.New("no OCSPServer provided") } req, err := ocsp.CreateRequest(cert, issuer, nil) if err != nil { return nil, err } for _, server := range cert.OCSPServer { if u, err := url.Parse(server); err == nil { u.Path = base64.StdEncoding.EncodeToString(req) if resp, err := http.Get(u.String()); err == nil && resp.StatusCode == http.StatusOK { body, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err == nil { return body, nil } log.Println(err) } else { log.Println(err) } } else { log.Println(err) } } return nil, errors.New("no OCSP response") }
func (cp *TLSConf) OCSP() (err error) { if cp.IsEnabled() && len(cp.kp.Certificate) > 1 { for _, ocsp_server := range cp.cert.OCSPServer { for _, issuing := range cp.cert.IssuingCertificateURL { //log.Println("OCSP : ["+ocsp_server+"] ["+issuing+"]") issuer, err := load_issuer(issuing) if err != nil { return err } request, err := ocsp.CreateRequest(cp.cert, issuer, &ocsp.RequestOptions{crypto.SHA1}) if err != nil { return err } staple := get_or_post_OCSP(ocsp_server, "application/ocsp-request", request) if len(staple) < MIN_STAPLE_SIZE { return nil } _, err = ocsp.ParseResponse(staple, issuer) //log.Printf("\n%+v\n", struct{ // ProducedAt, ThisUpdate, NextUpdate string // }{ resp.ProducedAt.Format(time.RFC3339), resp.ThisUpdate.Format(time.RFC3339), resp.NextUpdate.Format(time.RFC3339) } ) if err == nil { cp.kp.OCSPStaple = staple return nil } } } } return err }
// sendPurge should only be called as a Goroutine as it will block until the purge // request is successful func (updater *OCSPUpdater) sendPurge(der []byte) { cert, err := x509.ParseCertificate(der) if err != nil { updater.log.AuditErr(fmt.Sprintf("Failed to parse certificate for cache purge: %s", err)) return } req, err := ocsp.CreateRequest(cert, updater.issuer, nil) if err != nil { updater.log.AuditErr(fmt.Sprintf("Failed to create OCSP request for cache purge: %s", err)) return } // Create a GET style OCSP url for each endpoint in cert.OCSPServer (still waiting // on word from Akamai on how to properly purge cached POST requests, for now just // do GET) urls := []string{} for _, ocspServer := range cert.OCSPServer { urls = append( urls, path.Join(ocspServer, url.QueryEscape(base64.StdEncoding.EncodeToString(req))), ) } err = updater.ccu.Purge(urls) if err != nil { updater.log.AuditErr(fmt.Sprintf("Failed to purge OCSP response from CDN: %s", err)) } }
// GetOCSPForCert takes a PEM encoded cert or cert bundle and returns a OCSP // response from the OCSP endpoint in the certificate. // This []byte can be passed directly into the OCSPStaple property of a tls.Certificate. // If the bundle only contains the issued certificate, this function will try // to get the issuer certificate from the IssuingCertificateURL in the certificate. func GetOCSPForCert(bundle []byte) ([]byte, error) { certificates, err := parsePEMBundle(bundle) if err != nil { return nil, err } // We only got one certificate, means we have no issuer certificate - get it. if len(certificates) == 1 { // TODO: build fallback. If this fails, check the remaining array entries. resp, err := http.Get(certificates[0].IssuingCertificateURL[0]) if err != nil { return nil, err } issuerBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } issuerCert, err := x509.ParseCertificate(issuerBytes) if err != nil { return nil, err } // Insert it into the slice on position 0 // We want it ordered right SRV CRT -> CA certificates = append(certificates, issuerCert) } // We expect the certificate slice to be ordered downwards the chain. // SRV CRT -> CA. We need to pull the cert and issuer cert out of it, // which should always be the last two certificates. issuedCert := certificates[0] issuerCert := certificates[1] // Finally kick off the OCSP request. ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil) if err != nil { return nil, err } reader := bytes.NewReader(ocspReq) req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader) if err != nil { return nil, err } ocspResBytes, err := ioutil.ReadAll(req.Body) _, err = ocsp.ParseResponse(ocspResBytes, nil) if err != nil { log.Printf("OCSPParse Error: %v", err) return nil, err } return ocspResBytes, nil }
func certIsRevokedOCSP(leaf *x509.Certificate, strict bool) (revoked, ok bool) { var err error ocspURLs := leaf.OCSPServer if len(ocspURLs) == 0 { // OCSP not enabled for this certificate. return false, true } var issuer *x509.Certificate for _, issuingCert := range leaf.IssuingCertificateURL { issuer, err = fetchRemote(issuingCert) if err != nil { continue } break } if issuer == nil { return } ocspRequest, err := ocsp.CreateRequest(leaf, issuer, &ocspOpts) if err != nil { return } for _, server := range ocspURLs { resp, err := sendOCSPRequest(server, ocspRequest, issuer) if err != nil { if strict { return } continue } // There wasn't an error fetching the OCSP status. ok = true if resp.Status != ocsp.Good { // The certificate was revoked. revoked = true } return } return }
// Checks OCSP for a certificate. The immediate issuer must be specified. If // the certificate does not support OCSP, (nil, nil) is returned. Uses HTTP // GET rather than POST. The response is verified. The caller must check the // response status. The raw OCSP response is also returned, even if parsing // failed and err is non-nil. func (c *Client) CheckOCSPRaw(crt, issuer *x509.Certificate, ctx context.Context) (parsedResponse *ocsp.Response, rawResponse []byte, err error) { if len(crt.OCSPServer) == 0 { return } b, err := ocsp.CreateRequest(crt, issuer, nil) if err != nil { return } b64 := base64.StdEncoding.EncodeToString(b) path := crt.OCSPServer[0] + "/" + b64 req, err := http.NewRequest("GET", path, nil) if err != nil { return } req.Header.Set("Accept", "application/ocsp-response") res, err := c.doReqActual(req, ctx) if err != nil { return } defer res.Body.Close() if res.StatusCode != 200 { err = fmt.Errorf("OCSP response has status %#v", res.Status) return } if res.Header.Get("Content-Type") != "application/ocsp-response" { err = fmt.Errorf("response to OCSP request had unexpected content type") return } // Read response, limiting response to 1MiB. rawResponse, err = ioutil.ReadAll(denet.LimitReader(res.Body, 1*1024*1024)) if err != nil { return } parsedResponse, err = ocsp.ParseResponse(rawResponse, issuer) return }
// Checks OCSP for a certificate. The immediate issuer must be specified. If // the HTTP client is nil, the default client is used. If the certificate does // not support OCSP, (nil, nil) is returned. Uses HTTP GET rather than POST. // The response is verified. The caller must check the response status. func CheckOCSP(httpClient *http.Client, crt, issuer *x509.Certificate) (*ocsp.Response, error) { if httpClient == nil { httpClient = http.DefaultClient } if len(crt.OCSPServer) == 0 { return nil, nil } b, err := ocsp.CreateRequest(crt, issuer, nil) if err != nil { return nil, err } b64 := base64.StdEncoding.EncodeToString(b) path := crt.OCSPServer[0] + "/" + b64 req, err := http.NewRequest("GET", path, nil) if err != nil { return nil, err } req.Header.Set("Accept", "application/ocsp-response") res, err := httpClient.Do(req) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != 200 { return nil, fmt.Errorf("OCSP response has status %#v", res.Status) } if res.Header.Get("Content-Type") != "application/ocsp-response" { return nil, fmt.Errorf("response to OCSP request had unexpected content type") } resb, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } return ocsp.ParseResponse(resb, issuer) }
func main() { http.HandleFunc("/", newLoggingHandleFunc(rootHandler)) http.HandleFunc("/new", newLoggingHandleFunc(newHandler)) http.HandleFunc("/view/", newLoggingHandleFunc(newGzipHandleFunc(titleHandler(viewHandler)))) http.HandleFunc("/edit/", newLoggingHandleFunc(newGzipHandleFunc(titleHandler(editHandler)))) http.HandleFunc("/save/", newLoggingHandleFunc(titleHandler(saveHandler))) http.HandleFunc("/del/", newLoggingHandleFunc(titleHandler(delHandler))) http.HandleFunc("/front", newLoggingHandleFunc(newGzipHandleFunc(frontHandler))) http.HandleFunc("/hsts_hpkp", hsts_hpkp) log.SetPrefix("goWiki: ") log.Println("listening... on port", HTTPS_PORT) go func() { for { err := func() error { var OCSPC OCSPCert var err error cert, err := tls.LoadX509KeyPair(CERT, KEY) if err != nil { return err } OCSPC.cert = &cert if OCSPC.cert.Leaf, err = x509.ParseCertificate(OCSPC.cert.Certificate[0]); err != nil { return err } issuerRAW, err := ioutil.ReadFile(ISSUER) if err != nil { return err } for { var issuerPEM *pem.Block issuerPEM, issuerRAW = pem.Decode(issuerRAW) if issuerPEM == nil { break } if issuerPEM.Type == "CERTIFICATE" { OCSPC.issuer, err = x509.ParseCertificate(issuerPEM.Bytes) if err != nil { return err } } } if OCSPC.issuer == nil { return errors.New("no issuer") } OCSPC.req, err = ocsp.CreateRequest(OCSPC.cert.Leaf, OCSPC.issuer, nil) if err != nil { return err } err = OCSPC.updateStaple() if err != nil { return err } go OCSPC.stapleLoop() TLSConfig := new(tls.Config) TLSConfig.Certificates = []tls.Certificate{cert} TLSConfig.CipherSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA} TLSConfig.PreferServerCipherSuites = true TLSConfig.MinVersion = tls.VersionTLS11 //MaxVersion needed because of bug with TLS_FALLBACK_SCSV gonna be fixed in go 1.5 TLSConfig.MaxVersion = tls.VersionTLS12 TLSConfig.NextProtos = []string{"http/1.1"} TLSConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { OCSPC.RLock() defer OCSPC.RUnlock() return OCSPC.cert, nil } ln, err := net.Listen("tcp", HTTPS_PORT) if err != nil { return err } tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, TLSConfig) return new(http.Server).Serve(tlsListener) }() if err != nil { log.Println(err) } time.Sleep(time.Second * TIMEOUT) } }() for { log.Println("redirecting from port", HTTP_PORT, "to", HTTPS_PORT) err := http.ListenAndServe(HTTP_PORT, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Frame-Options", "SAMEORIGIN") w.Header().Set("Server", "Jesus") log.Println("redirecting http", r.RemoteAddr, "to https", DOMAIN+HTTPS_PORT+r.URL.Path) http.Redirect(w, r, "https://"+DOMAIN+HTTPS_PORT+r.URL.Path, http.StatusMovedPermanently) })) if err != nil { log.Println(err) } time.Sleep(time.Second * TIMEOUT) } }
// GetOCSPForCert takes a PEM encoded cert or cert bundle returning the raw OCSP response, // the parsed response, and an error, if any. The returned []byte can be passed directly // into the OCSPStaple property of a tls.Certificate. If the bundle only contains the // issued certificate, this function will try to get the issuer certificate from the // IssuingCertificateURL in the certificate. If the []byte and/or ocsp.Response return // values are nil, the OCSP status may be assumed OCSPUnknown. func GetOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) { certificates, err := parsePEMBundle(bundle) if err != nil { return nil, nil, err } // We expect the certificate slice to be ordered downwards the chain. // SRV CRT -> CA. We need to pull the leaf and issuer certs out of it, // which should always be the first two certificates. If there's no // OCSP server listed in the leaf cert, there's nothing to do. And if // we have only one certificate so far, we need to get the issuer cert. issuedCert := certificates[0] if len(issuedCert.OCSPServer) == 0 { return nil, nil, errors.New("no OCSP server specified in cert") } if len(certificates) == 1 { // TODO: build fallback. If this fails, check the remaining array entries. if len(issuedCert.IssuingCertificateURL) == 0 { return nil, nil, errors.New("no issuing certificate URL") } resp, err := httpGet(issuedCert.IssuingCertificateURL[0]) if err != nil { return nil, nil, err } defer resp.Body.Close() issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024)) if err != nil { return nil, nil, err } issuerCert, err := x509.ParseCertificate(issuerBytes) if err != nil { return nil, nil, err } // Insert it into the slice on position 0 // We want it ordered right SRV CRT -> CA certificates = append(certificates, issuerCert) } issuerCert := certificates[1] // Finally kick off the OCSP request. ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil) if err != nil { return nil, nil, err } reader := bytes.NewReader(ocspReq) req, err := httpPost(issuedCert.OCSPServer[0], "application/ocsp-request", reader) if err != nil { return nil, nil, err } defer req.Body.Close() ocspResBytes, err := ioutil.ReadAll(limitReader(req.Body, 1024*1024)) ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert) if err != nil { return nil, nil, err } if ocspRes.Certificate == nil { err = ocspRes.CheckSignatureFrom(issuerCert) if err != nil { return nil, nil, err } } return ocspResBytes, ocspRes, nil }
func main() { flag.Parse() if logURL == nil || logKey == nil || fileName == nil { flag.PrintDefaults() return } statuses[ocsp.Good] = "good" statuses[ocsp.Revoked] = "revoked" statuses[ocsp.Unknown] = "unknown" statuses[ocsp.ServerFailed] = "fail" pemPublicKey := fmt.Sprintf(`-----BEGIN PUBLIC KEY----- %s -----END PUBLIC KEY-----`, *logKey) ctLog, err := certificatetransparency.NewLog(*logURL, pemPublicKey) if err != nil { fmt.Fprintf(os.Stderr, "Failed to initialize log: %s\n", err) os.Exit(1) } file, err := os.OpenFile(*fileName, os.O_RDWR|os.O_CREATE, 0666) if err != nil { fmt.Fprintf(os.Stderr, "Failed to open entries file: %s\n", err) os.Exit(1) } defer file.Close() entriesFile := certificatetransparency.EntriesFile{file} if !*skipUpdate { sth, err := ctLog.GetSignedTreeHead() if err != nil { fmt.Fprintf(os.Stderr, "GetSignedTreeHead: %s\n", err) os.Exit(1) } fmt.Printf("%d total entries at %s\n", sth.Size, sth.Time.Format(time.ANSIC)) count, err := entriesFile.Count() if err != nil { fmt.Fprintf(os.Stderr, "\nFailed to read entries file: %s\n", err) os.Exit(1) } if count < sth.Size { _, err = ctLog.DownloadRange(file, nil, count, sth.Size) if err != nil { fmt.Fprintf(os.Stderr, "\nFailed to update CT log: %s\n", err) os.Exit(1) } } entriesFile.Seek(0, 0) treeHash, err := entriesFile.HashTree(nil, sth.Size) if err != nil { fmt.Fprintf(os.Stderr, "Error hashing tree: %s", err) os.Exit(1) } if !bytes.Equal(treeHash[:], sth.Hash) { fmt.Fprintf(os.Stderr, "Hashes do not match! Calculated: %x, STH contains %x\n", treeHash, sth.Hash) os.Exit(1) } fmt.Println("Hashes match! Calculated: %s, STH contains %s\n", hex.EncodeToString(treeHash[:]), hex.EncodeToString(sth.Hash[:])) entriesFile.Seek(0, 0) } dataChan := make(chan data) go func() { entriesFile.Map(func(ent *certificatetransparency.EntryAndPosition, err error) { if err != nil { return } cert, err := x509.ParseCertificate(ent.Entry.X509Cert) if err != nil { return } if cert.Issuer.CommonName != "Let's Encrypt Authority X1" { return } if time.Now().After(cert.NotAfter) { return } var issuer *x509.Certificate if len(ent.Entry.ExtraCerts) > 0 { issuer, err = x509.ParseCertificate(ent.Entry.ExtraCerts[0]) if err != nil { fmt.Fprintf(os.Stderr, "Failed to parse issuer: %s\n", err) return } } if len(cert.OCSPServer) == 0 { if cert.Issuer.CommonName != "Merge Delay Intermediate 1" { fmt.Fprintf(os.Stderr, "No OCSP Server for %s\n", cert.Issuer.CommonName) } return } ocspServer := cert.OCSPServer[0] req, err := ocsp.CreateRequest(cert, issuer, &ocsp.RequestOptions{}) if err != nil { fmt.Fprintf(os.Stderr, "Error creating OCSP request: %s\n", err) return } url := fmt.Sprintf("%s%s", ocspServer, base64.StdEncoding.EncodeToString(req)) start := time.Now() httpResponse, err := http.Post(ocspServer, "application/ocsp-request", bytes.NewBuffer(req)) if err != nil { fmt.Fprintf(os.Stderr, "Error fetching OCSP: %s %s\n", err, url) return } defer httpResponse.Body.Close() datum := data{ serial: fmt.Sprintf("%032x", cert.SerialNumber), names: cert.DNSNames, ocspLatency: time.Now().Sub(start), notBefore: cert.NotBefore, url: url, } if datum.ocspLatency > time.Second { fmt.Printf("slow response (%dms) for %x: %s\n", datum.ocspLatency/time.Millisecond, cert.SerialNumber, url) } names := strings.Join(cert.DNSNames, ", ") if err != nil { datum.ocspErr = fmt.Errorf("error fetching OCSP for %s %s: %s\n", names, url, err) dataChan <- datum return } ocspResponse, err := ioutil.ReadAll(httpResponse.Body) if err != nil { datum.ocspErr = fmt.Errorf("error reading OCSP for %s %s: %s\n", names, url, err) dataChan <- datum return } parsedResponse, err := ocsp.ParseResponse(ocspResponse, issuer) if err != nil { datum.ocspErr = fmt.Errorf("error parsing OCSP response for %s %s: %s\n", names, url, err) dataChan <- datum return } datum.nextUpdate = parsedResponse.NextUpdate datum.thisUpdate = parsedResponse.ThisUpdate dataChan <- datum }) close(dataChan) }() processData(dataChan) }