Skip to content

Protocols

Lamassu supports a set of standards to perform some of its key functionalities such as enrolling devices as well as validating the status of a given certificate. This section aims to describe those protocols as well as explaining how them with practical examples.

OCSP

The Online Certificate Status Protocol or OCSP for short, is a protocol used to determine the current status of a digital certificate without requiring the use of Certificate Revocation Lists (CRLs).

As defined by the standard, there are two possible methods that can be used to perform the http request:

Method Path Headers Body payload Used when
GET {url}/{url-encoding of base-64 encoding of the DER encoding of the OCSPRequest} Recommended when the encoded request is less than 255 bytes
PUT {url} Content-Type: application/ocsp-request Binary value of the DER encoding of the OCSPRequest Can always be used

GET Request

Define the OCSP server endpoint as well as the

export OCSP_SERVER=dev.lamassu.io:443 
export CA_CERTIFICATE=issuer_ca.crt 
export DEVICE_CERTIFICATE=device.crt

Obtain the Root certificate used by the server

openssl s_client -connect $OCSP_SERVER  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem

Create the OCSP Request

OCSP_REQUEST=$(openssl ocsp -CAfile $CA_CERTIFICATE -issuer $CA_CERTIFICATE -cert $DEVICE_CERTIFICATE -reqout - | base64 -w 0)

Check the status of the certificate

curl --location --request GET "https://$OCSP_SERVER/api/ocsp/$OCSP_REQUEST" > ocspresponse.der 
openssl ocsp -respin ocspresponse.der -VAfile root-ca.pem -resp_text

package main

import (
    "crypto/x509"
    "encoding/base64"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"

    "github.com/lamassuiot/lamassuiot/pkg/ocsp/server/crypto/ocsp"
)

func main() {
    ocspServer := "http://localhost:9098"
    issuerCA := "ca.crt"
    certificateToCheck := "device.crt"

    caPEM, err := ioutil.ReadFile(issuerCA)
    if err != nil {
        fmt.Println("Could not load CA certificate")
        os.Exit(1)
    }
    caPemBlock, _ := pem.Decode(caPEM)
    ca, err := x509.ParseCertificate(caPemBlock.Bytes)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    devicePEM, err := ioutil.ReadFile(certificateToCheck)
    if err != nil {
        fmt.Println("Could not load Device certificate")
        os.Exit(1)
    }
    devicePemBlock, _ := pem.Decode(devicePEM)
    device, err := x509.ParseCertificate(devicePemBlock.Bytes)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    ocspRequestBytes, err := ocsp.CreateRequest(device, ca, &ocsp.RequestOptions{})
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    encodedRequest := base64.StdEncoding.EncodeToString(ocspRequestBytes)
    fmt.Println(encodedRequest)

    reqURL := ocspServer + "/" + encodedRequest

    resp, err := http.Get(reqURL)

    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    if resp.StatusCode != http.StatusOK {
        os.Exit(1)
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        os.Exit(1)
    }
    resp.Body.Close()

    ocspResponse, err := ocsp.ParseResponse(body, nil)
    if err != nil {
        fmt.Println("Could not parse OCSP response ", err)
        os.Exit(1)
    }

    fmt.Println(ocspResponse.Status == ocsp.Good)
    fmt.Println(ocspResponse.Status == ocsp.Revoked)
    fmt.Println(ocspResponse.RevokedAt)
}

POST Request

Define the OCSP server endpoint as well as the

export OCSP_SERVER=dev.lamassu.io:443 
export CA_CERTIFICATE=issuer_ca.crt 
export DEVICE_CERTIFICATE=device.crt

Obtain the Root certificate used by the server

openssl s_client -connect $OCSP_SERVER  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem

Create the OCSP Request

openssl ocsp -issuer ca.crt -cert dev.crt -reqout - > ocsp-request-post.der

Check the status of the certificate

