当签署证书时,将授权密钥标识符复制到SKID。

huangapple go评论112阅读模式
英文:

When signing a certificate, Authority Key Identifier is copied to SKID

问题

我正在尝试使用CSR和spacemonkeygo/openssl包来签发证书。

控制台中的openssl命令用于签发证书,结果符合预期,我得到了有效的证书。

  1. openssl x509 -req -days 365 -in cert_client.csr -CA ca/root.crt -CAkey ca/root.key -set_serial 10101 -out cert_client.crt -extfile ca/extensions.cnf

从屏幕截图中可以看出,SKID和颁发者的keyid是不同的。

然而,我的Go代码提供了一个错误的证书,其中SKID包含了颁发证书的keyid的确切值。这导致在“Authority Key Identifier”中将无效的值复制给“issuer”:因为SKID与颁发者的KeyId相同,它“认为”证书是自签发的。

  1. package main
  2. import (
  3. "github.com/spacemonkeygo/openssl"
  4. "math/big"
  5. "os"
  6. "time"
  7. )
  8. func main() {
  9. crtFilePath := FilePath("ca/root.crt")
  10. keyFilePath := FilePath("ca/root.key")
  11. certCA, privateKeyCA, err := getRootCA(PathCert(crtFilePath), PathKey(keyFilePath))
  12. if err != nil {
  13. panic(err)
  14. }
  15. serialNumber := big.NewInt(10101)
  16. country := "RU"
  17. organization := "Some Organization"
  18. commonName := "CommonName"
  19. expirationDate := time.Now().AddDate(1, 0, 0)
  20. certInfo := &openssl.CertificateInfo{
  21. Serial: serialNumber,
  22. Expires: expirationDate.Sub(time.Now()),
  23. CommonName: commonName,
  24. // will fail if these are empty or not initialized
  25. Country: country,
  26. Organization: organization,
  27. }
  28. // just for example. PublicKey is received from CSR
  29. privateKeyCert, err := openssl.GenerateRSAKey(2048)
  30. if err != nil {
  31. panic(err)
  32. }
  33. newCert, err := openssl.NewCertificate(certInfo, openssl.PublicKey(privateKeyCert))
  34. if err != nil {
  35. panic(err)
  36. }
  37. err = newCert.SetVersion(openssl.X509_V3)
  38. if err != nil {
  39. panic(err)
  40. }
  41. // (?) must be called before adding extensions
  42. err = newCert.SetIssuer(certCA)
  43. if err != nil {
  44. panic(err)
  45. }
  46. err = newCert.AddExtension(openssl.NID_basic_constraints,
  47. "critical,CA:FALSE")
  48. if err != nil {
  49. panic(err)
  50. }
  51. err = newCert.AddExtension(openssl.NID_subject_key_identifier,
  52. "hash")
  53. if err != nil {
  54. panic(err)
  55. }
  56. err = newCert.AddExtension(openssl.NID_authority_key_identifier,
  57. "keyid:always,issuer:always")
  58. if err != nil {
  59. panic(err)
  60. }
  61. err = newCert.Sign(privateKeyCA, openssl.EVP_SHA256)
  62. if err != nil {
  63. panic(err)
  64. }
  65. pemBytes, err := newCert.MarshalPEM()
  66. if err != nil {
  67. panic(err)
  68. }
  69. err = os.WriteFile("generated.crt", pemBytes, os.FileMode(0644))
  70. if err != nil {
  71. panic(err)
  72. }
  73. print("Done")
  74. }
  75. type FilePath string
  76. type PathCert string
  77. type PathKey string
  78. func getRootCA(pathCert PathCert, pathKey PathKey) (*openssl.Certificate, openssl.PrivateKey, error) {
  79. caPublicKeyFile, err := os.ReadFile(string(pathCert))
  80. if err != nil {
  81. return nil, nil, err
  82. }
  83. certCA, err := openssl.LoadCertificateFromPEM(caPublicKeyFile)
  84. if err != nil {
  85. return nil, nil, err
  86. }
  87. caPrivateKeyFile, err := os.ReadFile(string(pathKey))
  88. if err != nil {
  89. return nil, nil, err
  90. }
  91. privateKeyCA, err := openssl.LoadPrivateKeyFromPEM(caPrivateKeyFile)
  92. if err != nil {
  93. return nil, nil, err
  94. }
  95. return certCA, privateKeyCA, nil
  96. }

(生成的是正确的)

如果我不调用SetIssuer,SKID会被新生成,但生成的证书仍然被显示为“无效”。

