Giter Club home page Giter Club logo

goca's Introduction

Go Certificate Authority management package

Go Report Card Build Status Go Reference Docker Pulls

GocA provides a Certificate Authority (CA) framework managing, a Simple PKI.

GoCA is a framework that uses mainly crypto/x509 to manage Certificate Authorities.

Using GoCA makes it easy to create a CA and issue certificates, signing Certificates Signing Request (CSR), and revoke certificate generating Certificates Request List (CRL).

Content:

GoCA Package

go get github.com/kairoaraujo/goca

All files are store in the $CAPATH. The $CAPATH is an environment variable that defines where all files (keys, certificates, etc.) are stored. It is essential to have this folder in a safe place.

$CPATH structure:

$CPATH
├── <CA Common Name>
    ├── ca
    │   ├── <CA Common Name>.crl
    │   ├── <CA Common Name>.crt
    │   ├── key.pem
    │   └── key.pub
    └── certs
        └── <Certificate Common Name>
            ├── <Certificate Common Name>.crt
            ├── <Certificate Common Name>.csr
            ├── key.pem
            └── key.pub

GoCA also make it easier to manipulate files such as Private and Public Keys, Certificate Signing Request, Certificate Request Lists, and Certificates for other Go applications.

This example shows

  1. Creating a Certificate Authority (Root) or Loading if it already exists
  2. Issue a new Certificate
  3. Shows the certificate
// Define the GOCAPTH (Default is current dir)
os.Setenv("CAPATH", "/opt/GoCA/CA")

// RootCAIdentity for creation
rootCAIdentity := goca.Identity{
    Organization:       "GO CA Root Company Inc.",
    OrganizationalUnit: "Certificates Management",
    Country:            "NL",
    Locality:           "Noord-Brabant",
    Province:           "Veldhoven",
    Intermediate:       false,
}

// (1) Create the New Root CA or loads existent from disk ($CAPATH)
RootCA, err := goca.New("mycompany.com", rootCAIdentity)
if err != nil {
    // Loads in case it exists
    fmt.Println("Loading CA")
    RootCA, err = goca.Load("gocaroot.nl")
    if err != nil {
        log.Fatal(err)
    }

    // Check the CA status and shows the CA Certificate
    fmt.Println(RootCA.Status())
    fmt.Println(RootCA.GetCertificate())

} else {
    log.Fatal(err)
}

// (2) Issue certificate for example intranet server
intranetIdentity := goca.Identity{
    Organization:       "Intranet Company Inc.",
    OrganizationalUnit: "Global Intranet",
    Country:            "NL",
    Locality:           "Noord-Brabant",
    Province:           "Veldhoven",
    Intermediate:       false,
    DNSNames:           []string{"w3.intranet.example.com", "www.intranet.example.com"},
}

intranetCert, err := RootCA.IssueCertificate("intranet.example.com", intranetIdentity)
if err != nil {
    log.Fatal(err)
}

// (3) Shows the Certificate (string)
fmt.Println(intranetCert.GetCertificate())

// Shows all CA Certificates
fmt.Println(RootCA.ListCertificates())

GoCA HTTP REST API

GoCA also provides an implementation using HTTP REST API.

This is available in rest-api folder.

GoCA Docker Container

GoCA Docker ready to use HTTP Rest API that uses mainly crypto/x509 to manage Certificate Authorities and Certificates such as a simple PKI Service.

The API Documentation is online available at http://kairoaraujo.github.io/goca/.

More details in Docker README.

GoCA Docker Image is available at https://hub.docker.com/r/kairoaraujo/goca/

Contributing

See CONTRIBUTING.

goca's People

Contributors

dinghram avatar jacobalberty avatar kairoaraujo avatar kebugcheckex avatar necheffa avatar zenkiebear avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

goca's Issues

Intermediate CA cert is not recognized as a certificate authority

Here's roughly what I did

  1. Create a root CA
  2. Create an intermediate CA by setting intermediate: true. Now the ICA is pending certificate
  3. Use the root CA to sign the intermediate CA's CSR. This step actually populates the ICA's directory automatically so no uploading is needed.

