generated from deploymenttheory/Template
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: Update certificate authentication options
Update the `client_certificate_base64` and `client_certificate_file_path` attributes in the `M365Provider` schema to support both PEM and PKCS#12 certificate formats. The changes include: - Updating the description to clarify the supported formats and usage - Adding support for encrypted PKCS#12 certificates with the `client_certificate_password` attribute These updates enhance the flexibility and security of certificate authentication in the M365 provider.
- Loading branch information
Showing
4 changed files
with
185 additions
and
138 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,119 @@ | ||
package helpers | ||
|
||
import ( | ||
"context" | ||
"crypto" | ||
"crypto/rsa" | ||
"crypto/x509" | ||
"encoding/base64" | ||
"encoding/pem" | ||
"errors" | ||
"io" | ||
"os" | ||
|
||
pkcs12 "software.sslmate.com/src/go-pkcs12" | ||
"github.com/hashicorp/terraform-plugin-log/tflog" | ||
"golang.org/x/crypto/pkcs12" | ||
) | ||
|
||
// GetCertificatesAndKeyFromCertOrFilePath takes either a base64-encoded certificate or a file path to a PKCS#12 file, | ||
// decodes it, and returns the certificates and private key. | ||
func GetCertificatesAndKeyFromCertOrFilePath(certOrFilePath string, password string) ([]*x509.Certificate, interface{}, error) { | ||
certData, err := base64.StdEncoding.DecodeString(certOrFilePath) | ||
if err == nil { | ||
key, cert, err := pkcs12.Decode(certData, password) | ||
if err == nil { | ||
return []*x509.Certificate{cert}, key, nil | ||
// ParseCertificateData reads and parses the certificate data, extracting the certificate and private key. | ||
// It first tries to parse the data as PEM. If that fails, it assumes PKCS#12 format and tries to decode it. | ||
func ParseCertificateData(ctx context.Context, certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) { | ||
var certs []*x509.Certificate | ||
var key crypto.PrivateKey | ||
var err error | ||
var certType string | ||
|
||
// Try to parse as PEM | ||
blocks := []*pem.Block{} | ||
for { | ||
var block *pem.Block | ||
block, certData = pem.Decode(certData) | ||
if block == nil { | ||
break | ||
} | ||
blocks = append(blocks, block) | ||
} | ||
|
||
file, err := os.Open(certOrFilePath) | ||
if err != nil { | ||
return nil, nil, errors.New("could not open file or decode base64 input") | ||
for _, block := range blocks { | ||
switch block.Type { | ||
case "CERTIFICATE": | ||
cert, err := x509.ParseCertificate(block.Bytes) | ||
if err != nil { | ||
tflog.Error(ctx, "Failed to parse PEM certificate", map[string]interface{}{ | ||
"error": err, | ||
}) | ||
return nil, nil, err | ||
} | ||
certs = append(certs, cert) | ||
certType = "PEM" | ||
case "ENCRYPTED PRIVATE KEY": | ||
decryptedKey, err := x509.DecryptPEMBlock(block, password) | ||
if err != nil { | ||
tflog.Error(ctx, "Failed to decrypt PEM private key", map[string]interface{}{ | ||
"error": err, | ||
}) | ||
return nil, nil, err | ||
} | ||
key, err = x509.ParsePKCS8PrivateKey(decryptedKey) | ||
if err != nil { | ||
tflog.Error(ctx, "Failed to parse decrypted PEM private key", map[string]interface{}{ | ||
"error": err, | ||
}) | ||
return nil, nil, err | ||
} | ||
case "PRIVATE KEY": | ||
key, err = x509.ParsePKCS8PrivateKey(block.Bytes) | ||
if err != nil { | ||
key, err = x509.ParsePKCS1PrivateKey(block.Bytes) | ||
} | ||
if err != nil { | ||
tflog.Error(ctx, "Failed to parse PEM private key", map[string]interface{}{ | ||
"error": err, | ||
}) | ||
return nil, nil, err | ||
} | ||
case "RSA PRIVATE KEY": | ||
key, err = x509.ParsePKCS1PrivateKey(block.Bytes) | ||
if err != nil { | ||
tflog.Error(ctx, "Failed to parse PEM RSA private key", map[string]interface{}{ | ||
"error": err, | ||
}) | ||
return nil, nil, err | ||
} | ||
} | ||
} | ||
defer file.Close() | ||
|
||
pfxData, err := io.ReadAll(file) | ||
if err != nil { | ||
return nil, nil, errors.New("could not read file content") | ||
} | ||
// If PEM parsing failed, try to decode as PKCS#12 | ||
if len(certs) == 0 || key == nil { | ||
tflog.Debug(ctx, "Attempting to parse as PKCS#12") | ||
privateKey, certificate, err := pkcs12.Decode(certData, string(password)) | ||
if err != nil { | ||
tflog.Error(ctx, "Failed to parse PKCS#12 data", map[string]interface{}{ | ||
"error": err, | ||
}) | ||
return nil, nil, err | ||
} | ||
|
||
key, cert, err := pkcs12.Decode(pfxData, password) | ||
if err != nil { | ||
return nil, nil, err | ||
certs = append(certs, certificate) | ||
key = privateKey | ||
certType = "PKCS#12" | ||
} | ||
|
||
return []*x509.Certificate{cert}, key, nil | ||
} | ||
|
||
// ConvertBase64ToCert takes a base64 encoded PKCS#12 file, decodes it, and returns the certificate. | ||
func ConvertBase64ToCert(base64PfxData string, password string) (*x509.Certificate, error) { | ||
pfxData, err := base64.StdEncoding.DecodeString(base64PfxData) | ||
if err != nil { | ||
return nil, err | ||
if len(certs) == 0 { | ||
tflog.Error(ctx, "No certificates found") | ||
return nil, nil, errors.New("no certificates found") | ||
} | ||
if key == nil { | ||
tflog.Error(ctx, "No private key found") | ||
return nil, nil, errors.New("no private key found") | ||
} | ||
|
||
_, cert, err := pkcs12.Decode(pfxData, password) | ||
if err != nil { | ||
return nil, err | ||
// Check that the private key is of the expected RSA type | ||
if _, ok := key.(*rsa.PrivateKey); !ok { | ||
tflog.Error(ctx, "Private key is not of RSA type") | ||
return nil, nil, errors.New("private key is not of RSA type") | ||
} | ||
|
||
return cert, nil | ||
tflog.Info(ctx, "Certificate and private key parsed successfully", map[string]interface{}{ | ||
"certificateType": certType, | ||
}) | ||
|
||
return certs, key, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.