【问题标题】:Calling PFXExportCertStoreEx in Go does not return data在 Go 中调用 PFXExportCertStoreEx 不会返回数据
【发布时间】:2016-02-25 17:22:59
【问题描述】:

我在 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。我已经定义了一个包含data 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>

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

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

【问题讨论】:

  • “空白”是指pfxBlob.Data == 0,还是指Data点为空的内存块?
  • @JimB 如果我执行fmt.Println(pfxBlob.Data),我会得到<nil>。我还刚刚注意到我粘贴到代码 sn-p 中的结构来自以前的修订版,我将更新为最新版本。
  • 有没有试过检查调用后的返回值和错误?
  • 从快速的 google 看来,这是预期的行为:msdn.microsoft.com/en-us/library/windows/desktop/… - 从阅读看来,您需要预先分配缓冲区并将大小放入 cbData。如果pbData 为 nil,则调用仅返回所需的大小。
  • @JimB GetLastError() 不返回任何内容,系统调用的第一个返回表示成功。

标签: go system-calls x509 certificate-store


【解决方案1】:

这是预期的行为。

据此:https://msdn.microsoft.com/en-us/library/windows/desktop/aa387313(v=vs.85).aspxpPFX 结构需要一个预先分配的缓冲区,大小在cbData 字段中,它将随着复制进来的数据的大小而更新。

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

【讨论】:

    【解决方案2】:

    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 文件。

    【讨论】:

    猜你喜欢
    • 2021-11-17
    • 1970-01-01
    • 2022-12-21
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 2013-05-26
    • 2022-10-08
    • 1970-01-01
    相关资源
    最近更新 更多