0

我在 Windows 上使用 Go 1.6 并尝试将证书容器导出到 PFX(这里的最终目标是从证书存储访问可导出的私钥)。

我打开了一个内存存储并将证书插入到存储中:

var storedCertCtx *syscall.CertContext
storeHandle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
err = syscall.CertAddCertificateContextToStore(storeHandle, certenum, syscall.CERT_STORE_ADD_ALWAYS, &storedCertCtx)

现在我想生成该商店的 PFX。我已经定义了一个包含数据 blob的结构,并希望使用PFXExportCertStoreEx来获取存储的 PFX:

var (
    crypt32                  = syscall.NewLazyDLL("crypt32.dll")
    procPFXExportCertStoreEx = crypt32.NewProc("PFXExportCertStoreEx")
)

type CRYPTOAPI_BLOB struct {
    DataSize uint32
    Data     *byte
}

var pfxBlob CRYPTOAPI_BLOB
err = PfxExportCertStore(storeHandle, &pfxBlob, syscall.StringToUTF16Ptr("MyPassword"), 0, 0)

syscall.Syscall6(procPFXExportCertStoreEx.Addr(), 5,
        uintptr(storeHandle),                //hStore
        uintptr(unsafe.Pointer(&pfxBlob)),   //*pPFX
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("password"))), //szPassword
        0,   //*pvPara
        0,   //dwFlags
        0)

一半有效。

DataSize填充了看起来合适的值(即,如果我向商店添加更多证书,它会变得更大),Data总是. <nil>

看到它应该用指针填充,我尝试将它声明为*uintptrand uint32(只是看看是否填充了任何东西),但什么也没有。该值始终保持不变(如果我手动将垃圾数据放在那里,垃圾数据会在系统调用执行后保留)。

我是否错误地定义了结构?在 Go 中完成这项工作的示例很少,但从我从众多 C 示例中可以看出,这应该是可行的。

4

2 回答 2

3

这是预期的行为。

根据这个:https://msdn.microsoft.com/en-us/library/windows/desktop/aa387313(v=vs.85).aspx,该pPFX结构需要一个预先分配的缓冲区,大小在cbData字段中,它将使用复制的数据的大小进行更新。

如果使用pbDataequal 进行调用NULL,则仅cbData更新字段以反映输出缓冲区所需的大小。

于 2016-02-25T18:19:50.420 回答
0

JimB 的回答肯定是正确的,但我想添加这个以防万一其他人走上这条路。我必须用来获取 PFX 文件的实际代码CRYPTOAPI_BLOB是:

var (
    crypt32                  = syscall.NewLazyDLL("crypt32.dll")
    procPFXExportCertStoreEx = crypt32.NewProc("PFXExportCertStoreEx")
    procCryptMemAlloc        = crypt32.NewProc("CryptMemAlloc")
    procCryptMemFree         = crypt32.NewProc("CryptMemFree")
)

type CRYPTOAPI_BLOB struct {
    cbData uint32
    pbData *byte
}

func (b *CRYPTOAPI_BLOB) ToByteArray() []byte {
    d := make([]byte, b.cbData)
    copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
    return d
}

func PfxExportCertStore(storeHandle syscall.Handle, password string, flags uint32) (returnData []byte, err error) {

    var pfxBlob CRYPTOAPI_BLOB

    r1, _, _ := syscall.Syscall6(procPFXExportCertStoreEx.Addr(), 5,
        uintptr(storeHandle),                                        //hStore
        uintptr(unsafe.Pointer(&pfxBlob)),                           //*pPFX
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(password))), //szPassword
        0,              //*pvPara
        uintptr(flags), //dwFlags
        0)

    r2, _, _ := syscall.Syscall(procCryptMemAlloc.Addr(), 1, uintptr(unsafe.Pointer(&pfxBlob.cbData)), 0, 0)

    p := unsafe.Pointer(&r2)
    q := (*byte)(p)
    pfxBlob.pbData = q
    defer syscall.Syscall(procCryptMemFree.Addr(), 1, uintptr(unsafe.Pointer(pfxBlob.pbData)), 0, 0)

    r3, _, _ := syscall.Syscall6(procPFXExportCertStoreEx.Addr(), 5,
        uintptr(storeHandle),                                        //hStore
        uintptr(unsafe.Pointer(&pfxBlob)),                           //*pPFX
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(password))), //szPassword
        0,              //*pvPara
        uintptr(flags), //dwFlags
        0)


    returnData = pfxBlob.ToByteArray()
    return
}

(我已经剥离了错误处理以使其更易于阅读)。第一次调用PFXExportCertStoreEx只返回大小,一旦我们有了大小,我们就可以调用来PFXExportCertStoreEx分配缓冲区,然后我们将相同的指针传递给PFXExportCertStoreEx,但这一次它有分配的缓冲区,我们得到完整的 PFX文件返回。

于 2016-02-26T13:03:02.513 回答