Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions certtostore_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"reflect"
"strings"
"sync"
"syscall"
"time"
"unicode/utf16"
"unsafe"
Expand Down Expand Up @@ -86,6 +87,9 @@ type WinCertStorage interface {
// or a link to a matching certificate already exists in the store
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certaddcertificatecontexttostore
StoreWithDisposition(cert *x509.Certificate, intermediate *x509.Certificate, disposition uint32) error

// SetContainer updates the key container name used by Key() and Generate().
SetContainer(container string)
}

const (
Expand Down Expand Up @@ -580,7 +584,7 @@ func (w *WinCertStore) CertWithContext() (*x509.Certificate, *windows.CertContex
return c, ctx, nil
}

// cert is a helper function to lookup certificates based on a known issuer.
// cert is a helper function to look up certificates based on a known issuer.
// store is used to specify which store to perform the lookup in (system or user).
func (w *WinCertStore) cert(issuers []string, searchRoot *uint16, store uint32) (*x509.Certificate, *windows.CertContext, error) {
h, err := w.storeHandle(store, searchRoot)
Expand Down Expand Up @@ -1241,6 +1245,13 @@ func setACL(file, access, sid, perm string) error {
return nil
}

// SetContainer updates the key container name used by Key() and Generate().
func (w *WinCertStore) SetContainer(container string) {
w.mu.Lock()
defer w.mu.Unlock()
w.container = container
}

// Key opens a handle to an existing private key and returns key.
// Key implements both crypto.Signer and crypto.Decrypter.
//
Expand All @@ -1249,15 +1260,19 @@ func setACL(file, access, sid, perm string) error {
// with a different provider. Use CertKey() to derive a key directly from a Cert in situations
// where both are needed.
func (w *WinCertStore) Key() (Credential, error) {
w.mu.Lock()
container := w.container
w.mu.Unlock()

var kh uintptr
r, _, err := nCryptOpenKey.Call(
uintptr(w.Prov),
uintptr(unsafe.Pointer(&kh)),
uintptr(unsafe.Pointer(wide(w.container))),
uintptr(unsafe.Pointer(wide(container))),
0,
w.keyAccessFlags)
if r != 0 {
return nil, fmt.Errorf("NCryptOpenKey for container %q returned %X: %v", w.container, r, err)
return nil, fmt.Errorf("NCryptOpenKey for container %q returned %X: %v", container, r, err)
}

return keyMetadata(kh, w)
Expand Down Expand Up @@ -1289,6 +1304,9 @@ func (w *WinCertStore) CertKey(cert *windows.CertContext) (*Key, error) {
)
// If the function succeeds, the return value is nonzero (TRUE).
if r == 0 {
if errno, ok := err.(syscall.Errno); ok {
return nil, fmt.Errorf("cryptAcquireCertificatePrivateKey returned %X: %w", uint32(errno), err)
}
return nil, fmt.Errorf("cryptAcquireCertificatePrivateKey returned %X: %v", r, err)
}
if mustFree != 0 {
Expand Down Expand Up @@ -1332,14 +1350,18 @@ func (w *WinCertStore) Generate(opts GenerateOpts) (crypto.Signer, error) {
}

func (w *WinCertStore) generateECDSA(algID string) (crypto.Signer, error) {
w.mu.Lock()
container := w.container
w.mu.Unlock()

var kh uintptr
// Pass 0 as the fifth parameter because it is not used (legacy)
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa376247(v=vs.85).aspx
r, _, err := nCryptCreatePersistedKey.Call(
uintptr(w.Prov),
uintptr(unsafe.Pointer(&kh)),
uintptr(unsafe.Pointer(wide(algID))),
uintptr(unsafe.Pointer(wide(w.container))),
uintptr(unsafe.Pointer(wide(container))),
0,
w.keyAccessFlags)
if r != 0 {
Expand Down Expand Up @@ -1375,6 +1397,10 @@ func (w *WinCertStore) generateRSA(keySize int) (crypto.Signer, error) {
return nil, fmt.Errorf("unsupported keysize, got: %d, want: < %d", keySize, 16384)
}

w.mu.Lock()
container := w.container
w.mu.Unlock()

var kh uintptr
length := uint32(keySize)
// Pass 0 as the fifth parameter because it is not used (legacy)
Expand All @@ -1383,7 +1409,7 @@ func (w *WinCertStore) generateRSA(keySize int) (crypto.Signer, error) {
uintptr(w.Prov),
uintptr(unsafe.Pointer(&kh)),
uintptr(unsafe.Pointer(wide("RSA"))),
uintptr(unsafe.Pointer(wide(w.container))),
uintptr(unsafe.Pointer(wide(container))),
0,
w.keyAccessFlags)
if r != 0 {
Expand Down Expand Up @@ -1784,7 +1810,7 @@ func copyFile(from, to string) error {
}

// softwareKeyContainers returns the file path for a software backed key. If the key
// was finalized with with NCRYPT_WRITE_KEY_TO_LEGACY_STORE_FLAG, it also returns its
// was finalized with NCRYPT_WRITE_KEY_TO_LEGACY_STORE_FLAG, it also returns its
// equivalent CryptoAPI key file path.
// https://docs.microsoft.com/en-us/windows/win32/api/ncrypt/nf-ncrypt-ncryptfinalizekey.
func softwareKeyContainers(uniqueID string, storeDomain uint32) (string, string, error) {
Expand Down
14 changes: 14 additions & 0 deletions certtostore_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -856,3 +856,17 @@ func TestSetACL_NonRegularFile(t *testing.T) {
t.Errorf("setACL unexpected error string: got %q, want containing 'is not a regular file'", err)
}
}

func TestSetContainer(t *testing.T) {
store, err := OpenWinCertStoreCurrentUser(ProviderMSSoftware, "initial-container", nil, nil, false)
if err != nil {
t.Fatalf("OpenWinCertStoreCurrentUser() unexpected error: %v", err)
}
defer store.Close()

const want = "updated-container"
store.SetContainer(want)
if store.container != want {
t.Errorf("store.SetContainer(%q): store.container = %q, want %q", want, store.container, want)
}
}
Loading