keyvault_certificate.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // /*
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. // */
  14. package keyvault
  15. import (
  16. "crypto/rsa"
  17. "crypto/x509"
  18. "encoding/pem"
  19. "errors"
  20. "fmt"
  21. "github.com/Azure/go-autorest/autorest"
  22. "github.com/Azure/go-autorest/autorest/adal"
  23. "github.com/Azure/go-autorest/autorest/azure"
  24. )
  25. // ClientInMemoryCertificateConfig struct includes a Certificate field to hold the certificate data as a byte slice.
  26. type ClientInMemoryCertificateConfig struct {
  27. ClientID string
  28. Certificate []byte // Certificate data as a byte slice
  29. TenantID string
  30. AuxTenants []string
  31. AADEndpoint string
  32. Resource string
  33. }
  34. func NewClientInMemoryCertificateConfig(clientID string, certificate []byte, tenantID string) ClientInMemoryCertificateConfig {
  35. return ClientInMemoryCertificateConfig{
  36. ClientID: clientID,
  37. Certificate: certificate,
  38. TenantID: tenantID,
  39. Resource: azure.PublicCloud.ResourceManagerEndpoint,
  40. AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
  41. }
  42. }
  43. // ServicePrincipalToken creates a adal.ServicePrincipalToken from client certificate using the certificate byte slice.
  44. func (ccc ClientInMemoryCertificateConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
  45. oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID)
  46. if err != nil {
  47. return nil, err
  48. }
  49. // Use the byte slice directly instead of reading from a file
  50. certificate, rsaPrivateKey, err := loadCertificateFromBytes(ccc.Certificate)
  51. if err != nil {
  52. return nil, fmt.Errorf("failed to decode certificate: %w", err)
  53. }
  54. return adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
  55. }
  56. func loadCertificateFromBytes(certificateBytes []byte) (*x509.Certificate, *rsa.PrivateKey, error) {
  57. var cert *x509.Certificate
  58. var privateKey *rsa.PrivateKey
  59. var err error
  60. // Extract certificate and private key
  61. for {
  62. block, rest := pem.Decode(certificateBytes)
  63. if block == nil {
  64. break
  65. }
  66. if block.Type == "CERTIFICATE" {
  67. cert, err = x509.ParseCertificate(block.Bytes)
  68. if err != nil {
  69. return nil, nil, fmt.Errorf("failed to parse PEM certificate: %w", err)
  70. }
  71. } else {
  72. privateKey, err = parsePrivateKey(block.Bytes)
  73. if err != nil {
  74. return nil, nil, fmt.Errorf("failed to extract private key from PEM certificate: %w", err)
  75. }
  76. }
  77. certificateBytes = rest
  78. }
  79. if cert == nil {
  80. return nil, nil, errors.New("no certificate found in PEM file")
  81. }
  82. if privateKey == nil {
  83. return nil, nil, errors.New("no private key found in PEM file")
  84. }
  85. return cert, privateKey, nil
  86. }
  87. func parsePrivateKey(der []byte) (*rsa.PrivateKey, error) {
  88. if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
  89. return key, nil
  90. }
  91. if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
  92. switch key := key.(type) {
  93. case *rsa.PrivateKey:
  94. return key, nil
  95. default:
  96. return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
  97. }
  98. }
  99. return nil, errors.New("failed to parse private key")
  100. }
  101. // Implementation of the AuthorizerConfig interface.
  102. func (ccc ClientInMemoryCertificateConfig) Authorizer() (autorest.Authorizer, error) {
  103. spToken, err := ccc.ServicePrincipalToken()
  104. if err != nil {
  105. return nil, fmt.Errorf("failed to get oauth token from certificate auth: %w", err)
  106. }
  107. return autorest.NewBearerAuthorizer(spToken), nil
  108. }