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
Install the piv-go module:
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)
}
}
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.
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)
}
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.
