Golang crypto-x509.NewCertPool类(方法)实例源码

下面列出了Golang crypto-x509.NewCertPool 类(方法)源码代码实例,从而了解它的用法。

作者:alex-moh    项目:kubernete   
func (c *Config) ApplyAuthenticationOptions(o *options.BuiltInAuthenticationOptions) (*Config, error) {
	if o == nil || o.PasswordFile == nil {
		return c, nil
	}

	if c.SecureServingInfo != nil {
		if o.ClientCert != nil && len(o.ClientCert.ClientCA) > 0 {
			clientCAs, err := certutil.CertsFromFile(o.ClientCert.ClientCA)
			if err != nil {
				return nil, fmt.Errorf("unable to load client CA file: %v", err)
			}
			if c.SecureServingInfo.ClientCA == nil {
				c.SecureServingInfo.ClientCA = x509.NewCertPool()
			}
			for _, cert := range clientCAs {
				c.SecureServingInfo.ClientCA.AddCert(cert)
			}
		}
		if o.RequestHeader != nil && len(o.RequestHeader.ClientCAFile) > 0 {
			clientCAs, err := certutil.CertsFromFile(o.RequestHeader.ClientCAFile)
			if err != nil {
				return nil, fmt.Errorf("unable to load requestheader client CA file: %v", err)
			}
			if c.SecureServingInfo.ClientCA == nil {
				c.SecureServingInfo.ClientCA = x509.NewCertPool()
			}
			for _, cert := range clientCAs {
				c.SecureServingInfo.ClientCA.AddCert(cert)
			}
		}
	}

	c.SupportsBasicAuth = len(o.PasswordFile.BasicAuthFile) > 0
	return c, nil
}

作者:TheHipp    项目:grpctes   
func createTLS(serverName string) (*tls.Config, error) {
	cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
	if err != nil {
		return nil, err
	}

	rootCa, err := ioutil.ReadFile("ca.crt")
	if err != nil {
		return nil, err
	}
	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM(rootCa) {
		return nil, fmt.Errorf("Could not add root crt")
	}

	clientCert, err := ioutil.ReadFile("backend.pem")
	if err != nil {
		return nil, fmt.Errorf("Could not read client cert")
	}
	clientPool := x509.NewCertPool()
	if !clientPool.AppendCertsFromPEM(clientCert) {
		return nil, fmt.Errorf("Could not add client pem")
	}

	tls := &tls.Config{
		ClientCAs:    clientPool,
		Certificates: []tls.Certificate{cert},
		ClientAuth:   tls.RequireAndVerifyClientCert,
		RootCAs:      cp,
		ServerName:   serverName,
	}
	tls.BuildNameToCertificate()
	return tls, nil
}

作者:hildj    项目:boulde   
// NewBundlerFromPEM creates a new Bundler from PEM-encoded root certificates and
// intermediate certificates.
func NewBundlerFromPEM(caBundlePEM, intBundlePEM []byte) (*Bundler, error) {
	b := &Bundler{
		RootPool:         x509.NewCertPool(),
		IntermediatePool: x509.NewCertPool(),
		KnownIssuers:     map[string]bool{},
	}

	log.Debug("parsing root certificates from PEM")
	roots, err := helpers.ParseCertificatesPEM(caBundlePEM)
	if err != nil {
		log.Errorf("failed to parse root bundle: %v", err)
		return nil, errors.New(errors.RootError, errors.ParseFailed)
	}

	log.Debug("parse intermediate certificates from PEM")
	var intermediates []*x509.Certificate
	if intermediates, err = helpers.ParseCertificatesPEM(intBundlePEM); err != nil {
		log.Errorf("failed to parse intermediate bundle: %v", err)
		return nil, errors.New(errors.IntermediatesError, errors.ParseFailed)
	}

	log.Debug("building certificate pools")
	for _, c := range roots {
		b.RootPool.AddCert(c)
		b.KnownIssuers[string(c.Signature)] = true
	}

	for _, c := range intermediates {
		b.IntermediatePool.AddCert(c)
		b.KnownIssuers[string(c.Signature)] = true
	}

	log.Debug("bundler set up")
	return b, nil
}

