add ecdsa key support and enable 4,092 RSA

* apcssh: add descriptive error when required file(s) not passed
* create: dont create key+cert file when key isn't supported by NMC2
* config: fix usage messages re: key types
* p15 files: dont generate key+cert when it isn't needed (aka NMC2 doesn't support key)
* pkcs15: pre-calculate envelope when making the p15 struct
* pkcs15: omit key ID 8 & 9 from EC keys
* pkcs15: update key decode logic
* pkcs15: add key type value for easy determination of compatibility
* pkcs15: add ec key support
* pkcs15: separate functions for key and key+cert p15 files
* update README
see: https://github.com/gregtwallace/apc-p15-tool/issues/6
This commit is contained in:
Greg T. Wallace 2024-09-17 18:44:33 -04:00
parent 51e5847409
commit cbb831e009
12 changed files with 508 additions and 266 deletions

View file

@ -51,11 +51,14 @@ func (app *app) cmdCreate(_ context.Context, args []string) error {
}
app.stdLogger.Printf("create: apc p15 key file %s written to disk", keyFileName)
err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0600)
if err != nil {
return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err)
// skip key+cert if it wasn't generated
if len(apcKeyCertFile) > 0 {
err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0600)
if err != nil {
return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err)
}
app.stdLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileName)
}
app.stdLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileName)
// if debug, write additional debug files (b64 format to make copy/paste into asn1 decoder
// easy to do e.g., https://lapo.it/asn1js)
@ -67,19 +70,22 @@ func (app *app) cmdCreate(_ context.Context, args []string) error {
}
app.debugLogger.Printf("create: apc p15 key file %s written to disk", keyFileNameDebug)
keyCertFileNameDebug := keyCertFileName + ".noheader.b64"
err = os.WriteFile(keyCertFileNameDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0600)
if err != nil {
return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err)
}
app.debugLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileNameDebug)
// skip key+cert if it wasn't generated
if len(apcKeyCertFile) > 0 {
keyCertFileNameDebug := keyCertFileName + ".noheader.b64"
err = os.WriteFile(keyCertFileNameDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0600)
if err != nil {
return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err)
}
app.debugLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileNameDebug)
keyCertFileNameHeaderDebug := keyCertFileName + ".header.b64"
err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[:apcHeaderLen])), 0600)
if err != nil {
return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err)
keyCertFileNameHeaderDebug := keyCertFileName + ".header.b64"
err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[:apcHeaderLen])), 0600)
if err != nil {
return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err)
}
app.debugLogger.Printf("create: apc p15 key+cert file header %s written to disk", keyCertFileNameHeaderDebug)
}
app.debugLogger.Printf("create: apc p15 key+cert file header %s written to disk", keyCertFileNameHeaderDebug)
}

View file

