You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

certs.go 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. // Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package mkcerts
  4. import (
  5. "crypto/rand"
  6. "crypto/rsa"
  7. "crypto/x509"
  8. "crypto/x509/pkix"
  9. "encoding/pem"
  10. "fmt"
  11. "math/big"
  12. "net"
  13. "os"
  14. "time"
  15. )
  16. // CreateCertBytes creates a testing ECDSA certificate, returning the cert and key bytes.
  17. func CreateCertBytes(orgName string, host string) (certBytes []byte, keyBytes []byte, err error) {
  18. validFrom := time.Now()
  19. validFor := 365 * 24 * time.Hour
  20. notAfter := validFrom.Add(validFor)
  21. priv, err := rsa.GenerateKey(rand.Reader, 2048)
  22. if err != nil {
  23. return
  24. }
  25. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  26. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  27. if err != nil {
  28. return nil, nil, fmt.Errorf("failed to generate serial number: %s", err)
  29. }
  30. template := x509.Certificate{
  31. SerialNumber: serialNumber,
  32. Subject: pkix.Name{
  33. Organization: []string{orgName},
  34. },
  35. NotBefore: validFrom,
  36. NotAfter: notAfter,
  37. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  38. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  39. BasicConstraintsValid: true,
  40. }
  41. // TODO: allow explicitly listing allowed addresses/names
  42. template.IPAddresses = append(template.IPAddresses, net.ParseIP("127.0.0.1"))
  43. template.IPAddresses = append(template.IPAddresses, net.ParseIP("::1"))
  44. if host != "" {
  45. template.DNSNames = append(template.DNSNames, host)
  46. }
  47. template.DNSNames = append(template.DNSNames, "localhost")
  48. derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
  49. if err != nil {
  50. return nil, nil, fmt.Errorf("Failed to create certificate: %s", err.Error())
  51. }
  52. certBytes = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
  53. b, err := x509.MarshalPKCS8PrivateKey(priv)
  54. if err != nil {
  55. return nil, nil, fmt.Errorf("Unable to marshal private key: %v", err.Error())
  56. }
  57. pemBlock := pem.Block{Type: "PRIVATE KEY", Bytes: b}
  58. keyBytes = pem.EncodeToMemory(&pemBlock)
  59. return certBytes, keyBytes, nil
  60. }
  61. // CreateCert creates a testing ECDSA certificate, outputting the cert and key at the given filenames.
  62. func CreateCert(orgName string, host string, certFilename string, keyFilename string) error {
  63. certBytes, keyBytes, err := CreateCertBytes(orgName, host)
  64. if err != nil {
  65. return err
  66. }
  67. certOut, err := os.Create(certFilename)
  68. if err != nil {
  69. return fmt.Errorf("failed to open %s for writing: %s", certFilename, err.Error())
  70. }
  71. defer certOut.Close()
  72. _, err = certOut.Write(certBytes)
  73. if err != nil {
  74. return fmt.Errorf("failed to write out cert file %s: %s", certFilename, err.Error())
  75. }
  76. keyOut, err := os.OpenFile(keyFilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
  77. if err != nil {
  78. return fmt.Errorf("failed to open %s for writing: %s", keyFilename, err.Error())
  79. }
  80. defer keyOut.Close()
  81. _, err = keyOut.Write(keyBytes)
  82. if err != nil {
  83. return fmt.Errorf("failed to write out key file %s: %s", keyFilename, err.Error())
  84. }
  85. return nil
  86. }