When working with TLS certificates in Go applications, you might encounter some unexpected challenges. In this post, we’ll explore a common issue that occurs when dealing with base64-encoded certificates and how to resolve it.

The Problem

Consider a scenario where you’re building a secure web server in Go that needs to use TLS certificates. You’re retrieving these certificates from a secrets management system, but when you try to start the server, you get this cryptic error:

Error loading server certificate and key: tls: failed to find any PEM data in certificate input

This error occurs because the certificates are being provided in base64-encoded format, but Go’s TLS configuration expects them in PEM format. Let’s look at how to fix this.

The Initial Code

Here’s what the problematic code might look like:

cert, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
if err != nil {
    log.Fatalf("Error loading server certificate and key: %v", err)
}

tlsConfig := &tls.Config{
    Certificates: []tls.Certificate{cert},
}

This code assumes the certificate and key are in PEM format, but what if they’re base64-encoded? Let’s look at how to modify this to handle base64-encoded certificates.

The Solution

The solution involves adding a decoding step before creating the X509 key pair. Here’s the complete code with the fix:

package main

import (
    "crypto/tls"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strings"
)

func main() {
    // ... (certificate fetching code)

    // Decode base64 encoded certificate and key
    decodedCert, err := base64.StdEncoding.DecodeString(serverCert)
    if err != nil {
        log.Fatalf("Error decoding server certificate: %v", err)
    }

    decodedKey, err := base64.StdEncoding.DecodeString(serverKey)
    if err != nil {
        log.Fatalf("Error decoding server key: %v", err)
    }

    // Configure TLS with decoded certificate and key
    cert, err := tls.X509KeyPair(decodedCert, decodedKey)
    if err != nil {
        log.Fatalf("Error loading server certificate and key: %v", err)
    }

    tlsConfig := &tls.Config{
        Certificates: []tls.Certificate{cert},
    }

    // Set up the HTTP server
    server := &http.Server{
        Addr:      ":8443",
        TLSConfig: tlsConfig,
    }

    fmt.Println("Server is running on :8443 with TLS enabled")
    log.Fatal(server.ListenAndServeTLS("", ""))
}

Understanding the Fix

The key changes in this solution are:

  1. Import the base64 package: We need to add encoding/base64 to our imports to handle the decoding.

  2. Decode the certificates: Before creating the X509 key pair, we decode both the certificate and key:

    decodedCert, err := base64.StdEncoding.DecodeString(serverCert)
    decodedKey, err := base64.StdEncoding.DecodeString(serverKey)
    
  3. Use decoded data: We use the decoded data to create the X509 key pair instead of the raw base64-encoded strings.

How to Identify Base64-Encoded Certificates

Base64-encoded certificates typically have these characteristics:

  • They contain only alphanumeric characters, plus ‘+’, ‘/’ and ‘=’
  • They don’t have the familiar “BEGIN CERTIFICATE” and “END CERTIFICATE” markers
  • They’re usually one long string without line breaks

In contrast, PEM-formatted certificates look like this:

-----BEGIN CERTIFICATE-----
MIIDQTCCAimgAwIBAgIUOL6XR5PRHNs...
-----END CERTIFICATE-----

Best Practices

When working with TLS certificates in Go:

  1. Always validate certificate format: Check whether your certificates are base64-encoded or PEM-formatted before processing them.

  2. Handle errors gracefully: Include proper error handling for both decoding and certificate loading steps.

  3. Log meaningful messages: Include detailed error messages to make debugging easier.

  4. Consider automation: Use tools or scripts to handle certificate format conversion if you frequently deal with different formats.

Conclusion

Dealing with TLS certificates in Go can be tricky, especially when they’re not in the expected format. By understanding that certificates might be base64-encoded and knowing how to handle them, you can avoid common pitfalls and build more robust applications.

Remember to always check the format of your certificates when debugging TLS-related issues. The error “failed to find any PEM data in certificate input” is often a good indicator that you’re dealing with encoded certificates that need to be decoded first.

Happy coding!