ACMEv2 client library for Go.

keyauth.go 2.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. package acmeutils
  2. import (
  3. "crypto"
  4. "crypto/ecdsa"
  5. "crypto/elliptic"
  6. "crypto/rand"
  7. "crypto/sha256"
  8. "crypto/x509"
  9. "crypto/x509/pkix"
  10. "encoding/base64"
  11. "encoding/hex"
  12. "gopkg.in/square/go-jose.v1"
  13. "math/big"
  14. "time"
  15. )
  16. // Calculates the base64 thumbprint of a public or private key. Returns an
  17. // error if the key is of an unknown type.
  18. func Base64Thumbprint(key interface{}) (string, error) {
  19. k := jose.JsonWebKey{Key: key}
  20. thumbprint, err := k.Thumbprint(crypto.SHA256)
  21. if err != nil {
  22. return "", err
  23. }
  24. return base64.RawURLEncoding.EncodeToString(thumbprint), nil
  25. }
  26. // Calculates a key authorization using the given account public or private key
  27. // and the token to prefix.
  28. func KeyAuthorization(accountKey interface{}, token string) (string, error) {
  29. thumbprint, err := Base64Thumbprint(accountKey)
  30. if err != nil {
  31. return "", err
  32. }
  33. return token + "." + thumbprint, nil
  34. }
  35. // Calculates a key authorization which is then hashed and base64 encoded as is
  36. // required for the DNS challenge.
  37. func DNSKeyAuthorization(accountKey interface{}, token string) (string, error) {
  38. ka, err := KeyAuthorization(accountKey, token)
  39. if err != nil {
  40. return "", err
  41. }
  42. return base64.RawURLEncoding.EncodeToString(sha256Bytes([]byte(ka))), nil
  43. }
  44. // Determines the hostname which must appear in a TLS-SNI challenge
  45. // certificate.
  46. func TLSSNIHostname(accountKey interface{}, token string) (string, error) {
  47. ka, err := KeyAuthorization(accountKey, token)
  48. if err != nil {
  49. return "", err
  50. }
  51. kaHex := sha256BytesHex([]byte(ka))
  52. return kaHex[0:32] + "." + kaHex[32:64] + ".acme.invalid", nil
  53. }
  54. // Creates a self-signed certificate and matching private key suitable for
  55. // responding to a TLS-SNI challenge. hostname should be a hostname returned by
  56. // TLSSNIHostname.
  57. func CreateTLSSNICertificate(hostname string) (certDER []byte, privateKey crypto.PrivateKey, err error) {
  58. crt := x509.Certificate{
  59. Subject: pkix.Name{
  60. CommonName: hostname,
  61. },
  62. Issuer: pkix.Name{
  63. CommonName: hostname,
  64. },
  65. SerialNumber: big.NewInt(1),
  66. NotBefore: time.Now().Add(-24 * time.Hour),
  67. NotAfter: time.Now().Add(365 * 24 * time.Hour),
  68. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  69. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  70. BasicConstraintsValid: true,
  71. DNSNames: []string{hostname},
  72. }
  73. pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  74. if err != nil {
  75. return
  76. }
  77. certDER, err = x509.CreateCertificate(rand.Reader, &crt, &crt, &pk.PublicKey, pk)
  78. privateKey = pk
  79. return
  80. }
  81. func sha256Bytes(b []byte) []byte {
  82. h := sha256.New()
  83. h.Write(b)
  84. return h.Sum(nil)
  85. }
  86. func sha256BytesHex(b []byte) string {
  87. return hex.EncodeToString(sha256Bytes(b))
  88. }