aboutsummaryrefslogtreecommitdiff
path: root/x509util/x509util.go
diff options
context:
space:
mode:
Diffstat (limited to 'x509util/x509util.go')
-rw-r--r--x509util/x509util.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/x509util/x509util.go b/x509util/x509util.go
new file mode 100644
index 0000000..b33b4e9
--- /dev/null
+++ b/x509util/x509util.go
@@ -0,0 +1,134 @@
+package x509util
+
+import (
+ "fmt"
+
+ "crypto/ed25519"
+ "crypto/x509"
+ "encoding/pem"
+ "io/ioutil"
+)
+
+// LoadTrustAnchors loads a list of PEM-encoded certificates from file
+func LoadTrustAnchors(path string) ([]*x509.Certificate, *x509.CertPool, error) {
+ rest, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed reading trust anchors: %v", err)
+ }
+
+ pool := x509.NewCertPool()
+ var anchors []*x509.Certificate
+ for len(rest) > 0 {
+ var block *pem.Block
+ block, rest = pem.Decode(rest)
+ if block == nil {
+ break
+ }
+ if block.Type != "CERTIFICATE" {
+ return nil, nil, fmt.Errorf("unexpected PEM block type: %s", block.Type)
+ }
+
+ certificate, err := x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ return nil, nil, fmt.Errorf("invalid trust anchor before rest(%s): %v", rest, err)
+ }
+
+ anchors = append(anchors, certificate)
+ pool.AddCert(certificate)
+ }
+
+ if len(anchors) == 0 {
+ return nil, nil, fmt.Errorf("found no valid trust anchor in: %s", path)
+ }
+ return anchors, pool, nil
+}
+
+// LoadEd25519SigningKey loads an Ed25519 private key from a given path
+func LoadEd25519SigningKey(path string) (ed25519.PrivateKey, error) {
+ data, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("failed reading private key: %v", err)
+ }
+ return ParseEd25519PrivateKey(data)
+}
+
+// ParseEd25519PrivateKey parses a PEM-encoded private key block
+func ParseEd25519PrivateKey(data []byte) (ed25519.PrivateKey, error) {
+ block, rest := pem.Decode(data)
+ if block == nil {
+ return nil, fmt.Errorf("pem block: is empty")
+ }
+ if block.Type != "PRIVATE KEY" {
+ return nil, fmt.Errorf("bad pem block type: %v", block.Type)
+ }
+ if len(rest) != 0 {
+ return nil, fmt.Errorf("pem block: trailing data")
+ }
+
+ key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
+ if err != nil {
+ fmt.Errorf("x509 parser failed: %v", err)
+ }
+ switch t := key.(type) {
+ case ed25519.PrivateKey:
+ return key.(ed25519.PrivateKey), nil
+ default:
+ return nil, fmt.Errorf("unexpected signing key type: %v", t)
+ }
+}
+
+// LoadChain loads a PEM-encoded certificate chain from a given path
+func LoadChain(path string) ([]*x509.Certificate, error) {
+ blob, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("failed reading certificate chain: %v", err)
+ }
+ return ParseChain(blob)
+}
+
+// ParseChain parses a PEM-encoded certificate chain
+func ParseChain(rest []byte) ([]*x509.Certificate, error) {
+ var chain []*x509.Certificate
+ for len(rest) > 0 {
+ var block *pem.Block
+ block, rest = pem.Decode(rest)
+ if block == nil {
+ break
+ }
+ if block.Type != "CERTIFICATE" {
+ return nil, fmt.Errorf("unexpected pem block type: %v", block.Type)
+ }
+
+ certificate, err := x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("failed parsing x509 certificate: %v", err)
+ }
+ chain = append(chain, certificate)
+ }
+ return chain, nil
+}
+
+// ParseDerChain parses a list of base64 DER-encoded X.509 certificates, such
+// that the first (zero-index) string is interpretted as an end-entity
+// certificate and the remaining ones as the an intermediate CertPool.
+func ParseDerChain(chain [][]byte) (*x509.Certificate, *x509.CertPool, error) {
+ var certificate *x509.Certificate
+ intermediatePool := x509.NewCertPool()
+ for index, der := range chain {
+ c, err := x509.ParseCertificate(der)
+ if err != nil {
+ return nil, nil, fmt.Errorf("certificate decoding failed: %v", err)
+ }
+
+ if index == 0 {
+ certificate = c
+ } else {
+ intermediatePool.AddCert(c)
+ }
+ }
+ if certificate == nil {
+ return nil, nil, fmt.Errorf("certificate chain is empty")
+ }
+ return certificate, intermediatePool, nil
+}
+