YubiKeys are fantastic for securing authentication and cryptography, but integrating them directly into a Go application on Windows takes a few extra steps. In this article, we’ll walk through accessing a YubiKey in Go on Windows, step by step. Installing piv-go piv-go Install the piv-go module: go get github.com/go-piv/piv-go/piv go get github.com/go-piv/piv-go/piv Listing Connected YubiKeys on Windows The following Go code will show you all the YubiKeys connected to your machine: package main import ( "fmt" "github.com/go-piv/piv-go/piv" ) func main() { cards, err := piv.Cards() if err != nil { panic(err) } if len(cards) == 0 { fmt.Println("No YubiKeys detected") return } for _, card := range cards { fmt.Println("Found YubiKey:", card) } } package main import ( "fmt" "github.com/go-piv/piv-go/piv" ) func main() { cards, err := piv.Cards() if err != nil { panic(err) } if len(cards) == 0 { fmt.Println("No YubiKeys detected") return } for _, card := range cards { fmt.Println("Found YubiKey:", card) } } Accessing a PIV Slot on Windows This Go program detects a connected YubiKey (PIV smart card) on Windows, opens it via piv-go, and reads the certificate from the PIV Authentication slot. It then prints the certificate’s subject details. piv-go package main import ( "crypto/x509" "fmt" "github.com/go-piv/piv-go/piv" ) func main() { cards, err := piv.Cards() if err != nil || len(cards) == 0 { panic("No YubiKeys detected") } yk, err := piv.Open(cards[0]) if err != nil { panic(err) } defer yk.Close() cert, err := yk.Certificate(piv.SlotAuthentication) if err != nil { panic(err) } fmt.Println("Certificate Subject:", cert.Subject) } package main import ( "crypto/x509" "fmt" "github.com/go-piv/piv-go/piv" ) func main() { cards, err := piv.Cards() if err != nil || len(cards) == 0 { panic("No YubiKeys detected") } yk, err := piv.Open(cards[0]) if err != nil { panic(err) } defer yk.Close() cert, err := yk.Certificate(piv.SlotAuthentication) if err != nil { panic(err) } fmt.Println("Certificate Subject:", cert.Subject) } Signing Data on Windows This Go program opens a connected YubiKey PIV device on Windows, retrieves the private key from the PIV Authentication slot, and uses it to sign a SHA-256 digest of some data. It then prints the resulting signature in hex. package main import ( "crypto" "crypto/rand" "fmt" "github.com/go-piv/piv-go/piv" ) func main() { cards, err := piv.Cards() if err != nil || len(cards) == 0 { panic("No YubiKeys detected") } yk, err := piv.Open(cards[0]) if err != nil { panic(err) } defer yk.Close() key, err := yk.PrivateKey(piv.SlotAuthentication, nil) if err != nil { panic(err) } data := []byte("Hello, secure Windows world!") hash := crypto.SHA256.New() hash.Write(data) digest := hash.Sum(nil) signature, err := key.(crypto.Signer).Sign(rand.Reader, digest, crypto.SHA256) if err != nil { panic(err) } fmt.Printf("Signed data: %x\n", signature) } package main import ( "crypto" "crypto/rand" "fmt" "github.com/go-piv/piv-go/piv" ) func main() { cards, err := piv.Cards() if err != nil || len(cards) == 0 { panic("No YubiKeys detected") } yk, err := piv.Open(cards[0]) if err != nil { panic(err) } defer yk.Close() key, err := yk.PrivateKey(piv.SlotAuthentication, nil) if err != nil { panic(err) } data := []byte("Hello, secure Windows world!") hash := crypto.SHA256.New() hash.Write(data) digest := hash.Sum(nil) signature, err := key.(crypto.Signer).Sign(rand.Reader, digest, crypto.SHA256) if err != nil { panic(err) } fmt.Printf("Signed data: %x\n", signature) } Wrapping Up On Windows, piv-go leverages native WinSCard APIs to access YubiKeys, making it straightforward to use PIV slots and sign data. Programmatic access keeps your private keys secure and opens doors for custom authentication workflows on Windows systems. piv-go