作者:ncd    项目:origi   
// ExtendedValidateRoute performs an extended validation on the route
// including checking that the TLS config is valid.
func ExtendedValidateRoute(route *routeapi.Route) field.ErrorList {
	tlsConfig := route.Spec.TLS
	result := field.ErrorList{}

	if tlsConfig == nil {
		return result
	}

	tlsFieldPath := field.NewPath("spec").Child("tls")
	if errs := validateTLS(route, tlsFieldPath); len(errs) != 0 {
		result = append(result, errs...)
	}

	// TODO: Check if we can be stricter with validating the certificate
	//       is for the route hostname. Don't want existing routes to
	//       break, so disable the hostname validation for now.
	// hostname := route.Spec.Host
	hostname := ""
	var certPool *x509.CertPool

	if len(tlsConfig.CACertificate) > 0 {
		certPool = x509.NewCertPool()
		if ok := certPool.AppendCertsFromPEM([]byte(tlsConfig.CACertificate)); !ok {
			result = append(result, field.Invalid(tlsFieldPath.Child("caCertificate"), tlsConfig.CACertificate, "failed to parse CA certificate"))
		}
	}

	verifyOptions := &x509.VerifyOptions{
		DNSName: hostname,
		Roots:   certPool,
	}

	if len(tlsConfig.Certificate) > 0 {
		if _, err := validateCertificatePEM(tlsConfig.Certificate, verifyOptions); err != nil {
			result = append(result, field.Invalid(tlsFieldPath.Child("certificate"), tlsConfig.Certificate, err.Error()))
		}

		certKeyBytes := []byte{}
		certKeyBytes = append(certKeyBytes, []byte(tlsConfig.Certificate)...)
		if len(tlsConfig.Key) > 0 {
			certKeyBytes = append(certKeyBytes, byte('\n'))
			certKeyBytes = append(certKeyBytes, []byte(tlsConfig.Key)...)
		}

		if _, err := tls.X509KeyPair(certKeyBytes, certKeyBytes); err != nil {
			result = append(result, field.Invalid(tlsFieldPath.Child("key"), tlsConfig.Key, err.Error()))
		}
	}

	if len(tlsConfig.DestinationCACertificate) > 0 {
		roots := x509.NewCertPool()
		if ok := roots.AppendCertsFromPEM([]byte(tlsConfig.DestinationCACertificate)); !ok {
			result = append(result, field.Invalid(tlsFieldPath.Child("destinationCACertificate"), tlsConfig.DestinationCACertificate, "failed to parse destination CA certificate"))
		}
	}

	return result
}

作者:RicHernandez    项目:fabri   
func (node *nodeImpl) initCryptoEngine() error {
	node.debug("Initializing node crypto engine...")

	// Init CLI
	node.eciesSPI = ecies.NewSPI()

	// Init certPools
	node.rootsCertPool = x509.NewCertPool()
	node.tlsCertPool = x509.NewCertPool()
	node.ecaCertPool = x509.NewCertPool()
	node.tcaCertPool = x509.NewCertPool()

	// Load ECA certs chain
	if err := node.loadECACertsChain(); err != nil {
		return err
	}

	// Load TCA certs chain
	if err := node.loadTCACertsChain(); err != nil {
		return err
	}

	// Load enrollment secret key
	if err := node.loadEnrollmentKey(); err != nil {
		return err
	}

	// Load enrollment certificate and set validator ID
	if err := node.loadEnrollmentCertificate(); err != nil {
		return err
	}

	// Load enrollment id
	if err := node.loadEnrollmentID(); err != nil {
		return err
	}

	// Load enrollment chain key
	if err := node.loadEnrollmentChainKey(); err != nil {
		return err
	}

	// Load TLS certs chain certificate
	if err := node.loadTLSCACertsChain(); err != nil {
		return err
	}

	// Load tls certificate
	if err := node.loadTLSCertificate(); err != nil {
		return err
	}

	node.debug("Initializing node crypto engine...done!")

	return nil
}

