Skip to content

Commit

Permalink
Allow setting CN and validity days on certificate generation (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmrod committed Aug 30, 2022
1 parent 077b73d commit a7538c5
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 18 deletions.
17 changes: 9 additions & 8 deletions cmd/deptokens/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,15 @@ import (
"github.com/micromdm/nanodep/tokenpki"
)

const (
defaultCN = "deptokens"
defaultDays = 1
)

// overridden by -ldflags -X
var version = "unknown"

func main() {
var (
flCert = flag.String("cert", "cert.pem", "path to certificate")
flKey = flag.String("key", "cert.key", "path to key")
flCN = flag.String("cn", "deptokens", "common name to use when creating the certificate")
flDays = flag.Int64("days", 1, "validity of the certificate in days")
flPassword = flag.String("password", "", "password to encrypt/decrypt private key with")
flTokens = flag.String("token", "", "path to tokens")
flForce = flag.Bool("f", false, "force overwriting the keypair")
Expand All @@ -39,10 +36,14 @@ func main() {

var err error
if *flTokens == "" {
if *flDays <= 0 {
fmt.Println("ERROR: invalid -days flag")
os.Exit(1)
}
if *flPassword == "" {
fmt.Println("WARNING: no password provided, private key will be saved in clear text")
}
err = generateKeyPair(*flCert, *flKey, *flPassword, *flForce)
err = generateKeyPair(*flCert, *flKey, *flPassword, *flForce, *flCN, *flDays)
if err == nil {
fmt.Printf("wrote %s, %s\n", *flCert, *flKey)
}
Expand Down Expand Up @@ -101,7 +102,7 @@ func decodeEncryptedKeyPEM(pemBytes []byte, password string) (*rsa.PrivateKey, e
}

// generateKeyPair creates and saves a keypair checking whether they exist first.
func generateKeyPair(certFile, keyFile, password string, force bool) error {
func generateKeyPair(certFile, keyFile, password string, force bool, cn string, days int64) error {
if !force {
_, err := os.Stat(certFile)
certExists := err == nil
Expand All @@ -111,7 +112,7 @@ func generateKeyPair(certFile, keyFile, password string, force bool) error {
return errors.New("cert or key already exist, not overwriting")
}
}
key, cert, err := tokenpki.SelfSignedRSAKeypair(defaultCN, defaultDays)
key, cert, err := tokenpki.SelfSignedRSAKeypair(cn, days)
if err != nil {
return fmt.Errorf("generating keypair: %w", err)
}
Expand Down
13 changes: 13 additions & 0 deletions docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,19 @@ paths:
description: Generate and store a new X.509 certificate and RSA private key (keypair) for exchanging the encrypted DEP OAuth1 tokens via the Apple ABM/ASM/BE portal. Each request generates a new (and overwrites the existing) keypair. The certificate is returned.
security:
- basicAuth: []
parameters:
- in: query
name: cn
required: false
schema:
type: string
example: "depserver"
- in: query
name: validity_days
required: false
schema:
type: integer
example: 365
responses:
'200':
description: X.509 certificate of the keypair used to encrypted the OAuth1 tokens.
Expand Down
20 changes: 18 additions & 2 deletions docs/operations-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The `/v1/tokenpki/{name}` endpoints deal with the public key exchange using the

* Endpoint: `GET, PUT /v1/tokens/{name}`

The `/v1/tokens/{name} ` endpoints deal with the raw DEP OAuth tokens in JSON form. I.e. after the PKI exchange you can query for the actual DEP OAuth tokens if you like. This also allows configuring the OAuth1 tokens for a DEP name if you already have the tokens in JSON format. I.e. if you used the `deptokens` tool or you're using the DEP simulator `depsim`.
The `/v1/tokens/{name}` endpoints deal with the raw DEP OAuth tokens in JSON form. I.e. after the PKI exchange you can query for the actual DEP OAuth tokens if you like. This also allows configuring the OAuth1 tokens for a DEP name if you already have the tokens in JSON format. I.e. if you used the `deptokens` tool or you're using the DEP simulator `depsim`.

#### Assigner

Expand Down Expand Up @@ -169,10 +169,14 @@ The [Quickstart Guide](quickstart.md) also documents some usage of these scripts
For the DEP "MDM server" in the environment variable $DEP_NAME (see above) this script generates and retrieves the public key certificate for use when downloading the DEP authentication tokens from the ABM/ASM/BE portal. The `curl` call will dump the PEM-encoded certificate to stdout so you'll likely want to redirect it somewhere useful so it can be uploaded to the portal.
This script has two optional arguments:
- The first argument specifies the Common Name to set in the certificate (default "depserver").
- The second argument specifies the validity of the certificate in days (default 1 day).
##### Example usage
```bash
$ ./tools/cfg-get-cert.sh > $DEP_NAME.pem
$ ./tools/cfg-get-cert.sh depserver 365 > $DEP_NAME.pem
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1001 100 1001 0 0 4509 0 --:--:-- --:--:-- --:--:-- 4509
Expand Down Expand Up @@ -520,6 +524,18 @@ The file path to read or save the RSA private key that corresponds to the public
A password to encrypt or decrypt RSA private key on disk with. Note this is password is just to protect the private key itself and does not play a role in the token PKI exchange with Apple.
#### -cn
* common name to set in the certificate
A Common Name string to set in the certificate (default is "depserver").
#### -days
* validity of the generated certificate in days
The generated certificate will expire after the provided days.
#### -token string
* path to tokens
Expand Down
30 changes: 24 additions & 6 deletions http/api/tokenpki.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"io"
"net/http"
"strconv"

"github.com/micromdm/nanodep/client"
"github.com/micromdm/nanodep/log"
Expand All @@ -24,11 +25,6 @@ type TokenPKIStorer interface {
StoreTokenPKI(context.Context, string, []byte, []byte) error
}

const (
defaultCN = "depserver"
defaultDays = 1
)

// PEMRSAPrivateKey returns key as a PEM block.
func PEMRSAPrivateKey(key *rsa.PrivateKey) []byte {
block := &pem.Block{
Expand All @@ -48,14 +44,36 @@ func PEMRSAPrivateKey(key *rsa.PrivateKey) []byte {
// errors to the output as this is meant for "API" users.
func GetCertTokenPKIHandler(store TokenPKIStorer, logger log.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
const (
defaultCN = "depserver"
defaultDays = 1
)
logger := ctxlog.Logger(r.Context(), logger)
if r.URL.Path == "" {
logger.Info("msg", "DEP name check", "err", "missing DEP name")
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
logger = logger.With("name", r.URL.Path)
key, cert, err := tokenpki.SelfSignedRSAKeypair(defaultCN, defaultDays)
var validityDays int64
if daysArg := r.URL.Query().Get("validity_days"); daysArg == "" {
logger.Debug("msg", "using default validity days", "days", defaultDays)
validityDays = defaultDays
} else {
var err error
validityDays, err = strconv.ParseInt(daysArg, 10, 64)
if err != nil {
logger.Info("msg", "validity_days check", "err", err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
}
cn := r.URL.Query().Get("cn")
if cn == "" {
logger.Debug("msg", "using default CN", "cn", defaultCN)
cn = defaultCN
}
key, cert, err := tokenpki.SelfSignedRSAKeypair(cn, validityDays)
if err != nil {
logger.Info("msg", "generating token keypair", "err", err)
jsonError(w, err)
Expand Down
2 changes: 1 addition & 1 deletion tokenpki/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
// SelfSignedRSAKeypair generates a 2048-bit RSA private key and self-signs an
// X.509 certificate using it. You can set the Common Name in cn and the
// validity duration with days.
func SelfSignedRSAKeypair(cn string, days int) (*rsa.PrivateKey, *x509.Certificate, error) {
func SelfSignedRSAKeypair(cn string, days int64) (*rsa.PrivateKey, *x509.Certificate, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
Expand Down
2 changes: 1 addition & 1 deletion tools/cfg-get-cert.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh

URL="${BASE_URL}/v1/tokenpki/${DEP_NAME}"
URL="${BASE_URL}/v1/tokenpki/${DEP_NAME}?cn=$1&validity_days=$2"

curl \
$CURL_OPTS \
Expand Down

0 comments on commit a7538c5

Please sign in to comment.