I have the following program that prints out information about each certificate in a .pem
file:
package main
import (
"crypto/x509"
"encoding/pem"
"io/ioutil"
"log"
"os"
"strconv"
)
func main() {
//for dev purposes set to 256
const SignatureLength int = 256
certPEMBlock, err := ioutil.ReadFile("testsign.crt")
if err != nil {
log.Fatal(err)
}
var blocks [][]byte
for {
var certDERBlock *pem.Block
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
blocks = append(blocks, certDERBlock.Bytes)
}
} //end for
//OPEN FILE TO APPEND CERT INFORMATION INTO
f, err := os.OpenFile("appendMe.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
for _, block := range blocks {
cert, err := x509.ParseCertificate(block)
if err != nil {
log.Println(err)
continue
}
//APPEND CERT INFO TO FILE
//VERSION
if _, err := f.Write([]byte(strconv.Itoa(cert.Version))); err != nil {
log.Fatal(err)
}
//KEY ID
if _, err := f.Write(cert.SubjectKeyId); err != nil {
log.Fatal(err)
}
//SIGNATURE LENGTH
if _, err := f.Write([]byte(strconv.Itoa(SignatureLength))); err != nil {
log.Fatal(err)
}
//COMMON NAME
if _, err := f.Write([]byte(cert.Subject.CommonName)); err != nil {
log.Fatal(err)
}
} //end for
//CLOSE THE FILE
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
it works, but notice that each line separately writes to the file. That seems a bit wasteful, but I am not clear about options in Go...do I create an array...do I slice it from the cert...another for loop...
QUESTION
Instead of f.Write() on each line, what is the proper or alternative approach in Go?
Instead of using the cert.*, should I be capturing that data into a struct or array?
Follow up
let's say the last write or any write fails, does Go roll back changes or does last write not get written to the file? This needs to be an all or nothing write.
(this is me learning as I do, thanks for your help)
The easiest way to write to a buffer in memory is using the bytes
package, which provides a Buffer
type. Since Buffer
implements the Writer
interface, you can update your Write
calls to use the Buffer
instead, and then use the Buffer.WriteTo
method to write the accumulated data out to the file. Here's what that looks like, with the loops combined also:
package main
import (
"bytes"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"log"
"os"
"strconv"
)
func main() {
//for dev purposes set to 256
const SignatureLength int = 256
certPEMBlock, err := ioutil.ReadFile("testsign.crt")
if err != nil {
log.Fatal(err)
}
//OPEN FILE TO APPEND CERT INFORMATION INTO
f, err := os.OpenFile("appendMe.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
var buf bytes.Buffer
for {
var certDERBlock *pem.Block
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(certDERBlock.Bytes)
if err != nil {
log.Println(err)
continue
}
//APPEND CERT INFO TO FILE
//VERSION
if _, err := buf.Write([]byte(strconv.Itoa(cert.Version))); err != nil {
log.Fatal(err)
}
//KEY ID
if _, err := buf.Write(cert.SubjectKeyId); err != nil {
log.Fatal(err)
}
//SIGNATURE LENGTH
if _, err := buf.Write([]byte(strconv.Itoa(SignatureLength))); err != nil {
log.Fatal(err)
}
//COMMON NAME
if _, err := buf.Write([]byte(cert.Subject.CommonName)); err != nil {
log.Fatal(err)
}
}
} //end for
// write data accumulated in buf out to f
buf.WriteTo(f)
//CLOSE THE FILE
if err := f.Close(); err != nil {
log.Fatal(err)
}
}