作者:Psiphon-Lab    项目:psiphon-tunnel-cor   
// Verify x509 certificate chain
func (x5c X5C) verifyCertChain() (leaf *x509.Certificate, validCN bool, err error) {
	if len(x5c) == 0 || len(x5c) > 10 {
		// OpenSSL's default maximum chain length is 10
		return nil, false, fmt.Errorf("Invalid certchain length of %d\n", len(x5c))
	}

	// Parse leaf certificate
	leafCertDer, err := base64.StdEncoding.DecodeString(x5c[0])
	if err != nil {
		return nil, false, err
	}
	leafCert, err := x509.ParseCertificate(leafCertDer)
	if err != nil {
		return nil, false, err
	}

	// Verify CN
	if leafCert.Subject.CommonName == safetynetCN {
		validCN = true
	}

	// Parse and add intermediate certificates
	intermediates := x509.NewCertPool()
	for i := 1; i < len(x5c); i++ {
		intermediateCertDer, err := base64.StdEncoding.DecodeString(x5c[i])
		if err != nil {
			return leafCert, false, err
		}

		intermediateCert, err := x509.ParseCertificate(intermediateCertDer)
		if err != nil {
			return leafCert, false, err
		}
		intermediates.AddCert(intermediateCert)
	}

	// Parse and verify root cert
	roots := x509.NewCertPool()
	ok := roots.AppendCertsFromPEM([]byte(geotrustCert))
	if !ok {
		return leafCert, false, fmt.Errorf("Failed to append GEOTRUST cert\n")
	}

	// Verify leaf certificate
	storeCtx := x509.VerifyOptions{
		Intermediates: intermediates,
		Roots:         roots,
		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
	}
	_, err = leafCert.Verify(storeCtx)
	if err != nil {
		return leafCert, false, err
	}

	return leafCert, validCN, nil
}

作者:juggernau    项目:cl   
func (repo CloudControllerBuildpackBitsRepository) downloadBuildpack(url string, cb func(*os.File, error)) {
	fileutils.TempFile("buildpack-download", func(tempfile *os.File, err error) {
		if err != nil {
			cb(nil, err)
			return
		}

		var certPool *x509.CertPool
		if len(repo.TrustedCerts) > 0 {
			certPool = x509.NewCertPool()
			for _, tlsCert := range repo.TrustedCerts {
				cert, _ := x509.ParseCertificate(tlsCert.Certificate[0])
				certPool.AddCert(cert)
			}
		}

		client := &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: &tls.Config{RootCAs: certPool},
				Proxy:           http.ProxyFromEnvironment,
			},
		}

		response, err := client.Get(url)
		if err != nil {
			cb(nil, err)
			return
		}

		io.Copy(tempfile, response.Body)
		tempfile.Seek(0, 0)
		cb(tempfile, nil)
	})
}

作者:yuda    项目:gott   
func (app *App) makeServer(addr string, handler *http.Handler) (*http.Server, error) {
	server := &http.Server{
		Addr:    addr,
		Handler: *handler,
	}

	if app.options.EnableTLSClientAuth {
		caFile := ExpandHomeDir(app.options.TLSCACrtFile)
		log.Printf("CA file: " + caFile)
		caCert, err := ioutil.ReadFile(caFile)
		if err != nil {
			return nil, errors.New("Could not open CA crt file " + caFile)
		}
		caCertPool := x509.NewCertPool()
		if !caCertPool.AppendCertsFromPEM(caCert) {
			return nil, errors.New("Could not parse CA crt file data in " + caFile)
		}
		tlsConfig := &tls.Config{
			ClientCAs:  caCertPool,
			ClientAuth: tls.RequireAndVerifyClientCert,
		}
		server.TLSConfig = tlsConfig
	}

	return server, nil
}

