keyvault_certificate.go 4.0 KB

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