kopia/kopia · error

unable to initialize client cert credential

Error message

unable to initialize client cert credential

What it means

getAZService wraps azidentity.NewClientCertificateCredential, which builds a service-principal credential from the parsed certificate and key. This error means the credential object could not be constructed even though the certificate parsed — typically an invalid key, mismatched cert/key pair, or invalid tenant/client IDs.

Solutions

  1. Ensure the private key is decrypted (no passphrase) and matches the certificate (compare moduli)
  2. Confirm the certificate is uploaded to the service principal in Azure AD (az ad app credential reset with --cert)
  3. Validate TenantID and ClientID are correct GUIDs for the principal
  4. Re-export cert+key together with openssl and re-parse before retrying
  5. Use az login --service-principal --certificate as an out-of-band check

Example fix

// before
openssl pkcs12 -in cert.pfx -nocerts -out key.pem // encrypted key
// after
openssl pkcs12 -in cert.pfx -nocerts -nodes -out key.pem // decrypted key
// then verify match:
// openssl x509 -noout -modulus -in cert.pem | openssl md5
// openssl rsa  -noout -modulus -in key.pem  | openssl md5
Defensive patterns

Strategy: validation

Validate before calling

func validateCertCredential(tenantID, clientID, certPEM string) error {
    if _, err := uuid.Parse(strings.TrimSpace(tenantID)); err != nil {
        return fmt.Errorf("tenant id invalid: %w", err)
    }
    if _, err := uuid.Parse(strings.TrimSpace(clientID)); err != nil {
        return fmt.Errorf("client id invalid: %w", err)
    }
    // key must be unencrypted and match the cert
    if strings.Contains(certPEM, "ENCRYPTED") {
        return errors.New("private key must be decrypted (no passphrase)")
    }
    return nil
}

Prevention

When it happens

Trigger: After ParseCertificates succeeds, NewClientCertificateCredential fails due to a malformed private key, cert/key mismatch, empty or malformed TenantID/ClientID, or invalid option values.

Common situations: Private key encrypted with a passphrase (unparseable without password); certificate and key from different principals; client certificate not uploaded/registered on the Azure AD app; GUID fields containing typos.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/7d04084f662c8700. Report an issue: GitHub.

Appendix: source

Thrown at repo/blob/azure/azure_storage.go:462

		)
	// client secret
	case opt.TenantID != "" && opt.ClientID != "" && opt.ClientSecret != "":
		cred, err := azidentity.NewClientSecretCredential(opt.TenantID, opt.ClientID, opt.ClientSecret, nil)
		if err != nil {
			return nil, errors.Wrap(err, "unable to initialize client secret credential")
		}

		service, serviceErr = azblob.NewClient(fmt.Sprintf("%s://%s/", protocol, storageHostname), cred, clientOptions)
	// client certificate
	case opt.TenantID != "" && opt.ClientID != "" && opt.ClientCertificate != "":
		certs, key, certErr := azidentity.ParseCertificates([]byte(opt.ClientCertificate), nil)
		if certErr != nil {
			return nil, errors.Wrap(certErr, "failed to read client cert")
		}

		cred, credErr := azidentity.NewClientCertificateCredential(opt.TenantID, opt.ClientID, certs, key, nil)
		if credErr != nil {
			return nil, errors.Wrap(credErr, "unable to initialize client cert credential")
		}

		service, serviceErr = azblob.NewClient(fmt.Sprintf("%s://%s/", protocol, storageHostname), cred, clientOptions)
	// Azure Federated Token
	case opt.TenantID != "" && opt.ClientID != "" && opt.AzureFederatedTokenFile != "":
		cred, err := azidentity.NewWorkloadIdentityCredential(&azidentity.WorkloadIdentityCredentialOptions{
			ClientID:      opt.ClientID,
			TenantID:      opt.TenantID,
			TokenFilePath: opt.AzureFederatedTokenFile,
		})
		if err != nil {
			return nil, errors.Wrap(err, "unable to initialize Azure Federated Identity workload identity credential")
		}

		service, serviceErr = azblob.NewClient(fmt.Sprintf("%s://%s/", protocol, storageHostname), cred, clientOptions)
	default:
		return nil, errors.New("one of the storage key, SAS token, client secret, client certificate, or Azure Federated Token file must be provided")
	}

View on GitHub (pinned to 82495e54b5)