作者:clint    项目:swar   
// Load the TLS certificates/keys and, if verify is true, the CA.
func loadTLSConfig(ca, cert, key string, verify bool) (*tls.Config, error) {
	c, err := tls.LoadX509KeyPair(cert, key)
	if err != nil {
		return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
			cert, key, err)
	}

	config := &tls.Config{
		Certificates: []tls.Certificate{c},
		MinVersion:   tls.VersionTLS10,
	}

	if verify {
		certPool := x509.NewCertPool()
		file, err := ioutil.ReadFile(ca)
		if err != nil {
			return nil, fmt.Errorf("Couldn't read CA certificate: %s", err)
		}
		certPool.AppendCertsFromPEM(file)
		config.RootCAs = certPool
		config.ClientAuth = tls.RequireAndVerifyClientCert
		config.ClientCAs = certPool
	} else {
		// If --tlsverify is not supplied, disable CA validation.
		config.InsecureSkipVerify = true
	}

	return config, nil
}

作者:rawlings    项目:gofabric   
// NewTLSServer creates and starts a TLS-enabled testing server.
func NewTLSServer(bind string, containerChan chan<- *docker.Container, hook func(*http.Request), tlsConfig TLSConfig) (*DockerServer, error) {
	listener, err := net.Listen("tcp", bind)
	if err != nil {
		return nil, err
	}
	defaultCertificate, err := tls.LoadX509KeyPair(tlsConfig.CertPath, tlsConfig.CertKeyPath)
	if err != nil {
		return nil, err
	}
	tlsServerConfig := new(tls.Config)
	tlsServerConfig.Certificates = []tls.Certificate{defaultCertificate}
	if tlsConfig.RootCAPath != "" {
		rootCertPEM, err := ioutil.ReadFile(tlsConfig.RootCAPath)
		if err != nil {
			return nil, err
		}
		certsPool := x509.NewCertPool()
		certsPool.AppendCertsFromPEM(rootCertPEM)
		tlsServerConfig.RootCAs = certsPool
	}
	tlsListener := tls.NewListener(listener, tlsServerConfig)
	server := buildDockerServer(tlsListener, containerChan, hook)
	go http.Serve(tlsListener, server)
	return server, nil
}

作者:rpheut    项目:router   
func (ff *ForwardFrontend) getCACert() *tls.Config {
	mTLSConfig := &tls.Config{
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		},
	}

	mTLSConfig.PreferServerCipherSuites = true
	mTLSConfig.MinVersion = tls.VersionTLS10
	mTLSConfig.MaxVersion = tls.VersionTLS12

	certs := x509.NewCertPool()

	pemData, err := ioutil.ReadFile(ff.config.CA)
	if err != nil {
		// do error
	}
	certs.AppendCertsFromPEM(pemData)
	mTLSConfig.RootCAs = certs

	return mTLSConfig
}