However, when I try to import both the root CA and the intermediate CA into my browser, the browser says the intermediate CA is not a certificate authority.

So I checked the intermediate CA certificate, using openssl x509 intermediate-ca.crt -noout -text, I don't see the following line

X509v3 Basic Constraints: critical
                CA:TRUE

I also inspected the code. It seems that when intermediate is set to true, it creates a CSR. In the CreateCSR function, nothing is setting the IsCA field.

Is this a bug or did I do something wrong?

Thanks.

enhance linter make recipe

Currently, the Makefile passes -e gosec to golangci-lint. However, this should be -E gosec or --enable gosec, as the lower-case e option is for excluding identified issues by regex.

Also consider enabling the errorlint, gofmt, gomnd, and unconvert linters.

excessive extention usage on CA certificates

When GoCA generates a certificate authority (either root or intermediate) the TLS Web Client Authentication and TLS Web Server Authentication extensions are set. CA certificates should be limited to CA activities (Digital Signature, Certificate Sign, CRL Sign).

This behavior can be validated via visual inspection of a certificate with the OpenSSL command: openssl x509 -noout -text -in myca.crt

Segmentation violation when attempting to load intermediate CA

When issuing an intermediate CA, the RSA key as well as the CSR are placed in the "ca" directory for the intermediate's common name, however the .crt file containing the signed certificate is placed under the root CA's "certs" directory (e.g. /mypki/myRoot/certs/myIntermediate.crt). Later, when attempting to goca.Load() the intermediate CA, goca expects the .crt file to be under /mypki/myIntermediate/ca/myIntermediate.crt. Because of this, in loadCa() the caData.Certificate field ends up being empty and later on this causes a segmentation fault.

A temporary work around is to symlink to the .crt so later calls to Load() find the .crt where it is expected.

A design decision will need to be made in order to create a fix. Either CA.SignCSR() will need to be smart enough to realize it is signing an intermediate CA and handle the location of the resulting .crt specially; some variation of goca.New() special handling for intermediate CAs / a new function call for issuing intermediate CAs; or goca.Load() needs to be smart enough to realize it is handling an intermediate CA and lookup the .crt in the signer's certs dir (seems hacky and gross IMO).

There is probably an opportunity for stronger "file exists" style validation in CA.loadCa() too since it relies on the existence of a directory matching the specified common name and assumes the child files of that directory exist.

See the following snippet for a demonstration of the issue.

package main

import (
    "os"
    "log"
    "fmt"

    "github.com/kairoaraujo/goca"
)

func main() {
    os.Setenv("CAPATH", "./crypt/mypki")

    // make sure root CA and intermediate CA generation
    // happen separately so when we go to sign a server
    // cert with the intermediate we have to Load()
    // the intermediate from disk.
    One()
    Two()
}

func Two() {
    // NOTE: crashes at the Load call below.
    interCA, err := goca.Load("myIntermediate")
    if err != nil {
        fmt.Println("loading inter")
        log.Fatal(err)
    }

    srvID := goca.Identity{
        Organization: "Something",
        OrganizationalUnit: "Something",
        Country: "US",
        Locality: "Somewhere",
        Province: "Some State",
        Intermediate: false,
    }

    _, err = interCA.IssueCertificate("yolo", srvID)
    if err != nil {
        fmt.Println("signing srv w/ inter")
        log.Fatal(err)
    }
}