在代码中我做错了什么?


更新:
我比较了两个包的扩展添加实现:spacemonkey/goPyOpenSSL

Go:

  1. // Add an extension to a certificate.
  2. // Extension constants are NID_* as found in openssl.
  3. func (c *Certificate) AddExtension(nid NID, value string) error {
  4. issuer := c
  5. if c.Issuer != nil {
  6. issuer = c.Issuer
  7. }
  8. var ctx C.X509V3_CTX
  9. C.X509V3_set_ctx(&ctx, c.x, issuer.x, nil, nil, 0)
  10. ex := C.X509V3_EXT_conf_nid(nil, &ctx, C.int(nid), C.CString(value))
  11. if ex == nil {
  12. return errors.New("failed to create x509v3 extension")
  13. }
  14. defer C.X509_EXTENSION_free(ex)
  15. if C.X509_add_ext(c.x, ex, -1) <= 0 {
  16. return errors.New("failed to add x509v3 extension")
  17. }
  18. return nil
  19. }

Python(省略了一些注释):

  1. # X509Extension::__init__
  2. def __init__(
  3. self,
  4. type_name: bytes,
  5. critical: bool,
  6. value: bytes,
  7. subject: Optional["X509"] = None,
  8. issuer: Optional["X509"] = None,
  9. ) -> None:
  10. ctx = _ffi.new("X509V3_CTX*")
  11. # A context is necessary for any extension which uses the r2i
  12. # conversion method. That is, X509V3_EXT_nconf may segfault if passed
  13. # a NULL ctx. Start off by initializing most of the fields to NULL.
  14. _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
  15. # We have no configuration database - but perhaps we should (some
  16. # extensions may require it).
  17. _lib.X509V3_set_ctx_nodb(ctx)
  18. # Initialize the subject and issuer, if appropriate. ctx is a local,
  19. # and as far as I can tell none of the X509V3_* APIs invoked here steal
  20. # any references, so no need to mess with reference counts or
  21. # duplicates.
  22. if issuer is not None:
  23. if not isinstance(issuer, X509):
  24. raise TypeError("issuer must be an X509 instance")
  25. ctx.issuer_cert = issuer._x509
  26. if subject is not None:
  27. if not isinstance(subject, X509):
  28. raise TypeError("subject must be an X509 instance")
  29. ctx.subject_cert = subject._x509
  30. if critical:
  31. # There are other OpenSSL APIs which would let us pass in critical
  32. # separately, but they're harder to use, and since value is already
  33. # a pile of crappy junk smuggling a ton of utterly important
  34. # structured data, what's the point of trying to avoid nasty stuff
  35. # with strings? (However, X509V3_EXT_i2d in particular seems like
  36. # it would be a better API to invoke. I do not know where to get
  37. # the ext_struc it desires for its last parameter, though.)
  38. value = b"critical," + value
  39. extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
  40. if extension == _ffi.NULL:
  41. _raise_current_error()
  42. self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)

明显的区别在于API:Python版本接受subjectissuer作为参数进行重载,而Go版本则不接受。

实现上的区别如下:

  • Go中调用了X509V3_EXT_conf_nid
  • Python中调用了X509V3_EXT_nconf

这两个函数都可以在GitHub上找到。

**我猜在使用openspacemonkey/go-openssl签名CA时,无法添加SKID扩展。**似乎唯一的方法是手动使用C绑定并“按照Python的方式”进行操作。

英文:

I am trying to sign a certificate with CSR and spacemonkeygo/openssl wrapper.
The console openssl command to sign a certificate works as expected and I get a valid certificate.

  1. openssl x509 -req -days 365 -in cert_client.csr -CA ca/root.crt -CAkey ca/root.key -set_serial 10101 -out cert_client.crt -extfile ca/extensions.cnf

当签署证书时,将授权密钥标识符复制到SKID。
As can be seen from the sceenshot, SKID and Issuer's keyid are different.

