Skip to content

Latest commit

 

History

History
144 lines (124 loc) · 4.1 KB

File metadata and controls

144 lines (124 loc) · 4.1 KB
package main

import (
	"context"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"

	client "github.com/binance/binance-connector-go/clients/alpha"
	"github.com/binance/binance-connector-go/common/v2/common"
)

// Replace this with the base64(SHA256(subjectPublicKeyInfo)) value you pin to.
const PINNED_PUBLIC_KEY = "YOUR-PINNED-PUBLIC-KEY" // e.g. "AbC...==" (base64 of sha256(pubkey_der))

func main() {
	if err := GetExchangeInfo(); err != nil {
		log.Fatalf("failed: %v", err)
	}
}

func GetExchangeInfo() error {
	// Build TLS config with certificate pinning
	tlsCfg, err := tlsConfigWithPinning(PINNED_PUBLIC_KEY)
	if err != nil {
		return fmt.Errorf("build tls config: %w", err)
	}

	// Create an HTTP transport that uses our TLS config
	transport := &http.Transport{
		TLSClientConfig:     tlsCfg,
		IdleConnTimeout:     90 * time.Second,
		DisableKeepAlives:   false,
		MaxIdleConnsPerHost: 10,
	}

	// Optionally wrap transport with logging, retrying, etc.
	// For the Binance client, pass the transport as the HTTP agent:
	configuration := common.NewConfigurationRestAPI(
		common.WithBasePath(common.AlphaRestApiProdUrl),
		common.WithApiKey("Your API Key"),
		common.WithApiSecret("Your API Secret"),
	)
	apiClient := client.NewBinanceAlphaClient(
		client.WithRestAPI(configuration),
	)
	resp, err := apiClient.RestApi.MarketDataAPI.GetExchangeInfo(context.Background()).Execute()
	if err != nil {
		log.Println(err)
		return
	}

	dataValue, _ := json.MarshalIndent(resp.Data, "", "  ")
	log.Printf("Response: %s\n", string(dataValue))
	return nil
}

// tlsConfigWithPinning returns a tls.Config that verifies the chain using system roots
// and also performs public-key pinning: the base64(SHA256(pubkey_der)) must match pinnedKey.
func tlsConfigWithPinning(pinnedKey string) (*tls.Config, error) {
	roots, err := x509.SystemCertPool()
	if err != nil {
		// On some platforms this can return nil, try to create a new pool.
		roots = x509.NewCertPool()
	}
	if roots == nil {
		roots = x509.NewCertPool()
	}

	cfg := &tls.Config{
		// We'll perform verification ourselves in VerifyPeerCertificate so set InsecureSkipVerify.
		//
		// ------------------------BEWARE------------------------
		// THIS DISABLES HOSTNAME VERIFICATION AND OTHER CHECKS.
		// We must do them ourselves in VerifyPeerCertificate.
		// ------------------------------------------------------
		InsecureSkipVerify: true,
		VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
			// rawCerts are DER-encoded certs; first is leaf
			if len(rawCerts) == 0 {
				return errors.New("no server certificates presented")
			}

			// Parse certificates
			certs := make([]*x509.Certificate, len(rawCerts))
			for i, asn1Data := range rawCerts {
				cert, err := x509.ParseCertificate(asn1Data)
				if err != nil {
					return fmt.Errorf("failed to parse certificate[%d]: %w", i, err)
				}
				certs[i] = cert
			}

			// Verify chain using system roots
			opts := x509.VerifyOptions{
				Roots:         roots,
				Intermediates: x509.NewCertPool(),
				CurrentTime:   time.Now(),
				// DNSName will be checked by Verify, but we don't have the server name here.
				// If you need strict hostname matching, use cfg.ServerName or call Verify with DNSName set.
			}
			for i, cert := range certs {
				if i == 0 {
					continue // leaf skipping adding to intermediates
				}
				opts.Intermediates.AddCert(cert)
			}

			if _, err := certs[0].Verify(opts); err != nil {
				return fmt.Errorf("certificate chain verification failed: %w", err)
			}

			// Extract public key from leaf cert and marshal into PKIX (SubjectPublicKeyInfo) DER
			pubKeyDER, err := x509.MarshalPKIXPublicKey(certs[0].PublicKey)
			if err != nil {
				return fmt.Errorf("marshal public key: %w", err)
			}

			// Compute base64(SHA256(pubkey_der))
			sum := sha256.Sum256(pubKeyDER)
			hashB64 := base64.StdEncoding.EncodeToString(sum[:])

			if hashB64 != pinnedKey {
				return fmt.Errorf("public key pinning mismatch: expected %s got %s", pinnedKey, hashB64)
			}

			// All checks passed
			return nil
		},
	}

	return cfg, nil
}