curl --location --request POST "https://$DOMAIN/api/ocsp/" --header 'Content-Type: application/ocsp-request' --data-binary '@ocsp-request-post.der' > ocsp-response-post.der -k
openssl ocsp -respin ocsp-response-post.der -VAfile root-ca.pem -resp_text

    package main

    import (
        "bytes"
        "crypto/x509"
        "encoding/pem"
        "fmt"
        "io/ioutil"
        "net/http"
        "os"

        "github.com/lamassuiot/lamassuiot/pkg/ocsp/server/crypto/ocsp"
    )

    func main() {
        ocspServer := "http://localhost:9098"
        issuerCA := "ca.crt"
        certificateToCheck := "device.crt"

        caPEM, err := ioutil.ReadFile(issuerCA)
        if err != nil {
            fmt.Println("Could not load CA certificate")
            os.Exit(1)
        }
        caPemBlock, _ := pem.Decode(caPEM)
        ca, err := x509.ParseCertificate(caPemBlock.Bytes)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        devicePEM, err := ioutil.ReadFile(certificateToCheck)
        if err != nil {
            fmt.Println("Could not load Device certificate")
            os.Exit(1)
        }
        devicePemBlock, _ := pem.Decode(devicePEM)
        device, err := x509.ParseCertificate(devicePemBlock.Bytes)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        ocspRequestBytes, err := ocsp.CreateRequest(device, ca, &ocsp.RequestOptions{})
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        resp, err := http.Post(ocspServer, "application/ocsp-request", bytes.NewReader(ocspRequestBytes))

        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        if resp.StatusCode != http.StatusOK {
            os.Exit(1)
        }

        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            os.Exit(1)
        }
        resp.Body.Close()

        ocspResponse, err := ocsp.ParseResponse(body, nil)
        if err != nil {
            fmt.Println("Could not parse OCSP response ", err)
            os.Exit(1)
        }

        fmt.Println(ocspResponse.Status == ocsp.Good)
        fmt.Println(ocspResponse.Status == ocsp.Revoked)
        fmt.Println(ocspResponse.RevokedAt)

    }

EST

The core mechanism to obtain valid certificates for your devices is using the enrollment process described by the EST protocol. EST or Enrollment over Secure Transport establishes a set of standardized endpoints. The following table sums up all endpoints defined by the EST protocol and wether or not are supported by the current implementation.

Operation Operation Path Required by RFC7030 Supported
Distribution of CA Certificates /api/devmanager/.well-known/cacerts
Enrollment of Clients /api/devmanager/.well-known/simpleenroll
Re-enrollment of Clients /api/devmanager/.well-known/simplereenroll
Full CMC /api/devmanager/.well-known/fullcmc
Server-Side Key Generation /api/devmanager/.well-known/serverkeygen
CSR Attributes /api/devmanager/.well-known/csrattrs

Distribution of CA Certificates

Install GlobalSign Est Client

go install github.com/globalsign/est/cmd/estclient@latest

Define the DOMAIN as well as the

export DOMAIN=dev.lamassu.io 
Obtain the Root certificate used by the server
openssl s_client -connect $DOMAIN:443  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem
Obtaining CAs certificates
estclient cacerts -server $DOMAIN/api/devmanager -explicit root-ca.pem -out cacerts.pem

package main

import (
    "context"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "os"

    "github.com/lamassuiot/lamassuiot/pkg/est/client"
)

func main() {
    estServerAddr := "dev.lamassu.io/api/devmanager"
    servercrt := "server.crt"
    clientcrt := "dms.crt"
    clientkey := "dms.key"

    caCert, err := ioutil.ReadFile(servercrt)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)

    certContent, err := ioutil.ReadFile(clientcrt)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    cpb, _ := pem.Decode(certContent)

    crt, err := x509.ParseCertificate(cpb.Bytes)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    key, err := ioutil.ReadFile(clientkey)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    estClient, err := client.NewLamassuEstClient(estServerAddr, caCertPool, crt, key, nil)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    cas, err := estClient.CACerts(context.Background())
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

Define the DOMAIN as well as the

export DOMAIN=dev.lamassu.io 
Obtain the Root certificate used by the server
openssl s_client -connect $DOMAIN:443  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem
Obtaining CAs certificates
curl https://$DOMAIN/api/devmanager/.well-known/est/cacerts -o cacerts.p7 --cacert root-ca.pem
openssl base64 -d -in cacerts.p7 | openssl pkcs7 -inform DER -outform PEM -print_certs -out cacerts.pem 

Enrollment of Devices

Install GlobalSign Est Client

go install github.com/globalsign/est/cmd/estclient@latest

Define environment variables as well as the

export DOMAIN=dev.lamassu.io
export CA_NAME=Test-CA
export DEVICE_ID=$(uuidgen)
export DMS_CERT=dms.crt
export DMS_KEY=dms.key  

Note

The name of the CA has to be that of a CA that has the DMS as Authorized_CAs.

Obtain the Root certificate used by the server

openssl s_client -connect $DOMAIN:443  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem
Enrolling with an existing private key
openssl genrsa 4096 > key.pem
estclient csr -key key.pem -cn $DEVICE_ID -out csr.pem
estclient enroll -server $DOMAIN/api/devmanager -explicit root-ca.pem -csr csr.pem -aps $CA_NAME -key $DMS_KEY -certs $DMS_CERT -out cert.pem  