However, my code in Go provides an erroneous certificate, where SKID contains the exact value of Issuing certificate's keyid. It results in copying invalid values for "issuer" in "Authority Key Identifier": since SKID is the same as KeyId of the Issuer, it "considers" the certificate to be self-issued.

  1. package main
  2. import (
  3. &quot;github.com/spacemonkeygo/openssl&quot;
  4. &quot;math/big&quot;
  5. &quot;os&quot;
  6. &quot;time&quot;
  7. )
  8. func main() {
  9. crtFilePath := FilePath(&quot;ca/root.crt&quot;)
  10. keyFilePath := FilePath(&quot;ca/root.key&quot;)
  11. certCA, privateKeyCA, err := getRootCA(PathCert(crtFilePath), PathKey(keyFilePath))
  12. if err != nil {
  13. panic(err)
  14. }
  15. serialNumber := big.NewInt(10101)
  16. country := &quot;RU&quot;
  17. organization := &quot;Some Organization&quot;
  18. commonName := &quot;CommonName&quot;
  19. expirationDate := time.Now().AddDate(1, 0, 0)
  20. certInfo := &amp;openssl.CertificateInfo{
  21. Serial: serialNumber,
  22. Expires: expirationDate.Sub(time.Now()),
  23. CommonName: commonName,
  24. // will fail if these are empty or not initialized
  25. Country: country,
  26. Organization: organization,
  27. }
  28. // just for example. PublicKey is received from CSR
  29. privateKeyCert, err := openssl.GenerateRSAKey(2048)
  30. if err != nil {
  31. panic(err)
  32. }
  33. newCert, err := openssl.NewCertificate(certInfo, openssl.PublicKey(privateKeyCert))
  34. if err != nil {
  35. panic(err)
  36. }
  37. err = newCert.SetVersion(openssl.X509_V3)
  38. if err != nil {
  39. panic(err)
  40. }
  41. // (?) must be called before adding extensions
  42. err = newCert.SetIssuer(certCA)
  43. if err != nil {
  44. panic(err)
  45. }
  46. err = newCert.AddExtension(openssl.NID_basic_constraints,
  47. &quot;critical,CA:FALSE&quot;)
  48. if err != nil {
  49. panic(err)
  50. }
  51. err = newCert.AddExtension(openssl.NID_subject_key_identifier,
  52. &quot;hash&quot;)
  53. if err != nil {
  54. panic(err)
  55. }
  56. err = newCert.AddExtension(openssl.NID_authority_key_identifier,
  57. &quot;keyid:always,issuer:always&quot;)
  58. if err != nil {
  59. panic(err)
  60. }
  61. err = newCert.Sign(privateKeyCA, openssl.EVP_SHA256)
  62. if err != nil {
  63. panic(err)
  64. }
  65. pemBytes, err := newCert.MarshalPEM()
  66. if err != nil {
  67. panic(err)
  68. }
  69. err = os.WriteFile(&quot;generated.crt&quot;, pemBytes, os.FileMode(0644))
  70. if err != nil {
  71. panic(err)
  72. }
  73. print(&quot;Done&quot;)
  74. }
  75. type FilePath string
  76. type PathCert string
  77. type PathKey string
  78. func getRootCA(pathCert PathCert, pathKey PathKey) (*openssl.Certificate, openssl.PrivateKey, error) {
  79. caPublicKeyFile, err := os.ReadFile(string(pathCert))
  80. if err != nil {
  81. return nil, nil, err
  82. }
  83. certCA, err := openssl.LoadCertificateFromPEM(caPublicKeyFile)
  84. if err != nil {
  85. return nil, nil, err
  86. }
  87. caPrivateKeyFile, err := os.ReadFile(string(pathKey))
  88. if err != nil {
  89. return nil, nil, err
  90. }
  91. privateKeyCA, err := openssl.LoadPrivateKeyFromPEM(caPrivateKeyFile)
  92. if err != nil {
  93. return nil, nil, err
  94. }
  95. return certCA, privateKeyCA, nil
  96. }

当签署证书时,将授权密钥标识符复制到SKID。
(Generated is the right one)

If I don't invoke SetIssuer, SKID is newly generated, but the resulting certificate is still shown as "invalid".

What am I doing wrong in the code?


UPDATE:
I have compared implementations for adding extensions for 2 wrappers: spacemonkey/go and PyOpenSSL.

Go:

  1. // Add an extension to a certificate.
  2. // Extension constants are NID_* as found in openssl.
  3. func (c *Certificate) AddExtension(nid NID, value string) error {
  4. issuer := c
  5. if c.Issuer != nil {
  6. issuer = c.Issuer
  7. }
  8. var ctx C.X509V3_CTX
  9. C.X509V3_set_ctx(&amp;ctx, c.x, issuer.x, nil, nil, 0)
  10. ex := C.X509V3_EXT_conf_nid(nil, &amp;ctx, C.int(nid), C.CString(value))
  11. if ex == nil {
  12. return errors.New(&quot;failed to create x509v3 extension&quot;)
  13. }
  14. defer C.X509_EXTENSION_free(ex)
  15. if C.X509_add_ext(c.x, ex, -1) &lt;= 0 {
  16. return errors.New(&quot;failed to add x509v3 extension&quot;)
  17. }
  18. return nil
  19. }