作者:djibi    项目:lx   
func (c *Client) baseGet(getUrl string) (*Response, error) {
	req, err := http.NewRequest("GET", getUrl, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", shared.UserAgent)

	resp, err := c.Http.Do(req)
	if err != nil {
		return nil, err
	}

	if c.scert != nil && resp.TLS != nil {
		if !bytes.Equal(resp.TLS.PeerCertificates[0].Raw, c.scert.Raw) {
			pUrl, _ := url.Parse(getUrl)
			return nil, fmt.Errorf(i18n.G("Server certificate for host %s has changed. Add correct certificate or remove certificate in %s"), pUrl.Host, c.Config.ConfigPath("servercerts"))
		}
	}

	if c.scertDigestSet == false && resp.TLS != nil {
		c.scertWire = resp.TLS.PeerCertificates[0]
		c.scertIntermediates = x509.NewCertPool()
		for _, cert := range resp.TLS.PeerCertificates {
			c.scertIntermediates.AddCert(cert)
		}
		c.scertDigest = sha256.Sum256(resp.TLS.PeerCertificates[0].Raw)
		c.scertDigestSet = true
	}

	return HoistResponse(resp, Sync)
}

作者:f47h3    项目:go-mutual-tl   
func main() {
	certBytes, err := ioutil.ReadFile("../cert.pem")
	if err != nil {
		log.Fatalln("Unable to read cert.pem", err)
	}

	clientCertPool := x509.NewCertPool()
	if ok := clientCertPool.AppendCertsFromPEM(certBytes); !ok {
		log.Fatalln("Unable to add certificate to certificate pool")
	}

	tlsConfig := &tls.Config{
		// Reject any TLS certificate that cannot be validated
		ClientAuth: tls.RequireAndVerifyClientCert,
		// Ensure that we only use our "CA" to validate certificates
		ClientCAs: clientCertPool,
		// PFS because we can
		CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
		// Force it server side
		PreferServerCipherSuites: true,
		// TLS 1.2 because we can
		MinVersion: tls.VersionTLS12,
	}

	tlsConfig.BuildNameToCertificate()

	http.HandleFunc("/", HelloUser)

	httpServer := &http.Server{
		Addr:      ":8080",
		TLSConfig: tlsConfig,
	}

	log.Println(httpServer.ListenAndServeTLS("../cert.pem", "../key.pem"))
}

作者:kelseyhightowe    项目:conf   
func getConfig(address, cert, key, caCert string) (*vaultapi.Config, error) {
	conf := vaultapi.DefaultConfig()
	conf.Address = address

	tlsConfig := &tls.Config{}
	if cert != "" && key != "" {
		clientCert, err := tls.LoadX509KeyPair(cert, key)
		if err != nil {
			return nil, err
		}
		tlsConfig.Certificates = []tls.Certificate{clientCert}
		tlsConfig.BuildNameToCertificate()
	}

	if caCert != "" {
		ca, err := ioutil.ReadFile(caCert)
		if err != nil {
			return nil, err
		}
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(ca)
		tlsConfig.RootCAs = caCertPool
	}

	conf.HttpClient.Transport = &http.Transport{
		TLSClientConfig: tlsConfig,
	}

	return conf, nil
}

作者:apaow    项目:gocqltabl   
func setupTLSConfig(sslOpts *SslOptions) (*tls.Config, error) {
	certPool := x509.NewCertPool()
	// ca cert is optional
	if sslOpts.CaPath != "" {
		pem, err := ioutil.ReadFile(sslOpts.CaPath)
		if err != nil {
			return nil, fmt.Errorf("connectionpool: unable to open CA certs: %v", err)
		}

		if !certPool.AppendCertsFromPEM(pem) {
			return nil, errors.New("connectionpool: failed parsing or CA certs")
		}
	}

	mycert, err := tls.LoadX509KeyPair(sslOpts.CertPath, sslOpts.KeyPath)
	if err != nil {
		return nil, fmt.Errorf("connectionpool: unable to load X509 key pair: %v", err)
	}

	config := &tls.Config{
		Certificates: []tls.Certificate{mycert},
		RootCAs:      certPool,
	}

	config.InsecureSkipVerify = !sslOpts.EnableHostVerification

	return config, nil
}

作者:paulta    项目:deceiv   
func main() {
	conf := GetConfig()

	caPool := x509.NewCertPool()
	x509CaCrt, err := ioutil.ReadFile(conf.CaCert)
	if err != nil {
		panic(err)
	}
	if ok := caPool.AppendCertsFromPEM(x509CaCrt); !ok {
		panic(fmt.Errorf("Error appending CA cert from PEM!"))
	}

	s := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", conf.Host, conf.Port),
		Handler: http.DefaultServeMux,
		TLSConfig: &tls.Config{
			ClientAuth: tls.RequireAndVerifyClientCert,
			ClientCAs:  caPool,
		},
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		clientName := r.TLS.PeerCertificates[0].Subject.CommonName
		l := func(message string, args ...interface{}) {
			log.Printf("%s: %s", clientName, fmt.Sprintf(message, args...))
		}
		HandleUpload(l, conf, w, r, clientName)
	})

	log.Printf("Listening...\n")
	log.Fatal(s.ListenAndServeTLS(conf.Cert, conf.Key))
}