@ -68,9 +68,9 @@ func (app *app) getConfig(args []string) error {
// create -- subcommand
createFlags := ff.NewFlagSet("create").SetParent(rootFlags)
cfg.create.keyPemFilePath = createFlags.StringLong("keyfile", "", "path and filename of the rsa-1024 or rsa-2048 key in pem format")
cfg.create.keyPemFilePath = createFlags.StringLong("keyfile", "", "path and filename of the key in pem format")
cfg.create.certPemFilePath = createFlags.StringLong("certfile", "", "path and filename of the certificate in pem format")
cfg.create.keyPem = createFlags.StringLong("keypem", "", "string of the rsa-1024 or rsa-2048 key in pem format")
cfg.create.keyPem = createFlags.StringLong("keypem", "", "string of the key in pem format")
cfg.create.certPem = createFlags.StringLong("certpem", "", "string of the certificate in pem format")
cfg.create.outFilePath = createFlags.StringLong("outfile", createDefaultOutFilePath, "path and filename to write the key+cert p15 file to")
cfg.create.outKeyFilePath = createFlags.StringLong("outkeyfile", createDefaultOutKeyFilePath, "path and filename to write the key p15 file to")
@ -88,9 +88,9 @@ func (app *app) getConfig(args []string) error {
// install -- subcommand
installFlags := ff.NewFlagSet("install").SetParent(rootFlags)
cfg.install.keyPemFilePath = installFlags.StringLong("keyfile", "", "path and filename of the rsa-1024 or rsa-2048 key in pem format")
cfg.install.keyPemFilePath = installFlags.StringLong("keyfile", "", "path and filename of the key in pem format")
cfg.install.certPemFilePath = installFlags.StringLong("certfile", "", "path and filename of the certificate in pem format")
cfg.install.keyPem = installFlags.StringLong("keypem", "", "string of the rsa-1024 or rsa-2048 key in pem format")
cfg.install.keyPem = installFlags.StringLong("keypem", "", "string of the key in pem format")
cfg.install.certPem = installFlags.StringLong("certpem", "", "string of the certificate in pem format")
cfg.install.hostAndPort = installFlags.StringLong("apchost", "", "hostname:port of the apc ups to install the certificate on")
cfg.install.fingerprint = installFlags.StringLong("fingerprint", "", "the SHA256 fingerprint value of the ups' ssh server")

View file

@ -3,13 +3,22 @@ package app
import (
"apc-p15-tool/pkg/pkcs15"
"fmt"
"slices"
)
// pemToAPCP15 reads the specified pem files and returns the apc p15 files (both a
// p15 file with just the private key, and also a p15 file with both the private key
// and certificate). The key+cert file includes the required APC header, prepended.
// list of keys supported by the NMC2
var nmc2SupportedKeyTypes = []pkcs15.KeyType{
pkcs15.KeyTypeRSA1024,
pkcs15.KeyTypeRSA2048,
pkcs15.KeyTypeRSA3072, // officially not supported but works
}
// pemToAPCP15 reads the specified pem files and returns the apc p15 file(s). If the
// key type of the key is not supported by NMC2, the combined key+cert file is not
// generated and nil is returned instead for that file. If the key IS supported by
// NMC2, the key+cert file is generated and the proper header is prepended.
func (app *app) pemToAPCP15(keyPem, certPem []byte, parentCmdName string) (keyFile []byte, apcKeyCertFile []byte, err error) {
app.stdLogger.Printf("%s: making apc p15 file from pem", parentCmdName)
app.stdLogger.Printf("%s: making apc p15 file(s) content from pem", parentCmdName)
// make p15 struct
p15, err := pkcs15.ParsePEMToPKCS15(keyPem, certPem)
@ -17,24 +26,40 @@ func (app *app) pemToAPCP15(keyPem, certPem []byte, parentCmdName string) (keyFi
return nil, nil, fmt.Errorf("%s: failed to parse pem files (%w)", parentCmdName, err)
}
app.stdLogger.Printf("%s: successfully loaded pem files", parentCmdName)
app.stdLogger.Printf("%s: successfully parsed pem files", parentCmdName)
// make file bytes
keyCertFile, keyFile, err := p15.ToP15Files()
// make key file (always)
keyFile, err = p15.ToP15Key()
if err != nil {
return nil, nil, fmt.Errorf("%s: failed to make p15 file (%w)", parentCmdName, err)
return nil, nil, fmt.Errorf("%s: failed to make p15 key file (%w)", parentCmdName, err)
}
// make header for file bytes
apcHeader, err := makeFileHeader(keyCertFile)
if err != nil {
return nil, nil, fmt.Errorf("%s: failed to make p15 file header (%w)", parentCmdName, err)
app.stdLogger.Printf("%s: successfully generated p15 key file content", parentCmdName)
// check key type for compat with NMC2
if slices.Contains(nmc2SupportedKeyTypes, p15.KeyType()) {
app.stdLogger.Printf("%s: key type is supported by NMC2, generating p15 key+cert file content...", parentCmdName)
// make file bytes
keyCertFile, err := p15.ToP15KeyCert()
if err != nil {
return nil, nil, fmt.Errorf("%s: failed to make p15 key+cert file content (%w)", parentCmdName, err)
}
// make header for file bytes
apcHeader, err := makeFileHeader(keyCertFile)
if err != nil {
return nil, nil, fmt.Errorf("%s: failed to make p15 key+cert file header (%w)", parentCmdName, err)
}
// combine header with file
apcKeyCertFile = append(apcHeader, keyCertFile...)
} else {
// NMC2 unsupported
app.stdLogger.Printf("%s: key type is not supported by NMC2, skipping p15 key+cert file content", parentCmdName)
}
// combine header with file
apcKeyCertFile = append(apcHeader, keyCertFile...)
app.stdLogger.Printf("%s: apc p15 file data succesfully generated", parentCmdName)
app.stdLogger.Printf("%s: apc p15 file(s) data succesfully generated", parentCmdName)
return keyFile, apcKeyCertFile, nil
}