Python (omitted some comments):

  1. # X509Extension::__init__
  2. def __init__(
  3. self,
  4. type_name: bytes,
  5. critical: bool,
  6. value: bytes,
  7. subject: Optional[&quot;X509&quot;] = None,
  8. issuer: Optional[&quot;X509&quot;] = None,
  9. ) -&gt; None:
  10. ctx = _ffi.new(&quot;X509V3_CTX*&quot;)
  11. # A context is necessary for any extension which uses the r2i
  12. # conversion method. That is, X509V3_EXT_nconf may segfault if passed
  13. # a NULL ctx. Start off by initializing most of the fields to NULL.
  14. _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0)
  15. # We have no configuration database - but perhaps we should (some
  16. # extensions may require it).
  17. _lib.X509V3_set_ctx_nodb(ctx)
  18. # Initialize the subject and issuer, if appropriate. ctx is a local,
  19. # and as far as I can tell none of the X509V3_* APIs invoked here steal
  20. # any references, so no need to mess with reference counts or
  21. # duplicates.
  22. if issuer is not None:
  23. if not isinstance(issuer, X509):
  24. raise TypeError(&quot;issuer must be an X509 instance&quot;)
  25. ctx.issuer_cert = issuer._x509
  26. if subject is not None:
  27. if not isinstance(subject, X509):
  28. raise TypeError(&quot;subject must be an X509 instance&quot;)
  29. ctx.subject_cert = subject._x509
  30. if critical:
  31. # There are other OpenSSL APIs which would let us pass in critical
  32. # separately, but they&#39;re harder to use, and since value is already
  33. # a pile of crappy junk smuggling a ton of utterly important
  34. # structured data, what&#39;s the point of trying to avoid nasty stuff
  35. # with strings? (However, X509V3_EXT_i2d in particular seems like
  36. # it would be a better API to invoke. I do not know where to get
  37. # the ext_struc it desires for its last parameter, though.)
  38. value = b&quot;critical,&quot; + value
  39. extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value)
  40. if extension == _ffi.NULL:
  41. _raise_current_error()
  42. self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free)

The obvious difference is in API: python's version accepts subject and issuer for overloading as arguments. Go's version does not.
Difference in implemenmtation is the following:

  • X509V3_EXT_nconf called in Python
  • X509V3_EXT_conf_nid called in Go
    Both of the functions can be found on github.

I guess it is impossible to add SKID extension when signing with CA using openspacemonkey/go-openssl.
It seems that the only way would be to use C bindings manually and "do as Python does".

答案1

得分: 0

我已经实现了一个笨拙的解决方法来添加SKID和authorityKeyIdentifier。生成的证书是有效的。然而,由于x *C.X509结构体的Certificate成员是未导出的,唯一访问它们的方式是通过不安全指针和类型转换。

这不是推荐的方式,但在spacemonkey/go更新之前是一种可行的方式(我怀疑更新会很快发生)。

  1. func addAuthorityKeyIdentifier(c *openssl.Certificate) error {
  2. var ctx C.X509V3_CTX
  3. C.X509V3_set_ctx(&ctx, nil, nil, nil, nil, 0)
  4. // 这很丑陋且非常不安全!
  5. cx509 := *(**C.X509)(unsafe.Pointer(c))
  6. cx509Issuer := cx509
  7. if c.Issuer != nil {
  8. cx509Issuer = *(**C.X509)(unsafe.Pointer(c.Issuer))
  9. }
  10. ctx.issuer_cert = cx509Issuer
  11. cExtName := C.CString("authorityKeyIdentifier")
  12. defer C.free(unsafe.Pointer(cExtName))
  13. cExtValue := C.CString("keyid:always,issuer:always")
  14. defer C.free(unsafe.Pointer(cExtValue))
  15. extension := C.X509V3_EXT_nconf(nil, &ctx, cExtName, cExtValue)
  16. if extension == nil {
  17. return errors.New("failed to set 'authorityKeyIdentifier' extension")
  18. }
  19. defer C.X509_EXTENSION_free(extension)
  20. addResult := C.X509_add_ext(cx509, extension, -1)
  21. if addResult == 0 {
  22. return errors.New("failed to set 'authorityKeyIdentifier' extension")
  23. }
  24. return nil
  25. }
  26. func addSKIDExtension(c *openssl.Certificate) error {
  27. var ctx C.X509V3_CTX
  28. C.X509V3_set_ctx(&ctx, nil, nil, nil, nil, 0)
  29. // 这很丑陋且非常不安全!
  30. cx509 := *(**C.X509)(unsafe.Pointer(c))
  31. _ = cx509
  32. ctx.subject_cert = cx509
  33. _ = ctx
  34. cExtName := C.CString("subjectKeyIdentifier")
  35. defer C.free(unsafe.Pointer(cExtName))
  36. cExtValue := C.CString("hash")
  37. defer C.free(unsafe.Pointer(cExtValue))
  38. extension := C.X509V3_EXT_nconf(nil, &ctx, cExtName, cExtValue)
  39. if extension == nil {
  40. return errors.New("failed to set 'subjectKeyIdentifier' extension")
  41. }
  42. defer C.X509_EXTENSION_free(extension)
  43. // 将自身添加为主题
  44. addResult := C.X509_add_ext(cx509, extension, -1)
  45. if addResult == 0 {
  46. return errors.New("failed to set 'subjectKeyIdentifier' extension")
  47. }
  48. return nil
  49. }

