github.com/annwntech/go-micro/v2@v2.9.5/util/pki/pki.go (about) 1 // Package pki provides PKI all the PKI functions necessary to run micro over an untrusted network 2 // including a CA 3 package pki 4 5 import ( 6 "bytes" 7 "crypto/ed25519" 8 "crypto/rand" 9 "crypto/x509" 10 "encoding/pem" 11 12 "github.com/pkg/errors" 13 ) 14 15 // GenerateKey returns an ed25519 key 16 func GenerateKey() (ed25519.PublicKey, ed25519.PrivateKey, error) { 17 return ed25519.GenerateKey(rand.Reader) 18 } 19 20 // CA generates a self signed CA and returns cert, key in PEM format 21 func CA(opts ...CertOption) ([]byte, []byte, error) { 22 opts = append(opts, IsCA()) 23 options := CertOptions{} 24 for _, o := range opts { 25 o(&options) 26 } 27 template := &x509.Certificate{ 28 SignatureAlgorithm: x509.PureEd25519, 29 Subject: options.Subject, 30 DNSNames: options.DNSNames, 31 IPAddresses: options.IPAddresses, 32 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 33 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 34 NotBefore: options.NotBefore, 35 NotAfter: options.NotAfter, 36 SerialNumber: options.SerialNumber, 37 BasicConstraintsValid: true, 38 } 39 if options.IsCA { 40 template.IsCA = true 41 template.KeyUsage |= x509.KeyUsageCertSign 42 } 43 x509Cert, err := x509.CreateCertificate(rand.Reader, template, template, options.Pub, options.Priv) 44 if err != nil { 45 return nil, nil, err 46 } 47 cert, key := &bytes.Buffer{}, &bytes.Buffer{} 48 if err := pem.Encode(cert, &pem.Block{Type: "CERTIFICATE", Bytes: x509Cert}); err != nil { 49 return nil, nil, err 50 } 51 x509Key, err := x509.MarshalPKCS8PrivateKey(options.Priv) 52 if err != nil { 53 return nil, nil, err 54 } 55 if err := pem.Encode(key, &pem.Block{Type: "PRIVATE KEY", Bytes: x509Key}); err != nil { 56 return nil, nil, err 57 } 58 59 return cert.Bytes(), key.Bytes(), nil 60 } 61 62 // CSR generates a certificate request in PEM format 63 func CSR(opts ...CertOption) ([]byte, error) { 64 options := CertOptions{} 65 for _, o := range opts { 66 o(&options) 67 } 68 csrTemplate := &x509.CertificateRequest{ 69 Subject: options.Subject, 70 SignatureAlgorithm: x509.PureEd25519, 71 DNSNames: options.DNSNames, 72 IPAddresses: options.IPAddresses, 73 } 74 out := &bytes.Buffer{} 75 csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, options.Priv) 76 if err != nil { 77 return nil, err 78 } 79 if err := pem.Encode(out, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csr}); err != nil { 80 return nil, err 81 } 82 83 return out.Bytes(), nil 84 } 85 86 // Sign decodes a CSR and signs it with the CA 87 func Sign(CACrt, CAKey, CSR []byte, opts ...CertOption) ([]byte, error) { 88 options := CertOptions{} 89 for _, o := range opts { 90 o(&options) 91 } 92 asn1CACrt, err := decodePEM(CACrt) 93 if err != nil { 94 return nil, errors.Wrap(err, "failed to decode CA Crt PEM") 95 } 96 if len(asn1CACrt) != 1 { 97 return nil, errors.Errorf("expected 1 CA Crt, got %d", len(asn1CACrt)) 98 } 99 caCrt, err := x509.ParseCertificate(asn1CACrt[0].Bytes) 100 if err != nil { 101 return nil, errors.Wrap(err, "ca is not a valid certificate") 102 } 103 asn1CAKey, err := decodePEM(CAKey) 104 if err != nil { 105 return nil, errors.Wrap(err, "failed to decode CA Key PEM") 106 } 107 if len(asn1CAKey) != 1 { 108 return nil, errors.Errorf("expected 1 CA Key, got %d", len(asn1CACrt)) 109 } 110 caKey, err := x509.ParsePKCS8PrivateKey(asn1CAKey[0].Bytes) 111 if err != nil { 112 return nil, errors.Wrap(err, "ca key is not a valid private key") 113 } 114 asn1CSR, err := decodePEM(CSR) 115 if err != nil { 116 return nil, errors.Wrap(err, "failed to decode CSR PEM") 117 } 118 if len(asn1CSR) != 1 { 119 return nil, errors.Errorf("expected 1 CSR, got %d", len(asn1CSR)) 120 } 121 csr, err := x509.ParseCertificateRequest(asn1CSR[0].Bytes) 122 if err != nil { 123 return nil, errors.Wrap(err, "csr is invalid") 124 } 125 template := &x509.Certificate{ 126 SignatureAlgorithm: x509.PureEd25519, 127 Subject: csr.Subject, 128 DNSNames: csr.DNSNames, 129 IPAddresses: csr.IPAddresses, 130 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 131 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 132 NotBefore: options.NotBefore, 133 NotAfter: options.NotAfter, 134 SerialNumber: options.SerialNumber, 135 BasicConstraintsValid: true, 136 } 137 138 x509Cert, err := x509.CreateCertificate(rand.Reader, template, caCrt, caCrt.PublicKey, caKey) 139 if err != nil { 140 return nil, errors.Wrap(err, "Couldn't sign certificate") 141 } 142 out := &bytes.Buffer{} 143 if err := pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: x509Cert}); err != nil { 144 return nil, errors.Wrap(err, "couldn't encode cert") 145 } 146 return out.Bytes(), nil 147 } 148 149 func decodePEM(PEM []byte) ([]*pem.Block, error) { 150 var blocks []*pem.Block 151 var asn1 *pem.Block 152 var rest []byte 153 for { 154 asn1, rest = pem.Decode(PEM) 155 if asn1 == nil { 156 return nil, errors.New("PEM is not valid") 157 } 158 blocks = append(blocks, asn1) 159 if len(rest) == 0 { 160 break 161 } 162 } 163 return blocks, nil 164 }