package main import ( "bytes" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "fmt" "log" "net/http" "strings" ) type CTLogRootsResponse struct { Certificates []string `json:"certificates"` } func generateRoots(args []string) { sourceURL := "https://rennet2027h2.log.ct.ipng.ch/" outputFile := "roots.pem" // Parse command line arguments for i := 0; i < len(args); i++ { switch args[i] { case "--source": if i+1 >= len(args) { log.Fatal("--source flag requires a URL argument") } sourceURL = args[i+1] i++ // Skip the next argument since we used it case "--output": if i+1 >= len(args) { log.Fatal("--output flag requires a filename argument") } outputFile = args[i+1] i++ // Skip the next argument since we used it default: log.Fatalf("Unknown argument: %s", args[i]) } } // Ensure source URL ends with / if !strings.HasSuffix(sourceURL, "/") { sourceURL += "/" } // Construct the get-roots URL getRootsURL := sourceURL + "ct/v1/get-roots" // Fetch roots from CT log fmt.Printf("Fetching roots from: %s\n", getRootsURL) resp, err := http.Get(getRootsURL) if err != nil { log.Fatalf("Failed to fetch roots: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { log.Fatalf("HTTP request failed with status: %d", resp.StatusCode) } // Parse JSON response var rootsResp CTLogRootsResponse err = json.NewDecoder(resp.Body).Decode(&rootsResp) if err != nil { log.Fatalf("Failed to parse JSON response: %v", err) } // Collect all valid certificates in a buffer var pemBuffer bytes.Buffer validCertCount := 0 for _, certBase64 := range rootsResp.Certificates { // Decode base64 certificate certBytes, err := base64.StdEncoding.DecodeString(certBase64) if err != nil { log.Fatalf("Failed to decode certificate: %v", err) } // Parse X.509 certificate to check serial number cert, err := x509.ParseCertificate(certBytes) if err != nil { log.Printf("Warning: Failed to parse certificate, skipping: %v", err) continue } // Check for negative serial number if cert.SerialNumber.Sign() < 0 { log.Printf("Warning: Certificate with negative serial number found, skipping (serial: %s)", cert.SerialNumber.String()) continue } // Create PEM block pemBlock := &pem.Block{ Type: "CERTIFICATE", Bytes: certBytes, } // Write PEM to buffer err = pem.Encode(&pemBuffer, pemBlock) if err != nil { log.Fatalf("Failed to encode PEM certificate: %v", err) } validCertCount++ } // Write all certificates to file with status err = writeFileWithStatus(outputFile, pemBuffer.Bytes()) if err != nil { log.Fatalf("Failed to write output file %s: %v", outputFile, err) } fmt.Printf("Successfully wrote %d certificates to %s (out of %d total)\n", validCertCount, outputFile, len(rootsResp.Certificates)) }