package main

import (
    "context"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "os"

    "github.com/lamassuiot/lamassuiot/pkg/est/client"
)

func main() {
    estServerAddr := "dev.lamassu.io/api/devmanager"
    servercrt := "server.crt"
    dmscrt := "dms.crt"
    dmskey := "dms.key"
    devicecsr := "device.csr"
    devicecrt:="device.crt"
    ca_name := "Test-CA"
    caCert, err := ioutil.ReadFile(servercrt)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)

    certContent, err := ioutil.ReadFile(dmscrt)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    cpb, _ := pem.Decode(certContent)

    crt, err := x509.ParseCertificate(cpb.Bytes)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    certContent, err = ioutil.ReadFile(devicecsr)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    cpb, _ = pem.Decode(certContent)

    csr, err := x509.ParseCertificateRequest(cpb.Bytes)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    key, err := ioutil.ReadFile(dmskey)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    estClient, err := client.NewLamassuEstClient(estServerAddr, caCertPool, crt, key, nil)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    cert, err := estClient.Enroll(context.Background(), ca_name, csr)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    b := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
    certPEM := pem.EncodeToMemory(&b)
    ioutil.WriteFile(devicecrt, certPEM, 0777)
}

Define environment variables as well as the

export DOMAIN=dev.lamassu.io
export CA_NAME=Test-CA
export DEVICE_ID=$(uuidgen)
export DMS_CERT=dms.crt
export DMS_KEY=dms.key  

Note

The name of the CA has to be that of a CA that has the DMS as Authorized_CAs.

Obtain the Root certificate used by the server

openssl s_client -connect $DOMAIN:443  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem
Create device CSR and private Key
openssl req -new -newkey rsa:2048 -nodes -keyout device.key -out device.csr -subj "/CN=$DEVICE_ID"

sed '/CERTIFICATE/d' device.csr > device_enroll.csr   

Enrolling with an existing private key

curl https://$DOMAIN/api/devmanager/.well-known/est/$CA_NAME/simpleenroll --cert $DMS_CRT --key $DMS_KEY -s -o cert.p7 --cacert root-ca.pem  --data-binary @device_enroll.csr -H "Content-Type: application/pkcs10"

openssl base64 -d -in cert.p7 | openssl pkcs7 -inform DER -outform PEM -print_certs -out cert.pem   

Re-enrollment of Devices

Install GlobalSign Est Client

go install github.com/globalsign/est/cmd/estclient@latest

Define environment variables as well as the

export DOMAIN=dev.lamassu.io
export DEVICE_CRT=device.crt
export DEVICE_KEY=device.key  

Obtain the Root certificate used by the server

openssl s_client -connect $DOMAIN:443  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem
Reenrolling
estclient reenroll -server $DOMAIN/api/devmanager -explicit root-ca.pem -key $DEVICE_KEY -certs $DEVICE_CRT -out newcert.pem

    package main

    import (
        "context"
        "crypto/x509"
        "encoding/pem"
        "fmt"
        "io/ioutil"
        "os"

        "github.com/lamassuiot/lamassuiot/pkg/est/client"
    )

    func main() {
        estServerAddr := "dev.lamassu.io/api/devmanager"
        servercrt := "server.crt"
        devicecrt := "device.crt"
        devicekey := "device.key"
        devicecsr := "device.csr"

        caCert, err := ioutil.ReadFile(servercrt)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        caCertPool := x509.NewCertPool()
        caCertPool.AppendCertsFromPEM(caCert)

        certContent, err := ioutil.ReadFile(devicecrt)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        cpb, _ := pem.Decode(certContent)

        crt, err := x509.ParseCertificate(cpb.Bytes)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        certContent, err = ioutil.ReadFile(devicecsr)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        cpb, _ = pem.Decode(certContent)

        csr, err := x509.ParseCertificateRequest(cpb.Bytes)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        key, err := ioutil.ReadFile(devicekey)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        estClient, err := client.NewLamassuEstClient(estServerAddr, caCertPool, crt, key, nil)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        cert, err := estClient.Reenroll(context.Background(), csr)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        b := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
        certPEM := pem.EncodeToMemory(&b)
        ioutil.WriteFile(devicecrt, certPEM, 0777)
    }

Define environment variables as well as the

export DOMAIN=dev.lamassu.io
export DEVICE_CRT=device.crt
export DEVICE_KEY=device.key
exprot DEVICE_CSR=device.csr  