func One() {
    rootCAID := goca.Identity{
        Organization: "Some Org.",
        OrganizationalUnit: "Certificate Authority",
        Country:    "US",
        Locality:   "Somewhere",
        Province:   "Some State",
        Intermediate:   false,
        KeyBitSize: 4096,
        // although regular certs have a max validity of 825 days, there does not
        // appear to be a max imposed by goca for root CAs.
        Valid:      3650,
    }

    rootCA, err := goca.New("myRoot", rootCAID)

    if err != nil {
        fmt.Println("create root CA")
        log.Fatal(err)
    }

    interID := goca.Identity{
        Organization: "Some Org.",
        OrganizationalUnit: "Intermediate",
        Country: "US",
        Locality: "Somewhere",
        Province: "Some State",
        Intermediate: true,
        KeyBitSize: 4096,
        // with goca, intermediates are also limited to 825 days, probably for the best
        Valid: 825,
    }

    interCA, err := goca.New("myIntermediate", interID)
    if err != nil {
        fmt.Println("create inter CA")
        log.Fatal(err)
    }

    if !interCA.IsIntermediate() {
        fmt.Println("inter is not inter...")
    }

    // When Intermediate == true, goca only generates a CSR from New()
    // and the root CA needs to sign it to seal the deal and make it a
    // proper intermediate CA.
    _, err = rootCA.SignCSR(*interCA.GoCSR(), 825)
    if err != nil {
        fmt.Println("sign inter with root")
        log.Fatal(err)
    }
}

evaluate thread safety of storage package

The storage package may have race conditions when accessing certificates. See pull #16 for more discussion.
Note that if file locks are used they will need to be implemented in a cross-platform way.

Some design work is needed, since the storage package is intended to be private to Goca, it may make more sense to implement any kind of locking at a higher level of abstraction.

GoCA should use ``path/filepath`` to join paths

Modify GoCA to use path/filepath using filepath.Join() to join paths.

Currently GoCA uses paths based on *nix paths.
Using file path.join() will make it compatible with Windows as well.

net.IP slice breaks swag generated documentation

The tags on the goca.Identity.IPAddresses field for swag generated documentation cause the doc tests to fail. Swag doesn't know what to do with the []net.IP type when parsing the provided example.

As a temporary workaround, the tag can be replaced with swaggerignore:"true" to have swag skip it.

The swaggertype tag can be used to provide a type hint to swag, but since byte isn't a primitive type to swag, it fails on this too. (net.IP is a typedef of []byte).

According to the swag README section "Use swaggertype tag to supported custom type" we may be able to implement the encoding.JSON.Marshaler interface and use the swaggertype tag to get example parsing working.

When creating an intermediate CA an HTTP 400 can be returned while retaining the CSR on disk.

When the REST API is accessed to create an intermediate CA, but the parent_common_name field on the payload is omitted, the following error message is returned to the client along with an HTTP 400 status set: {"error":"parent common name is empty when creating an intermediate CA certificate"}

However, getting a listing of $CAPATH on the file system of the server shows that the CSR exists on disk, it just has not been signed by a root CA.

We either want to continue returning an error status but not retain the CSR on the server; or only issue a warning but retain the CSR so that the client can try to manually sign the CSR with a root CA.

creating intermediate ca signed by an external root

I don't understand the concept of creating an intermediate ca.

In my understanding, it should create a full CA, but instead of the ca.crt, a csr should be created, and after getting it signed with the root-CA, the ca.crt should be uploaded. it works this way using easy-rsa f.e.

here, when I create a ca with "intermediate": true, and "parent_common_name" added, the resulted CA data will contain only keys, and "isIntermediate": false

It's my issue tbh, but how this flow supposed to work?

make unit test suite cross-platform

Currently, unit tests executed via go test require a Unix-like environment for execution.

This is due to a Unix-style relative path being set as CaTestFolder in goca_test.go as well as some Unix-style relative paths in the caPathInit() function, currently defined in storage.go, specifically for handling testing.

Improving the cross-platform compatibility in these locations would make it easier to support Windows as a development platform.

Support databases as storage

This is a feature request. I will probably work on it later.

Current storage uses file system to store keys and certificates. This will not scale well when the number of certificates grows to a magnitude of 10^4 or larger. I'm considering create a storage adapter that can support databases, both relational databases (e.g. MySQL) and key-value storage (e.g. Redis), while retaining the ability to use file system as the storage.

I don't have a detailed design yet. On the high level, we can abstract the load/save operations and have different implementations behind the scene. I will add more details soon.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.