func (k Key) Public() crypto.PublicKey {
	return k.pub
}
Public exports a public key to implement crypto.Signer
package certtostore
import (
	"bytes"
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rsa"
	"crypto/x509"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/big"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"strings"
	"syscall"
	"time"
	"unicode/utf16"
	"unsafe"
	"golang.org/x/crypto/cryptobyte/asn1"
	"golang.org/x/crypto/cryptobyte"
	"golang.org/x/sys/windows"
	"github.com/google/logger"
)
const (
	acquireCached           = 0x1                                              
	acquireSilent           = 0x40                                             
	acquireOnlyNCryptKey    = 0x40000                                          
	encodingX509ASN         = 1                                                
	encodingPKCS7           = 65536                                            
	certStoreProvSystem     = 10                                               
	certStoreCurrentUser    = uint32(certStoreCurrentUserID << compareShift)   
	certStoreLocalMachine   = uint32(certStoreLocalMachineID << compareShift)  
	certStoreCurrentUserID  = 1                                                
	certStoreLocalMachineID = 2                                                
	infoIssuerFlag          = 4                                                
	compareNameStrW         = 8                                                
	compareShift            = 16                                               
	findIssuerStr           = compareNameStrW<<compareShift | infoIssuerFlag   
	signatureKeyUsage       = 0x80                                             
	ncryptKeySpec           = 0xFFFFFFFF                                       
	bCryptPadPKCS1 uintptr = 0x2
	rsa1Magic = 0x31415352  
	ecs1Magic = 0x31534345  
	ecs3Magic = 0x33534345  
	ecs5Magic = 0x35534345  
	ncryptPersistFlag           = 0x80000000  
	ncryptAllowDecryptFlag      = 0x1         
	ncryptAllowSigningFlag      = 0x2         
	ncryptWriteKeyToLegacyStore = 0x00000200  
	NCryptPadOAEPFlag = 0x00000004  
	nCryptMachineKey   = 0x20  
	nCryptOverwriteKey = 0x80  
	cryptENotFound = 0x80092004  
	ProviderMSPlatform = "Microsoft Platform Crypto Provider"
	ProviderMSSoftware = "Microsoft Software Key Storage Provider"
	ProviderMSLegacy = "Microsoft Enhanced Cryptographic Provider v1.0"
	hcceLocalMachine                  = windows.Handle(0x01)  
	certChainCacheOnlyURLRetrieval    = 0x00000004            
	certChainDisableAIA               = 0x00002000            
	certChainRevocationCheckCacheOnly = 0x80000000            
)
var (
	bCryptRSAPublicBlob = wide("RSAPUBLICBLOB")
	bCryptECCPublicBlob = wide("ECCPUBLICBLOB")
	nCryptAlgorithmGroupProperty = wide("Algorithm Group")  
	nCryptUniqueNameProperty     = wide("Unique Name")      
	curveIDs = map[uint32]elliptic.Curve{
		ecs1Magic: elliptic.P256(),  
		ecs3Magic: elliptic.P384(),  
		ecs5Magic: elliptic.P521(),  
	}
	algIDs = map[crypto.Hash]*uint16{
		crypto.SHA1:   wide("SHA1"),    
		crypto.SHA256: wide("SHA256"),  
		crypto.SHA384: wide("SHA384"),  
		crypto.SHA512: wide("SHA512"),  
	}
	my   = wide("MY")
	ca   = wide("CA")
	root = wide("ROOT")
	crypt32 = windows.MustLoadDLL("crypt32.dll")
	nCrypt  = windows.MustLoadDLL("ncrypt.dll")
	certDeleteCertificateFromStore    = crypt32.MustFindProc("CertDeleteCertificateFromStore")
	certFindCertificateInStore        = crypt32.MustFindProc("CertFindCertificateInStore")
	certFreeCertificateChain          = crypt32.MustFindProc("CertFreeCertificateChain")
	certGetCertificateChain           = crypt32.MustFindProc("CertGetCertificateChain")
	certGetIntendedKeyUsage           = crypt32.MustFindProc("CertGetIntendedKeyUsage")
	cryptAcquireCertificatePrivateKey = crypt32.MustFindProc("CryptAcquireCertificatePrivateKey")
	cryptFindCertificateKeyProvInfo   = crypt32.MustFindProc("CryptFindCertificateKeyProvInfo")
	nCryptCreatePersistedKey          = nCrypt.MustFindProc("NCryptCreatePersistedKey")
	nCryptDecrypt                     = nCrypt.MustFindProc("NCryptDecrypt")
	nCryptExportKey                   = nCrypt.MustFindProc("NCryptExportKey")
	nCryptFinalizeKey                 = nCrypt.MustFindProc("NCryptFinalizeKey")
	nCryptOpenKey                     = nCrypt.MustFindProc("NCryptOpenKey")
	nCryptOpenStorageProvider         = nCrypt.MustFindProc("NCryptOpenStorageProvider")
	nCryptGetProperty                 = nCrypt.MustFindProc("NCryptGetProperty")
	nCryptSetProperty                 = nCrypt.MustFindProc("NCryptSetProperty")
	nCryptSignHash                    = nCrypt.MustFindProc("NCryptSignHash")
)
type paddingInfo struct {
	pszAlgID *uint16
}
func wide(s string) *uint16 {
	w := utf16.Encode([]rune(s))
	w = append(w, 0)
	return &w[0]
}
func openProvider(provider string) (uintptr, error) {
	var err error
	var hProv uintptr
	pname := wide(provider)
	r, _, err := nCryptOpenStorageProvider.Call(uintptr(unsafe.Pointer(&hProv)), uintptr(unsafe.Pointer(pname)), 0)
	if r == 0 {
		return hProv, nil
	}
	return hProv, fmt.Errorf("NCryptOpenStorageProvider returned %X: %v", r, err)
}
func findCert(store windows.Handle, enc, findFlags, findType uint32, para *uint16, prev *windows.CertContext) (*windows.CertContext, error) {
	h, _, err := certFindCertificateInStore.Call(
		uintptr(store),
		uintptr(enc),
		uintptr(findFlags),
		uintptr(findType),
		uintptr(unsafe.Pointer(para)),
		uintptr(unsafe.Pointer(prev)),
	)
	if h == 0 {
		if errno, ok := err.(syscall.Errno); ok && errno == cryptENotFound {
			return nil, nil
		}
		return nil, err
	}
	return (*windows.CertContext)(unsafe.Pointer(h)), nil
}
func intendedKeyUsage(enc uint32, cert *windows.CertContext) (usage uint16) {
	certGetIntendedKeyUsage.Call(uintptr(enc), uintptr(unsafe.Pointer(cert.CertInfo)), uintptr(unsafe.Pointer(&usage)), 2)
	return
}
type WinCertStore struct {
	CStore              windows.Handle
	Prov                uintptr
	ProvName            string
	issuers             []string
	intermediateIssuers []string
	container           string
	keyStorageFlags     uintptr
	certChains          [][]*x509.Certificate
}
func OpenWinCertStore(provider, container string, issuers, intermediateIssuers []string, legacyKey bool) (*WinCertStore, error) {
	cngProv, err := openProvider(provider)
	if err != nil {
		return nil, fmt.Errorf("unable to open crypto provider or provider not available: %v", err)
	}
	wcs := &WinCertStore{
		Prov:                cngProv,
		ProvName:            provider,
		issuers:             issuers,
		intermediateIssuers: intermediateIssuers,
		container:           container,
	}
	if legacyKey {
		wcs.keyStorageFlags = ncryptWriteKeyToLegacyStore
		wcs.ProvName = ProviderMSLegacy
	}
	return wcs, nil
}
func certContextToX509(ctx *windows.CertContext) (*x509.Certificate, error) {
	var der []byte
	slice := (*reflect.SliceHeader)(unsafe.Pointer(&der))
	slice.Data = uintptr(unsafe.Pointer(ctx.EncodedCert))
	slice.Len = int(ctx.Length)
	slice.Cap = int(ctx.Length)
	return x509.ParseCertificate(der)
}
func extractSimpleChain(simpleChain **windows.CertSimpleChain, chainCount, chainIndex int) ([]*x509.Certificate, error) {
	if simpleChain == nil || chainCount == 0 || chainIndex >= chainCount {
		return nil, errors.New("invalid simple chain")
	}
	simpleChains := (*[1 << 20]*windows.CertSimpleChain)(unsafe.Pointer(simpleChain))[:chainCount:chainCount]
	lastChain := simpleChains[chainIndex]
	chainLen := int(lastChain.NumElements)
	elements := (*[1 << 20]*windows.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:chainLen:chainLen]
	chain := make([]*x509.Certificate, 0, chainLen)
	for _, element := range elements {
		xc, err := certContextToX509(element.CertContext)
		if err != nil {
			return nil, err
		}
		chain = append(chain, xc)
	}
	return chain, nil
}
func (w *WinCertStore) resolveChains(cert *windows.CertContext) error {
	var (
		chainPara windows.CertChainPara
		chainCtx  *windows.CertChainContext
	)
	chainPara.Size = uint32(unsafe.Sizeof(chainPara))
	success, _, err := certGetCertificateChain.Call(
		uintptr(unsafe.Pointer(hcceLocalMachine)),
		uintptr(unsafe.Pointer(cert)),
		uintptr(unsafe.Pointer(nil)),  
		uintptr(cert.Store),
		uintptr(unsafe.Pointer(&chainPara)),
		certChainRevocationCheckCacheOnly|certChainCacheOnlyURLRetrieval|certChainDisableAIA,
		uintptr(unsafe.Pointer(nil)),  
		uintptr(unsafe.Pointer(&chainCtx)),
	)
	if success == 0 {
		return fmt.Errorf("certGetCertificateChain: %v", err)
	}
	defer certFreeCertificateChain.Call(uintptr(unsafe.Pointer(chainCtx)))
	chainCount := int(chainCtx.ChainCount)
	certChains := make([][]*x509.Certificate, 0, chainCount)
	for i := 0; i < chainCount; i++ {
		x509Certs, err := extractSimpleChain(chainCtx.Chains, chainCount, i)
		if err != nil {
			return fmt.Errorf("extractSimpleChain: %v", err)
		}
		certChains = append(certChains, x509Certs)
	}
	return nil
}
func (w *WinCertStore) Cert() (*x509.Certificate, error) {
	c, ctx, err := w.cert(w.issuers, my, certStoreLocalMachine)
	if err != nil {
		return nil, err
	}
	if c == nil {
		return nil, nil
	}
	if err := w.resolveChains(ctx); err != nil {
		return nil, err
	}
	return c, nil
}
func (w *WinCertStore) CertWithContext() (*x509.Certificate, *windows.CertContext, error) {
	c, ctx, err := w.cert(w.issuers, my, certStoreLocalMachine)
	if err != nil {
		return nil, nil, err
	}
	if c == nil {
		return nil, nil, nil
	}
	if err := w.resolveChains(ctx); err != nil {
		return nil, nil, err
	}
	return c, ctx, nil
}
func (w *WinCertStore) cert(issuers []string, searchRoot *uint16, store uint32) (*x509.Certificate, *windows.CertContext, error) {
	certStore, err := windows.CertOpenStore(
		certStoreProvSystem,
		0,
		0,
		store,
		uintptr(unsafe.Pointer(searchRoot)))
	if err != nil {
		return nil, nil, fmt.Errorf("CertOpenStore returned: %v", err)
	}
	defer windows.CertCloseStore(certStore, 0)
	var prev *windows.CertContext
	var cert *x509.Certificate
	for _, issuer := range issuers {
		i, err := windows.UTF16PtrFromString(issuer)
		if err != nil {
			return nil, nil, err
		}
		nc, err := findCert(certStore, encodingX509ASN|encodingPKCS7, 0, findIssuerStr, i, prev)
		if err != nil {
			return nil, nil, fmt.Errorf("finding certificates: %v", err)
		}
		if nc == nil {
			continue
		}
		prev = nc
		if (intendedKeyUsage(encodingX509ASN, nc) & signatureKeyUsage) == 0 {
			continue
		}
		xc, err := certContextToX509(nc)
		if err != nil {
			continue
		}
		cert = xc
		break
	}
	if cert == nil {
		return nil, nil, nil
	}
	return cert, prev, nil
}
func (w *WinCertStore) Link() error {
	cert, _, err := w.cert(w.issuers, my, certStoreLocalMachine)
	if err != nil {
		return fmt.Errorf("checking for existing machine certificates returned: %v", err)
	}
	if cert == nil {
		return nil
	}
	userCert, _, err := w.cert(w.issuers, my, certStoreCurrentUser)
	if err != nil {
		return fmt.Errorf("checking for existing user certificates returned: %v", err)
	}
	if userCert != nil {
		if cert.SerialNumber.Cmp(userCert.SerialNumber) == 0 {
			fmt.Fprintf(os.Stdout, "Certificate %s is already linked to the user certificate store.\n", cert.SerialNumber)
			return nil
		}
	}
	certContext, err := windows.CertCreateCertificateContext(
		encodingX509ASN|encodingPKCS7,
		&cert.Raw[0],
		uint32(len(cert.Raw)))
	if err != nil {
		return fmt.Errorf("CertCreateCertificateContext returned: %v", err)
	}
	defer windows.CertFreeCertificateContext(certContext)
	r, _, err := cryptFindCertificateKeyProvInfo.Call(
		uintptr(unsafe.Pointer(certContext)),
		uintptr(uint32(0)),
		0,
	)
	if r == 0 {
		fmt.Printf("found a matching private key for the certificate, but association failed: %v", err)
	}
	userStore, err := windows.CertOpenStore(
		certStoreProvSystem,
		0,
		0,
		certStoreCurrentUser,
		uintptr(unsafe.Pointer(my)))
	if err != nil {
		return fmt.Errorf("CertOpenStore for the user store returned: %v", err)
	}
	defer windows.CertCloseStore(userStore, 0)
	if err := windows.CertAddCertificateContextToStore(userStore, certContext, windows.CERT_STORE_ADD_ALWAYS, nil); err != nil {
		return fmt.Errorf("CertAddCertificateContextToStore returned: %v", err)
	}
	logger.Infof("Successfully linked to existing system certificate with serial %s.", cert.SerialNumber)
	fmt.Fprintf(os.Stdout, "Successfully linked to existing system certificate with serial %s.\n", cert.SerialNumber)
	if w.ProvName == ProviderMSLegacy {
		return w.linkLegacy()
	}
	return nil
}
func (w *WinCertStore) linkLegacy() error {
	if w.ProvName != ProviderMSLegacy {
		return fmt.Errorf("cannot link legacy key, Provider mismatch: got %q, want %q", w.ProvName, ProviderMSLegacy)
	}
	logger.Info("Linking legacy key to the user private store.")
	cert, context, err := w.cert(w.issuers, my, certStoreLocalMachine)
	if err != nil {
		return fmt.Errorf("cert lookup returned: %v", err)
	}
	if context == nil {
		return errors.New("cert lookup returned: nil")
	}
	k, err := w.CertKey(context)
	if err != nil {
		return fmt.Errorf("unable to find legacy private key for %s: %v", cert.SerialNumber, err)
	}
	if k == nil {
		return errors.New("private key lookup returned: nil")
	}
	if k.LegacyContainer == "" {
		return fmt.Errorf("unable to find legacy private key for %s: container was empty", cert.SerialNumber)
	}
	sid, err := UserSID()
	if err != nil {
		return fmt.Errorf("unable to determine user SID: %v", err)
	}
	_, file := filepath.Split(k.LegacyContainer)
	userContainer := fmt.Sprintf(`%s\Microsoft\Crypto\RSA\%s\%s`, os.Getenv("AppData"), sid, file)
	if err := copyFile(k.LegacyContainer, userContainer); err != nil {
		return err
	}
	logger.Infof("Legacy key %q was located and linked to the user store.", k.LegacyContainer)
	return nil
}
func (w *WinCertStore) Remove(removeSystem bool) error {
	for _, issuer := range w.issuers {
		if err := w.remove(issuer, removeSystem); err != nil {
			return err
		}
	}
	return nil
}
func (w *WinCertStore) remove(issuer string, removeSystem bool) error {
	userStore, err := windows.CertOpenStore(
		certStoreProvSystem,
		0,
		0,
		certStoreCurrentUser,
		uintptr(unsafe.Pointer(my)))
	if err != nil {
		return fmt.Errorf("certopenstore for the user store returned: %v", err)
	}
	defer windows.CertCloseStore(userStore, 0)
	userCertContext, err := findCert(
		userStore,
		encodingX509ASN|encodingPKCS7,
		0,
		findIssuerStr,
		wide(issuer),
		nil)
	if err != nil {
		return fmt.Errorf("remove: finding user certificate issued by %s failed: %v", issuer, err)
	}
	if userCertContext != nil {
		if err := removeCert(userCertContext); err != nil {
			return fmt.Errorf("failed to remove user cert: %v", err)
		}
		logger.Info("Cleaned up a user certificate.")
		fmt.Fprintln(os.Stderr, "Cleaned up a user certificate.")
	}
	if !removeSystem {
		return nil
	}
	systemStore, err := windows.CertOpenStore(
		certStoreProvSystem,
		0,
		0,
		certStoreLocalMachine,
		uintptr(unsafe.Pointer(my)))
	if err != nil {
		return fmt.Errorf("certopenstore for the system store returned: %v", err)
	}
	defer windows.CertCloseStore(systemStore, 0)
	systemCertContext, err := findCert(
		systemStore,
		encodingX509ASN|encodingPKCS7,
		0,
		findIssuerStr,
		wide(issuer),
		nil)
	if err != nil {
		return fmt.Errorf("remove: finding system certificate issued by %s failed: %v", issuer, err)
	}
	if systemCertContext != nil {
		if err := removeCert(systemCertContext); err != nil {
			return fmt.Errorf("failed to remove system cert: %v", err)
		}
		logger.Info("Cleaned up a system certificate.")
		fmt.Fprintln(os.Stderr, "Cleaned up a system certificate.")
	}
	return nil
}
func removeCert(certContext *windows.CertContext) error {
	r, _, err := certDeleteCertificateFromStore.Call(uintptr(unsafe.Pointer(certContext)))
	if r != 1 {
		return fmt.Errorf("certdeletecertificatefromstore failed with %X: %v", r, err)
	}
	return nil
}
func (w *WinCertStore) Intermediate() (*x509.Certificate, error) {
	c, _, err := w.cert(w.intermediateIssuers, my, certStoreLocalMachine)
	return c, err
}
func (w *WinCertStore) Root(issuer []string) (*x509.Certificate, error) {
	c, _, err := w.cert(issuer, root, certStoreLocalMachine)
	return c, err
}
func (w *WinCertStore) CertificateChain() ([][]*x509.Certificate, error) {
	cert, err := w.Cert()
	if err != nil {
		return nil, fmt.Errorf("unable to load leaf: %v", err)
	}
	if cert == nil {
		return nil, fmt.Errorf("load leaf: no certificate found")
	}
	return w.certChains, nil
}
type Key struct {
	handle          uintptr
	pub             crypto.PublicKey
	Container       string
	LegacyContainer string
	AlgorithmGroup  string
}