X.509 certificates

A Public Key Infrastructure (PKI) is a framework for a collection of public keys, along with additional information such as owner name and location, and links between them giving some sort of approval mechanism.

The principal PKI in use today is based on X.509 certificates. For example, web browsers use them to verify the identity of web sites.

An example program to generate a self-signed X.509 certificate for my web site and store it in a .cer file is

  1. /* GenX509Cert
  2. */
  3. package main
  4. import (
  5. "crypto/rand"
  6. "crypto/rsa"
  7. "crypto/x509"
  8. "crypto/x509/pkix"
  9. "encoding/gob"
  10. "encoding/pem"
  11. "fmt"
  12. "math/big"
  13. "os"
  14. "time"
  15. )
  16. func main() {
  17. random := rand.Reader
  18. var key rsa.PrivateKey
  19. loadKey("private.key", &key)
  20. now := time.Now()
  21. then := now.Add(60 * 60 * 24 * 365 * 1000 * 1000 * 1000) // one year
  22. template := x509.Certificate{
  23. SerialNumber: big.NewInt(1),
  24. Subject: pkix.Name{
  25. CommonName: "jan.newmarch.name",
  26. Organization: []string{"Jan Newmarch"},
  27. },
  28. // NotBefore: time.Unix(now, 0).UTC(),
  29. // NotAfter: time.Unix(now+60*60*24*365, 0).UTC(),
  30. NotBefore: now,
  31. NotAfter: then,
  32. SubjectKeyId: []byte{1, 2, 3, 4},
  33. KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  34. BasicConstraintsValid: true,
  35. IsCA: true,
  36. DNSNames: []string{"jan.newmarch.name", "localhost"},
  37. }
  38. derBytes, err := x509.CreateCertificate(random, &template,
  39. &template, &key.PublicKey, &key)
  40. checkError(err)
  41. certCerFile, err := os.Create("jan.newmarch.name.cer")
  42. checkError(err)
  43. certCerFile.Write(derBytes)
  44. certCerFile.Close()
  45. certPEMFile, err := os.Create("jan.newmarch.name.pem")
  46. checkError(err)
  47. pem.Encode(certPEMFile, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
  48. certPEMFile.Close()
  49. keyPEMFile, err := os.Create("private.pem")
  50. checkError(err)
  51. pem.Encode(keyPEMFile, &pem.Block{Type: "RSA PRIVATE KEY",
  52. Bytes: x509.MarshalPKCS1PrivateKey(&key)})
  53. keyPEMFile.Close()
  54. }
  55. func loadKey(fileName string, key interface{}) {
  56. inFile, err := os.Open(fileName)
  57. checkError(err)
  58. decoder := gob.NewDecoder(inFile)
  59. err = decoder.Decode(key)
  60. checkError(err)
  61. inFile.Close()
  62. }
  63. func checkError(err error) {
  64. if err != nil {
  65. fmt.Println("Fatal error ", err.Error())
  66. os.Exit(1)
  67. }
  68. }

This can then be read back in by

  1. /* ReadX509Cert
  2. */
  3. package main
  4. import (
  5. "crypto/x509"
  6. "fmt"
  7. "os"
  8. )
  9. func main() {
  10. certCerFile, err := os.Open("jan.newmarch.name.cer")
  11. checkError(err)
  12. derBytes := make([]byte, 1000) // bigger than the file
  13. count, err := certCerFile.Read(derBytes)
  14. checkError(err)
  15. certCerFile.Close()
  16. // trim the bytes to actual length in call
  17. cert, err := x509.ParseCertificate(derBytes[0:count])
  18. checkError(err)
  19. fmt.Printf("Name %s\n", cert.Subject.CommonName)
  20. fmt.Printf("Not before %s\n", cert.NotBefore.String())
  21. fmt.Printf("Not after %s\n", cert.NotAfter.String())
  22. }
  23. func checkError(err error) {
  24. if err != nil {
  25. fmt.Println("Fatal error ", err.Error())
  26. os.Exit(1)
  27. }
  28. }