Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting CN and validity days on certificate generation #6

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions cmd/deptokens/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,21 @@ import (
"github.com/micromdm/nanodep/tokenpki"
)

const (
defaultCN = "deptokens"
defaultDays = 1
)
const defaultCN = "deptokens"
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved

// 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")
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")
flVersion = flag.Bool("version", false, "print version")
flCert = flag.String("cert", "cert.pem", "path to certificate")
flKey = flag.String("key", "cert.key", "path to key")
flCN = flag.String("cn", defaultCN, "common name to use when creating the certificate")
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved
flValidityDays = flag.Int64("validity-days", 0, "validity of the certificate in days")
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved
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")
flVersion = flag.Bool("version", false, "print version")
)
flag.Parse()

Expand All @@ -39,10 +38,14 @@ func main() {

var err error
if *flTokens == "" {
if *flValidityDays <= 0 {
fmt.Println("ERROR: missing or invalid -validity-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, *flValidityDays)
if err == nil {
fmt.Printf("wrote %s, %s\n", *flCert, *flKey)
}
Expand Down Expand Up @@ -101,7 +104,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, validityDays int64) error {
if !force {
_, err := os.Stat(certFile)
certExists := err == nil
Expand All @@ -111,7 +114,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, validityDays)
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:
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved
- in: query
name: cn
required: false
schema:
type: string
example: "depserver"
- in: query
name: validity_days
required: true
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved
schema:
type: integer
example: 365
responses:
'200':
description: X.509 certificate of the keypair used to encrypted the OAuth1 tokens.
Expand Down
18 changes: 15 additions & 3 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 @@ -172,7 +172,7 @@ For the DEP "MDM server" in the environment variable $DEP_NAME (see above) this
##### Example usage
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved

```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 +520,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").

#### -validity-days

* validity of the generated certificate in days

The generated certificate will expire after the provided days.

#### -token string

* path to tokens
Expand All @@ -539,7 +551,7 @@ Print version and exit.
#### Keypair generation

```bash
$ ./deptokens-darwin-amd64 -password supersecret
$ ./deptokens-darwin-amd64 -password supersecret -validity-days 1
wrote cert.pem, cert.key
```

Expand Down
23 changes: 20 additions & 3 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 @@ -25,8 +26,7 @@ type TokenPKIStorer interface {
}

const (
defaultCN = "depserver"
defaultDays = 1
defaultCN = "depserver"
)

// PEMRSAPrivateKey returns key as a PEM block.
Expand Down Expand Up @@ -55,7 +55,24 @@ func GetCertTokenPKIHandler(store TokenPKIStorer, logger log.Logger) http.Handle
return
}
logger = logger.With("name", r.URL.Path)
key, cert, err := tokenpki.SelfSignedRSAKeypair(defaultCN, defaultDays)
daysArg := r.URL.Query().Get("validity_days")
if daysArg == "" {
logger.Info("msg", "validity_days check", "err", "missing validity_days")
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved
}
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) {
jessepeterson marked this conversation as resolved.
Show resolved Hide resolved
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
Expand Down
4 changes: 3 additions & 1 deletion tools/cfg-get-cert.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/bin/sh

URL="${BASE_URL}/v1/tokenpki/${DEP_NAME}"
CN="${1:-depserver}"
VALIDITY_DAYS="${2:-1}"
URL="${BASE_URL}/v1/tokenpki/${DEP_NAME}?cn=$CN&validity_days=$VALIDITY_DAYS"
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved

curl \
$CURL_OPTS \
Expand Down