package pemfile import ( "bufio" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "io/fs" "os" "src.lwithers.me.uk/go/writefile" ) const ( TypePKCS1PrivateKey = "RSA PRIVATE KEY" TypePKCS8PrivateKey = "PRIVATE KEY" TypePKCS1PublicKey = "RSA PUBLIC KEY" TypePKIXPublicKey = "PUBLIC KEY" TypeX509Certificate = "CERTIFICATE" TypeX509CSR = "CERTIFICATE REQUEST" TypeX509CRL = "X509 CRL" ) func Write(filename, typ string, der []byte) error { finalFname, f, err := writefile.New(filename) if err != nil { return err } defer writefile.Abort(f) out := bufio.NewWriter(f) pem.Encode(out, &pem.Block{ Type: typ, Bytes: der, }) if err = out.Flush(); err != nil { return err } return writefile.Commit(finalFname, f) } func ReadKey(filename string) (*rsa.PrivateKey, error) { raw, err := os.ReadFile(filename) if err != nil { return nil, err } for { block, rest := pem.Decode(raw) raw = rest switch { case block == nil: return nil, &fs.PathError{ Op: "decode", Path: filename, Err: errors.New("no private key PEM block found"), } case block.Type == TypePKCS1PrivateKey: key, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return nil, &fs.PathError{ Op: "parse key", Path: filename, Err: err, } } return key, nil case block.Type == TypePKCS8PrivateKey: key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return nil, &fs.PathError{ Op: "parse key", Path: filename, Err: err, } } rsakey, ok := key.(*rsa.PrivateKey) if !ok { return nil, &fs.PathError{ Op: "parse key", Path: filename, Err: errors.New("not an RSA key"), } } return rsakey, nil } } } func ReadCert(filename string) (*x509.Certificate, error) { raw, err := os.ReadFile(filename) if err != nil { return nil, err } for { block, rest := pem.Decode(raw) raw = rest switch { case block == nil: return nil, &fs.PathError{ Op: "decode", Path: filename, Err: errors.New("no certificate PEM block found"), } case block.Type == TypeX509Certificate: cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, &fs.PathError{ Op: "parse cert", Path: filename, Err: err, } } return cert, nil } } } func ReadCRL(filename string) (*x509.RevocationList, error) { raw, err := os.ReadFile(filename) if err != nil { return nil, err } for { block, rest := pem.Decode(raw) raw = rest switch { case block == nil: return nil, &fs.PathError{ Op: "decode", Path: filename, Err: errors.New("no CRL PEM block found"), } case block.Type == TypeX509CRL: crl, err := x509.ParseRevocationList(block.Bytes) if err != nil { return nil, &fs.PathError{ Op: "parse cert", Path: filename, Err: err, } } return crl, nil } } } func ReadCSR(filename string) (*x509.CertificateRequest, error) { raw, err := os.ReadFile(filename) if err != nil { return nil, err } for { block, rest := pem.Decode(raw) raw = rest switch { case block == nil: return nil, &fs.PathError{ Op: "decode", Path: filename, Err: errors.New("no CSR PEM block found"), } case block.Type == TypeX509CSR: csr, err := x509.ParseCertificateRequest(block.Bytes) if err != nil { return nil, &fs.PathError{ Op: "parse CSR", Path: filename, Err: err, } } return csr, nil } } }