Obtain the Root certificate used by the server

openssl s_client -connect $DOMAIN:443  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem
Create device enroll CSR
sed '/CERTIFICATE/d' device.csr > device_enroll.csr   
Reenrolling
curl https://$DOMAIN/api/devmanager/.well-known/est/simplereenroll --cert $DEVICE_CRT --key $DEVICE_KEY -s -o newcert.p7 --cacert root-ca.pem --data-binary @device_enroll.csr -H "Content-Type: application/pkcs10"

openssl base64 -d -in newcert.p7 | openssl pkcs7 -inform DER -outform PEM -print_certs -out $DEVICE_CRT

Server Key Generation of Devices

Install GlobalSign Est Client

go install github.com/globalsign/est/cmd/estclient@latest

Define environment variables as well as the

export DOMAIN=dev.lamassu.io
export CA_NAME=Test-CA
export DEVICE_ID=$(uuidgen)
export DMS_CERT=dms.crt
export DMS_KEY=dms.key  

Note

The name of the CA has to be that of a CA that has the DMS as Authorized_CAs.

Obtain the Root certificate used by the server

openssl s_client -connect $DOMAIN:443  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem
Enrolling with a server-generated private key
openssl genrsa 4096 > key.pem
estclient csr -key key.pem -cn $DEVICE_ID -out csr.pem
estclient serverkeygen -server $DOMAIN/api/devmanager -explicit root-ca.pem -csr csr.pem -aps $CA_NAME -key $DMS_KEY -certs $DMS_CERT -out device.crt -keyout device.key

    package main

    import (
        "context"
        "crypto/x509"
        "encoding/pem"
        "fmt"
        "io/ioutil"
        "os"

        "github.com/lamassuiot/lamassuiot/pkg/est/client"
    )

    func main() {
        estServerAddr := "dev.lamassu.io/api/devmanager"
        servercrt := "server.crt"
        dmscrt := "dms.crt"
        dmskey := "dms.key"
        devicecrt := "device.crt"
        devicekey := "device.key"
        devicecsr := "device.csr"
        ca_name := "Test-CA"
        caCert, err := ioutil.ReadFile(servercrt)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        caCertPool := x509.NewCertPool()
        caCertPool.AppendCertsFromPEM(caCert)

        certContent, err := ioutil.ReadFile(dmscrt)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        cpb, _ := pem.Decode(certContent)

        crt, err := x509.ParseCertificate(cpb.Bytes)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        certContent, err = ioutil.ReadFile(devicecsr)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        cpb, _ = pem.Decode(certContent)

        csr, err := x509.ParseCertificateRequest(cpb.Bytes)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        key, err := ioutil.ReadFile(dmskey)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        estClient, err := client.NewLamassuEstClient(estServerAddr, caCertPool, crt, key, nil)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        cert, key, err := estClient.ServerKeyGen(context.Background(), ca_name, csr)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        b := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
        certPEM := pem.EncodeToMemory(&b)
        ioutil.WriteFile(devicecrt, certPEM, 0777)

        b = pem.Block{Type: "PRIVATE KEY", Bytes: key}
        keyPEM := pem.EncodeToMemory(&b)
        ioutil.WriteFile(devicekey, keyPEM, 0777)
    }

Define environment variables as well as the

export DOMAIN=dev.lamassu.io
export CA_NAME=Test-CA
export DEVICE_ID=$(uuidgen)
export DMS_CERT=dms.crt
export DMS_KEY=dms.key  

Note

The name of the CA has to be that of a CA that has the DMS as Authorized_CAs.

Obtain the Root certificate used by the server

openssl s_client -connect $DOMAIN:443  2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > root-ca.pem
Create device CSR and private Key
openssl req -new -newkey rsa:2048 -nodes -keyout device.key -out device.csr -subj "/CN=$DEVICE_ID"

sed '/CERTIFICATE/d' device.csr > device_enroll.csr   
Enrolling with a server-generated private key
curl https://$DOMAIN/api/devmanager/.well-known/est/$CA_NAME/serverkeygen --cert $DMS_CERT --key $DMS_KEY -s -o cert.p7 --cacert root-ca.pem  --data-binary @device_enroll.csr -H "Content-Type: application/pkcs10"

cat cert.p7 | sed -ne '/application\/pkcs7-mime/,/-estServerKeyGenBoundary/p' |  sed '/-/d' > crt.p7

openssl base64 -d -in crt.p7 | openssl pkcs7 -inform DER -outform PEM -print_certs -out device.pem