希望对你有帮助!

英文:

I've implemented a hacky workaround to add SKID and authorityKeyIdentifier. The resulting certificate is valid. However, since x *C.X509 members of Certificate struct are unexported, the only way to access them is via unsafe pointers and casting.
This is not a recommended way, but a way to go until spacemonkey/go is updated (I doubt it will happen soon).

  1. func addAuthorityKeyIdentifier(c *openssl.Certificate) error {
  2. var ctx C.X509V3_CTX
  3. C.X509V3_set_ctx(&amp;ctx, nil, nil, nil, nil, 0)
  4. // this is ugly and very unsafe!
  5. cx509 := *(**C.X509)(unsafe.Pointer(c))
  6. cx509Issuer := cx509
  7. if c.Issuer != nil {
  8. cx509Issuer = *(**C.X509)(unsafe.Pointer(c.Issuer))
  9. }
  10. ctx.issuer_cert = cx509Issuer
  11. cExtName := C.CString(&quot;authorityKeyIdentifier&quot;)
  12. defer C.free(unsafe.Pointer(cExtName))
  13. cExtValue := C.CString(&quot;keyid:always,issuer:always&quot;)
  14. defer C.free(unsafe.Pointer(cExtValue))
  15. extension := C.X509V3_EXT_nconf(nil, &amp;ctx, cExtName, cExtValue)
  16. if extension == nil {
  17. return errors.New(&quot;failed to set &#39;authorityKeyIdentifier&#39; extension&quot;)
  18. }
  19. defer C.X509_EXTENSION_free(extension)
  20. addResult := C.X509_add_ext(cx509, extension, -1)
  21. if addResult == 0 {
  22. return errors.New(&quot;failed to set &#39;authorityKeyIdentifier&#39; extension&quot;)
  23. }
  24. return nil
  25. }
  26. func addSKIDExtension(c *openssl.Certificate) error {
  27. var ctx C.X509V3_CTX
  28. C.X509V3_set_ctx(&amp;ctx, nil, nil, nil, nil, 0)
  29. // this is ugly and very unsafe!
  30. cx509 := *(**C.X509)(unsafe.Pointer(c))
  31. _ = cx509
  32. ctx.subject_cert = cx509
  33. _ = ctx
  34. cExtName := C.CString(&quot;subjectKeyIdentifier&quot;)
  35. defer C.free(unsafe.Pointer(cExtName))
  36. cExtValue := C.CString(&quot;hash&quot;)
  37. defer C.free(unsafe.Pointer(cExtValue))
  38. extension := C.X509V3_EXT_nconf(nil, &amp;ctx, cExtName, cExtValue)
  39. if extension == nil {
  40. return errors.New(&quot;failed to set &#39;subjectKeyIdentifier&#39; extension&quot;)
  41. }
  42. defer C.X509_EXTENSION_free(extension)
  43. // adding itself as a subject
  44. addResult := C.X509_add_ext(cx509, extension, -1)
  45. if addResult == 0 {
  46. return errors.New(&quot;failed to set &#39;subjectKeyIdentifier&#39; extension&quot;)
  47. }
  48. return nil
  49. }

huangapple
  • 本文由 发表于 2022年11月28日 19:12:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/74599727.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定