作者:pombredann    项目:docke   
func setupTls(cert, key, ca string, l net.Listener) (net.Listener, error) {
	tlsCert, err := tls.LoadX509KeyPair(cert, key)
	if err != nil {
		return nil, fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
			cert, key, err)
	}
	tlsConfig := &tls.Config{
		NextProtos:   []string{"http/1.1"},
		Certificates: []tls.Certificate{tlsCert},
		// Avoid fallback on insecure SSL protocols
		MinVersion: tls.VersionTLS10,
	}

	if ca != "" {
		certPool := x509.NewCertPool()
		file, err := ioutil.ReadFile(ca)
		if err != nil {
			return nil, fmt.Errorf("Couldn't read CA certificate: %s", err)
		}
		certPool.AppendCertsFromPEM(file)
		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
		tlsConfig.ClientCAs = certPool
	}

	return tls.NewListener(l, tlsConfig), nil
}

作者:sisatec    项目:raf   
func clientTLS() *tls.Config {

	tlsConfig := &tls.Config{}

	cert, err := tls.LoadX509KeyPair("test/client0.crt", "test/client0.key")
	if err != nil {
		log.Fatalf("Can not load certificate: %s", err.Error())
	}

	tlsConfig.Certificates = []tls.Certificate{cert}

	certPool := x509.NewCertPool()
	pem, err := ioutil.ReadFile("test/ca.crt")
	if err != nil {
		log.Fatalf("Can not read CA for client tls: %s", err.Error())
	}

	ok := certPool.AppendCertsFromPEM(pem)

	if !ok {
		log.Fatalf("Can not append cert")
	}

	tlsConfig.RootCAs = certPool

	return tlsConfig
}

作者:mattbair    项目:tableau4g   
// apps will set two OS variables:
// atscale_http_sslcert - location of the http ssl cert
// atscale_http_sslkey - location of the http ssl key
func NewTimeoutClient(cTimeout time.Duration, rwTimeout time.Duration, useClientCerts bool) *http.Client {
	certLocation := os.Getenv("atscale_http_sslcert")
	keyLocation := os.Getenv("atscale_http_sslkey")
	caFile := os.Getenv("atscale_ca_file")
	// default
	tlsConfig := &tls.Config{InsecureSkipVerify: true}
	if useClientCerts && len(certLocation) > 0 && len(keyLocation) > 0 {
		// Load client cert if available
		cert, err := tls.LoadX509KeyPair(certLocation, keyLocation)
		if err == nil {
			if len(caFile) > 0 {
				caCertPool := x509.NewCertPool()
				caCert, err := ioutil.ReadFile(caFile)
				if err != nil {
					fmt.Printf("Error setting up caFile [%s]:%v\n", caFile, err)
				}
				caCertPool.AppendCertsFromPEM(caCert)
				tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true, RootCAs: caCertPool}
				tlsConfig.BuildNameToCertificate()
			} else {
				tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
			}
		}
	}
	return &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: tlsConfig,
			Dial:            timeoutDialer(cTimeout, rwTimeout),
		},
	}
}

作者:Jimmy-X    项目:learn-g   
func main() {
	flag.Parse()
	log.SetFlags(0)

	pool := x509.NewCertPool()
	caCertPath := "ssl/ca.crt"
	caCrt, err := ioutil.ReadFile(caCertPath)
	if err != nil {
		fmt.Println("ReadFile err:", err)
		return
	}
	pool.AppendCertsFromPEM(caCrt)

	s := &http.Server{
		Addr:    ":8888",
		Handler: &myHandler{},
		TLSConfig: &tls.Config{
			ClientCAs:  pool,
			ClientAuth: tls.RequireAndVerifyClientCert,
		},
	}
	mux = make(map[string]func(http.ResponseWriter, *http.Request))
	mux["/"] = home
	mux["/echo"] = echo

	log.Fatal(s.ListenAndServeTLS("ssl/server.crt", "ssl/server.key"))
}


问题


面经


文章

微信
公众号

扫码关注公众号