Compare commits

..

2 commits

Author SHA1 Message Date
Greg T. Wallace
f102a1838b v1.1.0-b1 2024-07-09 19:05:29 -04:00
Greg T. Wallace
40eca754e0 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
2024-07-09 19:05:14 -04:00
17 changed files with 372 additions and 538 deletions

View file

@ -8,10 +8,40 @@ on:
env: env:
GITHUB_REF: ${{ github.ref }} GITHUB_REF: ${{ github.ref }}
GO_VERSION: '1.22.4'
jobs: jobs:
build-all: build-common:
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
steps:
- name: Checkout Main Repo
uses: actions/checkout@v4
with:
repository: gregtwallace/apc-p15-tool
ref: ${{ env.GITHUB_REF }}
fetch-depth: 0
- name: Save README
uses: actions/upload-artifact@v4
with:
name: README.md
path: ./README.md
- name: Save LICENSE
uses: actions/upload-artifact@v4
with:
name: LICENSE.md
path: ./LICENSE.md
- name: Save CHANGELOG
uses: actions/upload-artifact@v4
with:
name: CHANGELOG.md
path: ./CHANGELOG.md
build-linux-arm64:
runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -20,17 +50,255 @@ jobs:
ref: ${{ env.GITHUB_REF }} ref: ${{ env.GITHUB_REF }}
fetch-depth: 0 fetch-depth: 0
- name: Update apt
run: sudo apt update
- name: Install cross-compiler for linux/arm64
run: sudo apt-get -y install gcc-aarch64-linux-gnu
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '${{ env.GO_VERSION }}'
- name: Build Tool
run: go build -o ./apc-p15-tool -v ./cmd/tool
env:
GOOS: linux
GOARCH: arm64
CC: aarch64-linux-gnu-gcc
CGO_ENABLED: 0
- name: Save Compiled Binary
uses: actions/upload-artifact@v4
with:
name: apc-p15-tool-linux-arm64
path: ./apc-p15-tool
- name: Build Install Only
run: go build -o ./apc-p15-install -v ./cmd/install_only
env:
GOOS: linux
GOARCH: arm64
CC: aarch64-linux-gnu-gcc
CGO_ENABLED: 0
- name: Save Compiled Binary
uses: actions/upload-artifact@v4
with:
name: apc-p15-install-linux-arm64
path: ./apc-p15-install
build-linux-amd64:
runs-on: ubuntu-latest
steps:
- name: Checkout Backend Repo
uses: actions/checkout@v4
with:
repository: gregtwallace/apc-p15-tool
ref: ${{ env.GITHUB_REF }}
fetch-depth: 0
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version-file: 'go.mod' go-version: '${{ env.GO_VERSION }}'
- name: Build All - name: Build Tool
run: | run: go build -o ./apc-p15-tool -v ./cmd/tool
python ./build_release.py env:
GOOS: linux
GOARCH: amd64
CGO_ENABLED: 0
- name: Save Zip of all targets - name: Save Compiled Binary
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: apc-p15-tool-release name: apc-p15-tool-linux-amd64
path: ./_out/_release path: ./apc-p15-tool
- name: Build Install Only
run: go build -o ./apc-p15-install -v ./cmd/install_only
env:
GOOS: linux
GOARCH: amd64
CGO_ENABLED: 0
- name: Save Compiled Binary
uses: actions/upload-artifact@v4
with:
name: apc-p15-install-linux-amd64
path: ./apc-p15-install
build-windows-amd64:
runs-on: windows-latest
steps:
- name: Checkout Backend Repo
uses: actions/checkout@v4
with:
repository: gregtwallace/apc-p15-tool
ref: ${{ env.GITHUB_REF }}
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '${{ env.GO_VERSION }}'
- name: Build Tool
run: go build -o ./apc-p15-tool.exe -v ./cmd/tool
env:
GOOS: windows
GOARCH: amd64
CGO_ENABLED: 0
- name: Save Compiled Binary
uses: actions/upload-artifact@v4
with:
name: apc-p15-tool-windows-amd64
path: ./apc-p15-tool.exe
- name: Build Install Only
run: go build -o ./apc-p15-install.exe -v ./cmd/install_only
env:
GOOS: windows
GOARCH: amd64
CGO_ENABLED: 0
- name: Save Compiled Binary
uses: actions/upload-artifact@v4
with:
name: apc-p15-install-windows-amd64
path: ./apc-p15-install.exe
release-file-linux-arm64:
needs: [build-common, build-linux-arm64]
runs-on: ubuntu-latest
steps:
- name: Make release directory
run: mkdir ./release
- name: Download Tool Binary
uses: actions/download-artifact@v4
with:
name: apc-p15-tool-linux-arm64
path: ./release
- name: Download Install Binary
uses: actions/download-artifact@v4
with:
name: apc-p15-install-linux-arm64
path: ./release
- name: Download README
uses: actions/download-artifact@v4
with:
name: README.md
path: ./release
- name: Download LICENSE
uses: actions/download-artifact@v4
with:
name: LICENSE.md
path: ./release
- name: Download CHANGELOG
uses: actions/download-artifact@v4
with:
name: CHANGELOG.md
path: ./release
- name: Save Release
uses: actions/upload-artifact@v4
with:
name: apc-p15-tool_linux_arm64
path: ./release
release-file-linux-amd64:
needs: [build-common, build-linux-amd64]
runs-on: ubuntu-latest
steps:
- name: Make release directory
run: mkdir ./release
- name: Download Tool Binary
uses: actions/download-artifact@v4
with:
name: apc-p15-tool-linux-amd64
path: ./release
- name: Download Install Binary
uses: actions/download-artifact@v4
with:
name: apc-p15-install-linux-amd64
path: ./release
- name: Download README
uses: actions/download-artifact@v4
with:
name: README.md
path: ./release
- name: Download LICENSE
uses: actions/download-artifact@v4
with:
name: LICENSE.md
path: ./release
- name: Download CHANGELOG
uses: actions/download-artifact@v4
with:
name: CHANGELOG.md
path: ./release
- name: Save Release
uses: actions/upload-artifact@v4
with:
name: apc-p15-tool_linux_amd64
path: ./release
release-file-windows-amd64:
needs: [build-common, build-windows-amd64]
runs-on: ubuntu-latest
steps:
- name: Make release directory
run: mkdir ./release
- name: Download Tool Binary
uses: actions/download-artifact@v4
with:
name: apc-p15-tool-windows-amd64
path: ./release
- name: Download Install Binary
uses: actions/download-artifact@v4
with:
name: apc-p15-install-windows-amd64
path: ./release
- name: Download README
uses: actions/download-artifact@v4
with:
name: README.md
path: ./release
- name: Download LICENSE
uses: actions/download-artifact@v4
with:
name: LICENSE.md
path: ./release
- name: Download CHANGELOG
uses: actions/download-artifact@v4
with:
name: CHANGELOG.md
path: ./release
- name: Save Release
uses: actions/upload-artifact@v4
with:
name: apc-p15-tool_windows_amd64
path: ./release

View file

@ -1,75 +1,13 @@
# APC P15 Tool Changelog # APC P15 Tool Changelog
## [v1.3.0] - 2025-06-23 ## [v1.1.0-b1] - 2024-07-09
This release attempts to detect and warn of possible incompatibilies with a BETA: First effort to add support for EC key support for NMC3.
spcecified certificate. NMCs do not warn or error when a bad file is installed,
instead they silently fail and generally just generate a new self-signed
certificate. This release checks some properties of the specified certificate
and produces warning messages that can be referenced if the cert installation
appears to work but ultimately doesn't prododuce the expected result.
- Add warnings based on key type, signature algorithm, validity dates, and This version also enables RSA 4,092 bit length. Code was updated
extensions. so the NMC2 key+cert file is not generated when NMC2 is not
- Minor lint. supported. Log messages were also updated to signal user when
a compatibility issue is present.
## [v1.2.3] - 2025-06-19
Minor updates to the application. Large updates to the build process to
improve building, releasing, and maintainability.
- Go updated to 1.24.4 and all dependencies updated.
- Added FreeBSD arm64 and amd64 builds.
- Build process overhauled for simplicity. Build is now OS agnostic. PowerShell
script was removed and replaced with a python script.
- Build instructions added to README.
- GitHub build action now only runs in one Ubuntu container and cross-compiles.
- Release windows and macos as zip files and all others as gztar.
- Add file permissions for non-windows and non-macos releases.
## [v1.2.2] - 2025-04-22
All dependencies updated.
Add darwin arm64 and amd64 builds.
## [v1.2.1] - 2025-03-17
Fix time check for UPS when it is set to GMT timezone.
All dependencies updated.
## [v1.2.0] - 2025-01-27
Add a new feature to `install` that checks the time of the UPS to confirm
it is accurate. A log message is added that advises either way. Even if
the check fails, the install still proceeds with attempting to install
the new certificate.
Dependencies were also all updated.
## [v1.1.0] - 2024-09-17
> [!IMPORTANT]
> The flag `apchost` on the `install` command has been renamed to
> `hostname`. This flag should contain the hostname only. If a non-
> default SSH port is needed, specify it in the `sshport` flag.
This version brings support for for RSA 4,092 bit and EC keys. These
keys are only compatible with NMC3 running newer firmwares. To know
if your firmware is new enough, SSH into your UPS and type `ssh` and enter.
If the UPS responds `Command Not Found` the firmware is too old or
otherwise incompatible.
This version also adds a post `install` check that connects to the web
ui and verifies the certificate served is the expected one. You can
specify a non standard ssl port with the `sslport` flag or skip the check
entirely with the `skipverify` flag.
## [v1.0.0] - 2024-07-01 ## [v1.0.0] - 2024-07-01

View file

@ -53,88 +53,43 @@ This project aims to solve all of these problems by accepting the most
common key and cert file format (PEM) and by being 100% open source common key and cert file format (PEM) and by being 100% open source
and licensed under the GPL-3.0 license. and licensed under the GPL-3.0 license.
## Compatibility Notice
Both NMC2 and NMC3 devices should be fully supported. However, I have one
NMC2 device in a home lab and have no way to guarantee success in all cases.
### Key Types and Sizes ### Key Types and Sizes
Ensure you select an appropriate key!
NMC2 is extremely picky about the key type and size it supports. NMC3 is a bit
more flexible. Beware, some ACME clients will generate an ECDSA key by default
which is NOT supported by NMC2.
NMC2: NMC2:
- RSA 1,024, 2,048, 3,072* bit lengths. - RSA 1,024, 2,048, 3,072* bit lengths.
NMC3*: NMC3:
- RSA 1,024, 2,048, 3,072, and 4,092 bit lengths. - RSA 1,024, 2,048, 3,072, and 4,092 bit lengths.
- ECDSA curves P-256, P-384, and P-521. - ECDSA curves P-256, P-384, and P-521.
\* 3,072 bit length is not officially supported by my NMC2, but appears to work * 3,072 bit length is not officially supported by my NMC2, but appears to work
fine. fine.
\* The additional key types supported by NMC3 require newer firmware on the
device. I am unsure what the version cutoff is, but you can check support
by connecting to the UPS via SSH and typing `ssl`. If `Command Not Found`
is returned, the firmware is too old and only the key types listed under
NMC2 will work.
1,024 bit RSA is no longer considered completely secure; avoid keys of 1,024 bit RSA is no longer considered completely secure; avoid keys of
this size if possible. Most (all?) public ACME services won't accept keys this size if possible. Most (all?) public ACME services won't accept keys
of this size anyway. of this size anyway.
### Compatibility Notice ### General Troubleshooting
Both NMC2 and NMC3 devices should be fully supported. However, I have one
NMC2 device in a home lab and have no way to guarantee success in all cases.
My setup (and therefore the testing setup) is: My setup (and therefore the testing setup) is:
- APC Smart-UPS 1500VA RM 2U SUA1500RM2U (Firmware Revision 667.18.D) - APC Smart-UPS 1500VA RM 2U SUA1500RM2U (Firmware Revision 667.18.D)
- AP9631 NMC2 Hardware Revision 05 running AOS v7.1.2 and Boot Monitor - AP9631 NMC2 Hardware Revision 05 running AOS v7.1.2 and Boot Monitor
v1.0.9. v1.0.9.
Generally, if there is a compatibility issue, there is a good chance you will If you have trouble, your first step should be to update your NMC's firmware.
not see an error. Rather, the NMC will silently fail and you'll only know Many issues with this tool will be resolved simply by updating to the newest
something went wrong because the NMC's certificate didn't update, or it regenerated firmware.
a self-signed certificate that you'll see upon your next connection attempt.
I've tried to add some `WARNING` messages to the tool to indicate what might
be going wrong, but the list is definitely not exhaustive.
### Troubleshooting If you have a problem after that, please post the log in an issue and I can
try to fix it but it may be difficult without your particular hardware to
test with.
Suggested troubleshooting steps: In particular, if you are experiencing `ssh: handshake failed:` first try
- Review the `Key Types and Sizes` and `Compatibility Notice` sections of this
README.
- Update your NMC's firmware to the latest version.
- Read this tool's output, look specifically for any `WARNING` messages and
adjust your certificate accordingly.
- Test using an RSA 2048 bit key to obtain a certificate from Let's Encrypt.
Their certificates are known to work with NMC.
- Use the official NMC Security Wizard to verify you can create a working
certificate and load it into your NMC. If the official tool does not work
switching to this tool won't help.
If you have tried all of these steps and are still experiencing a problem,
you may open an Issue on GitHub.
Include:
- The full command you are running that is causing the problem.
- The full log of this tool's output when you run the command. Append the
`--debug` flag to your command to get the debug output.
Keep in mind, I am one person with one specific hardware setup. I may not
be able to help you.
#### NMC3 Install `ssh: parse error in message type 53` Error
Configuring a `System Message` on an NMC3 breaks the install function. I do
not have an NMC3 and after doing some code review it is highly unlikely I'll
be able to fix this. Don't use a `System Message` if the install feature is
important to you.
see: https://github.com/gregtwallace/apc-p15-tool/issues/14
#### Install `ssh: handshake failed` Error
If you are experiencing `ssh: handshake failed:` first try
using the `--insecurecipher` flag. If this works, you should upgrade your using the `--insecurecipher` flag. If this works, you should upgrade your
NMC to a newer firmware which includes secure ciphers. You should NOT automate NMC to a newer firmware which includes secure ciphers. You should NOT automate
your environment using this flag as SSH over these ciphers is broken and your environment using this flag as SSH over these ciphers is broken and
@ -185,7 +140,7 @@ disk. It instead installs the files directly on the NMC. Logic
automatically deduces if the device is an NMC2 or NMC3 and performs automatically deduces if the device is an NMC2 or NMC3 and performs
the appropriate installation steps. the appropriate installation steps.
e.g. `./apc-p15-tool install --keyfile ./apckey.pem --certfile ./apccert.pem --hostname myapc.example.com --username apc --password someSecret --fingerprint 123abc` e.g. `./apc-p15-tool install --keyfile ./apckey.pem --certfile ./apccert.pem --apchost myapc.example.com:22 --username apc --password someSecret --fingerprint 123abc`
## Note About Install Automation ## Note About Install Automation
@ -210,20 +165,6 @@ separate script.
![Cert Warden with APC P15 Tool](https://raw.githubusercontent.com/gregtwallace/apc-p15-tool/main/img/apc-p15-tool.png) ![Cert Warden with APC P15 Tool](https://raw.githubusercontent.com/gregtwallace/apc-p15-tool/main/img/apc-p15-tool.png)
## Building
Python3, Go, and git all must be installed to run the build script.
Once the dependencies are installed, clone this repo and run
`python build_release.py`. If you only want to build for certain OS or
ARCH targets, edit the `targets` array in the `build_release.py` file
before running it.
## Links
@Owl-Tec's write up using this tool with ACDS:
https://owltec.ca/Windows+Server/Deploying+An+Internal+HTTPS+Certificate+for+a+UPS+APC+with+ADCS+(Active+Directory+Certificate+Services)+with+APC+P15+Tool
## Thanks ## Thanks
Special thanks to the following people and resources which helped me Special thanks to the following people and resources which helped me

36
build.ps1 Normal file
View file

@ -0,0 +1,36 @@
# Parent dir is root
$scriptDir = Get-Location
$outDir = Join-Path -Path $scriptDir -ChildPath "/_out"
# Windows x64
$env:GOARCH = "amd64"
$env:GOOS = "windows"
$env:CGO_ENABLED = 0
go build -o $outDir/apc-p15-tool-amd64.exe ./cmd/tool
$env:GOARCH = "amd64"
$env:GOOS = "windows"
$env:CGO_ENABLED = 0
go build -o $outDir/apc-p15-install-amd64.exe ./cmd/install_only
# Linux x64
$env:GOARCH = "amd64"
$env:GOOS = "linux"
$env:CGO_ENABLED = 0
go build -o $outDir/apc-p15-tool-amd64 ./cmd/tool
$env:GOARCH = "amd64"
$env:GOOS = "linux"
$env:CGO_ENABLED = 0
go build -o $outDir/apc-p15-install-amd64 ./cmd/install_only
# Linux arm64
$env:GOARCH = "arm64"
$env:GOOS = "linux"
$env:CGO_ENABLED = 0
go build -o $outDir/apc-p15-tool-arm64 ./cmd/tool
$env:GOARCH = "arm64"
$env:GOOS = "linux"
$env:CGO_ENABLED = 0
go build -o $outDir/apc-p15-install-arm64 ./cmd/install_only

View file

@ -1,89 +0,0 @@
#!/usr/bin/env python3
import os.path
import shutil
import subprocess
import tarfile
# Configuration
# output path (relative to this script)
outRelativeDir = "_out"
# target strings must be in the format:
# `GOOS_GOARCH`
# see: https://github.com/golang/go/blob/master/src/internal/syslist/syslist.go
# or unofficially: https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63
targets = [
"windows_amd64",
"linux_amd64",
"linux_arm64",
"darwin_amd64",
"darwin_arm64",
"freebsd_amd64",
"freebsd_arm64",
]
###
# Script
# relative dir is root
scriptDir = dirname = os.path.dirname(__file__)
outBaseDir = os.path.join(scriptDir, outRelativeDir)
releaseDir = os.path.join(outBaseDir, "_release")
# recreate paths
if os.path.exists(outBaseDir):
shutil.rmtree(outBaseDir)
os.makedirs(outBaseDir)
os.makedirs(releaseDir)
# get version number / tag
gitTag = subprocess.check_output(["git", "describe", "--tags", "--abbrev=0"]).decode('utf-8').strip()
# loop through and build all targets
for target in targets:
# environment vars
split = target.split("_")
GOOS = split[0]
GOARCH = split[1]
os.environ["GOOS"] = GOOS
os.environ["GOARCH"] = GOARCH
os.environ["CGO_ENABLED"] = "0"
# send build product to GOOS_GOARCH subfolders
targetOutDir = os.path.join(outBaseDir, target)
if not os.path.exists(targetOutDir):
os.makedirs(targetOutDir)
# special case for windows to add file extensions
extension = ""
if GOOS.lower() == "windows":
extension = ".exe"
# build binary and install only binary
subprocess.run(["go", "build", "-o", f"{targetOutDir}/apc-p15-tool{extension}", "./cmd/tool"])
subprocess.run(["go", "build", "-o", f"{targetOutDir}/apc-p15-install{extension}", "./cmd/install_only"])
# copy other important files for release
shutil.copy("README.md", targetOutDir)
shutil.copy("CHANGELOG.md", targetOutDir)
shutil.copy("LICENSE.md", targetOutDir)
# compress release file
# special case for windows & mac to use zip format
if GOOS.lower() == "windows" or GOOS.lower() == "darwin":
shutil.make_archive(f"{releaseDir}/apc-p15-tool-{gitTag}_{target}", "zip", targetOutDir)
else:
# for others, use gztar and set permissions on the files
# filter for setting permissions
def set_permissions(tarinfo):
if tarinfo.name == "apc-p15-tool" or tarinfo.name == "apc-p15-install":
tarinfo.mode = 0o0755
else:
tarinfo.mode = 0o0644
return tarinfo
# make tar
with tarfile.open(f"{releaseDir}/apc-p15-tool-{gitTag}_{target}.tar.gz", "w:gz") as tar:
for file in os.listdir(targetOutDir):
tar.add(os.path.join(targetOutDir, file), arcname=file, recursive=False, filter=set_permissions)

8
go.mod
View file

@ -1,14 +1,14 @@
module apc-p15-tool module apc-p15-tool
go 1.24.4 go 1.22.4
require ( require (
github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 github.com/peterbourgon/ff/v4 v4.0.0-alpha.4
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3
golang.org/x/crypto v0.39.0 golang.org/x/crypto v0.18.0
) )
require golang.org/x/sys v0.33.0 // indirect require golang.org/x/sys v0.16.0 // indirect
replace apc-p15-tool/cmd/install_only => /cmd/install_only replace apc-p15-tool/cmd/install_only => /cmd/install_only

16
go.sum
View file

@ -2,13 +2,13 @@ github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3UO8QfUg0Z7Y= github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3UO8QfUg0Z7Y=
github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc= github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc=
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 h1:NVK+OqnavpyFmUiKfUMHrpvbCi2VFoWTrcpI7aDaJ2I= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs=
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

Binary file not shown.

Before

(image error) Size: 93 KiB

After

(image error) Size: 102 KiB

Before After
Before After

View file

@ -1,62 +0,0 @@
package apcssh
import (
"fmt"
"regexp"
"strings"
"time"
)
// GetTime sends the APC `system` command and then attempts to parse the
// response to determine the UPS current date/time.
func (cli *Client) GetTime() (time.Time, error) {
result, err := cli.cmd("date")
if err != nil {
return time.Time{}, fmt.Errorf("apcssh: failed to get time (%s)", err)
} else if !strings.EqualFold(result.code, "e000") {
return time.Time{}, fmt.Errorf("apcssh: failed to get time (%s: %s)", result.code, result.codeText)
}
// capture each portion of the date information
regex := regexp.MustCompile(`Date:\s*(\S*)\s*[\r\n]Time:\s*(\S*)\s*[\r\n]Format:\s*(\S*)\s*[\r\n]Time Zone:\s*(\S*)\s*[\r\n]?`)
datePieces := regex.FindStringSubmatch(result.resultText)
if len(datePieces) != 5 {
return time.Time{}, fmt.Errorf("apcssh: failed to get time (length of datetime value pieces was %d (expected: 5))", len(datePieces))
}
dateVal := datePieces[1]
timeVal := datePieces[2]
formatUPSVal := datePieces[3]
timeZoneVal := datePieces[4]
// GMT time requires + prefix
// APC UPS fails to use the required +, so add it
if timeZoneVal == "00:00" {
timeZoneVal = "+" + timeZoneVal
}
// known APC UPS format strings
dateFormatVal := ""
switch formatUPSVal {
case "mm/dd/yyyy":
dateFormatVal = "01/02/2006"
case "dd.mm.yyyy":
dateFormatVal = "02.01.2006"
case "mmm-dd-yy":
dateFormatVal = "Jan-02-06"
case "dd-mmm-yy":
dateFormatVal = "02-Jan-06"
case "yyyy-mm-dd":
dateFormatVal = "2006-01-02"
default:
return time.Time{}, fmt.Errorf("apcssh: failed to get time (ups returned unknown format string (%s)", formatUPSVal)
}
// convert to time.Time
t, err := time.Parse(dateFormatVal+" 15:04:05 -07:00", dateVal+" "+timeVal+" "+timeZoneVal)
if err != nil {
return time.Time{}, fmt.Errorf("apcssh: failed to get time (time parse failed: %s)", err)
}
return t, nil
}

View file

@ -12,7 +12,7 @@ import (
) )
const ( const (
appVersion = "1.3.0" appVersion = "1.1.0-b1"
) )
// struct for receivers to use common app pieces // struct for receivers to use common app pieces

View file

@ -2,18 +2,11 @@ package app
import ( import (
"apc-p15-tool/pkg/apcssh" "apc-p15-tool/pkg/apcssh"
"bytes"
"context" "context"
"crypto/tls"
"encoding/pem"
"errors" "errors"
"fmt" "fmt"
"strconv"
"time"
) )
const timeLoggingFormat = time.RFC1123Z
// cmdInstall is the app's command to create apc p15 file content from key and cert // cmdInstall is the app's command to create apc p15 file content from key and cert
// pem files and upload the p15 to the specified APC UPS // pem files and upload the p15 to the specified APC UPS
func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
@ -43,9 +36,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
} }
// host to install on must be specified // host to install on must be specified
if app.config.install.hostname == nil || *app.config.install.hostname == "" || if app.config.install.hostAndPort == nil || *app.config.install.hostAndPort == "" {
app.config.install.sshport == nil || *app.config.install.sshport == 0 {
return errors.New("install: failed, apc host not specified") return errors.New("install: failed, apc host not specified")
} }
@ -64,7 +55,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
// make APC SSH client // make APC SSH client
cfg := &apcssh.Config{ cfg := &apcssh.Config{
Hostname: *app.config.install.hostname + ":" + strconv.Itoa(*app.config.install.sshport), Hostname: *app.config.install.hostAndPort,
Username: *app.config.install.username, Username: *app.config.install.username,
Password: *app.config.install.password, Password: *app.config.install.password,
ServerFingerprint: *app.config.install.fingerprint, ServerFingerprint: *app.config.install.fingerprint,
@ -77,16 +68,6 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
} }
app.stdLogger.Println("install: connected to ups ssh, installing ssl key and cert...") app.stdLogger.Println("install: connected to ups ssh, installing ssl key and cert...")
// check time - don't fail it time is no good, just do logging here
upsT, err := client.GetTime()
if err != nil {
app.errLogger.Printf("warn: install: failed to fetch UPS time (%s), you should manually verify the time is correct on the UPS", err)
} else if upsT.After(time.Now().Add(1*time.Hour)) || upsT.Before(time.Now().Add(-1*time.Hour)) {
app.errLogger.Printf("warn: install: UPS clock skew detected (this system's time is %s vs. UPS time %s", time.Now().Format(timeLoggingFormat), upsT.Format(timeLoggingFormat))
} else {
app.stdLogger.Printf("install: UPS clock appears correct (%s)", upsT.Format(timeLoggingFormat))
}
// install SSL Cert // install SSL Cert
err = client.InstallSSLCert(keyP15, certPem, keyCertP15) err = client.InstallSSLCert(keyP15, certPem, keyCertP15)
if err != nil { if err != nil {
@ -94,7 +75,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
} }
// installed // installed
app.stdLogger.Printf("install: apc p15 file installed on %s", *app.config.install.hostname) app.stdLogger.Printf("install: apc p15 file installed on %s", *app.config.install.hostAndPort)
// restart UPS webUI // restart UPS webUI
if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI { if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI {
@ -108,48 +89,5 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error {
app.stdLogger.Println("install: sent webui restart command") app.stdLogger.Println("install: sent webui restart command")
} }
// check the new certificate is installed
if app.config.install.skipVerify != nil && !*app.config.install.skipVerify &&
app.config.install.webUISSLPort != nil && *app.config.install.webUISSLPort != 0 {
app.stdLogger.Println("install: attempting to verify certificate install...")
// sleep for UPS to finish anything it might be doing
time.Sleep(5 * time.Second)
// if UPS web UI was restarted, sleep longer
if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI {
app.stdLogger.Println("install: waiting for ups webui restart...")
time.Sleep(25 * time.Second)
}
// connect to the web UI to get the current certificate
conf := &tls.Config{
InsecureSkipVerify: true,
}
conn, err := tls.Dial("tcp", *app.config.install.hostname+":"+strconv.Itoa(*app.config.install.webUISSLPort), conf)
if err != nil {
return fmt.Errorf("install: failed to dial webui for verification (%s)", err)
}
defer conn.Close()
// get top cert
leafCert := conn.ConnectionState().PeerCertificates[0]
if leafCert == nil {
return fmt.Errorf("install: failed to get web ui leaf cert for verification (%s)", err)
}
// convert pem to DER for comparison
pemBlock, _ := pem.Decode(certPem)
// verify cert is the correct one
certVerified := bytes.Equal(leafCert.Raw, pemBlock.Bytes)
if !certVerified {
return errors.New("install: web ui leaf cert does not match new cert (your cert may not be compatible with NMC; check for WARNINGs in this tool's output)")
}
app.stdLogger.Println("install: ups web ui cert verified")
}
return nil return nil
} }

View file

@ -33,14 +33,11 @@ type config struct {
} }
install struct { install struct {
keyCertPemCfg keyCertPemCfg
hostname *string hostAndPort *string
sshport *int
fingerprint *string fingerprint *string
username *string username *string
password *string password *string
restartWebUI *bool restartWebUI *bool
webUISSLPort *int
skipVerify *bool
insecureCipher *bool insecureCipher *bool
} }
} }
@ -95,19 +92,16 @@ func (app *app) getConfig(args []string) error {
cfg.install.certPemFilePath = installFlags.StringLong("certfile", "", "path and filename of the certificate 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 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.certPem = installFlags.StringLong("certpem", "", "string of the certificate in pem format")
cfg.install.hostname = installFlags.StringLong("hostname", "", "hostname of the apc ups to install the certificate on") cfg.install.hostAndPort = installFlags.StringLong("apchost", "", "hostname:port of the apc ups to install the certificate on")
cfg.install.sshport = installFlags.IntLong("sshport", 22, "apc ups ssh port number")
cfg.install.fingerprint = installFlags.StringLong("fingerprint", "", "the SHA256 fingerprint value of the ups' ssh server") cfg.install.fingerprint = installFlags.StringLong("fingerprint", "", "the SHA256 fingerprint value of the ups' ssh server")
cfg.install.username = installFlags.StringLong("username", "", "username to login to the apc ups") cfg.install.username = installFlags.StringLong("username", "", "username to login to the apc ups")
cfg.install.password = installFlags.StringLong("password", "", "password to login to the apc ups") cfg.install.password = installFlags.StringLong("password", "", "password to login to the apc ups")
cfg.install.restartWebUI = installFlags.BoolLong("restartwebui", "some devices may need a webui restart to begin using the new cert, enabling this option sends the restart command after the p15 is installed") cfg.install.restartWebUI = installFlags.BoolLong("restartwebui", "some devices may need a webui restart to begin using the new cert, enabling this option sends the restart command after the p15 is installed")
cfg.install.webUISSLPort = installFlags.IntLong("sslport", 443, "apc ups ssl webui port number")
cfg.install.skipVerify = installFlags.BoolLong("skipverify", "the tool will try to connect to the UPS web UI to verify install success; this flag disables that check")
cfg.install.insecureCipher = installFlags.BoolLong("insecurecipher", "allows the use of insecure ssh ciphers (NOT recommended)") cfg.install.insecureCipher = installFlags.BoolLong("insecurecipher", "allows the use of insecure ssh ciphers (NOT recommended)")
installCmd := &ff.Command{ installCmd := &ff.Command{
Name: "install", Name: "install",
Usage: "apc-p15-tool install --keyfile key.pem --certfile cert.pem --hostname example.com --fingerprint 123abc --username apc --password test", Usage: "apc-p15-tool install --keyfile key.pem --certfile cert.pem --apchost example.com:22 --fingerprint 123abc --username apc --password test",
ShortHelp: "install the specified key and cert pem files on an apc ups (they will be converted to a comaptible p15 file)", ShortHelp: "install the specified key and cert pem files on an apc ups (they will be converted to a comaptible p15 file)",
Flags: installFlags, Flags: installFlags,
Exec: app.cmdInstall, Exec: app.cmdInstall,

View file

@ -2,11 +2,8 @@ package app
import ( import (
"apc-p15-tool/pkg/pkcs15" "apc-p15-tool/pkg/pkcs15"
"crypto/x509"
"encoding/asn1"
"fmt" "fmt"
"slices" "slices"
"time"
) )
// list of keys supported by the NMC2 // list of keys supported by the NMC2
@ -16,32 +13,6 @@ var nmc2SupportedKeyTypes = []pkcs15.KeyType{
pkcs15.KeyTypeRSA3072, // officially not supported but works pkcs15.KeyTypeRSA3072, // officially not supported but works
} }
// known good signing algorithms
var knownSupportedNMC2SigningAlgs = []x509.SignatureAlgorithm{
x509.SHA256WithRSA,
}
var knownSupportedNMC3SigningAlgs = append(knownSupportedNMC2SigningAlgs, []x509.SignatureAlgorithm{
x509.ECDSAWithSHA384,
}...)
// known supported cert extensions
var knownSupportedCriticalOIDs = []asn1.ObjectIdentifier{
{2, 5, 29, 15}, // keyUsage
{2, 5, 29, 19}, // basicConstraints
{2, 5, 29, 17}, // subjectAltName
}
var knownSupportedOIDs = append(knownSupportedCriticalOIDs, []asn1.ObjectIdentifier{
{2, 5, 29, 37}, // extKeyUsage
{2, 5, 29, 14}, // subjectKeyIdentifier
{2, 5, 29, 35}, // authorityKeyIdentifier
{1, 3, 6, 1, 5, 5, 7, 1, 1}, // authorityInfoAccess
{2, 5, 29, 32}, // certificatePolicies
{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}, // googleSignedCertificateTimestamp
{2, 5, 29, 31}, // cRLDistributionPoints
}...)
// pemToAPCP15 reads the specified pem files and returns the apc p15 file(s). If the // 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 // 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 // generated and nil is returned instead for that file. If the key IS supported by
@ -66,10 +37,7 @@ func (app *app) pemToAPCP15(keyPem, certPem []byte, parentCmdName string) (keyFi
app.stdLogger.Printf("%s: successfully generated p15 key file content", parentCmdName) app.stdLogger.Printf("%s: successfully generated p15 key file content", parentCmdName)
// check key type for compat with NMC2 // check key type for compat with NMC2
nmc2KeyType := false
if slices.Contains(nmc2SupportedKeyTypes, p15.KeyType()) { if slices.Contains(nmc2SupportedKeyTypes, p15.KeyType()) {
nmc2KeyType = true
app.stdLogger.Printf("%s: key type is supported by NMC2, generating p15 key+cert file content...", parentCmdName) app.stdLogger.Printf("%s: key type is supported by NMC2, generating p15 key+cert file content...", parentCmdName)
// make file bytes // make file bytes
@ -86,84 +54,11 @@ func (app *app) pemToAPCP15(keyPem, certPem []byte, parentCmdName string) (keyFi
// combine header with file // combine header with file
apcKeyCertFile = append(apcHeader, keyCertFile...) apcKeyCertFile = append(apcHeader, keyCertFile...)
}
// check various parts of cert and log compatibility warnings
warned := false
// key not supported for NMC2
if !nmc2KeyType {
app.stdLogger.Printf("WARNING: %s: key type is %s and is not supported by NMC2.", parentCmdName, p15.KeyType().String())
warned = true
}
// signature algorithm (see: https://github.com/gregtwallace/apc-p15-tool/issues/18)
if !nmc2KeyType {
// definitely not for NMC2
if !slices.Contains(knownSupportedNMC3SigningAlgs, p15.Cert.SignatureAlgorithm) {
app.stdLogger.Printf("WARNING: %s: Certificate signing algorithm is %s and it is not known if NMC3 supports this algorithm.", parentCmdName, p15.Cert.SignatureAlgorithm.String())
warned = true
}
} else { } else {
// could be for either NMC2 or NMC3 // NMC2 unsupported
if !slices.Contains(knownSupportedNMC2SigningAlgs, p15.Cert.SignatureAlgorithm) { app.stdLogger.Printf("%s: key type is not supported by NMC2, skipping p15 key+cert file content", parentCmdName)
if !slices.Contains(knownSupportedNMC3SigningAlgs, p15.Cert.SignatureAlgorithm) {
// not in NMC2 or NMC3 list
app.stdLogger.Printf("WARNING: %s: Certificate signing algorithm is %s and is not supported by NMC2. It is also not known if NMC3 supports this algorithm.", parentCmdName, p15.Cert.SignatureAlgorithm.String())
} else {
// not in NMC2 list, but is in NMC3 list
app.stdLogger.Printf("WARNING: %s: Certificate signing algorithm is %s and it does not support NMC2.", parentCmdName, p15.Cert.SignatureAlgorithm.String())
}
warned = true
}
} }
// check validity dates
if time.Now().Before(p15.Cert.NotBefore) {
app.stdLogger.Printf("WARNING: %s: Current time (%s) is before certificate's NotBefore time (%s).",
parentCmdName, time.Now().Format(timeLoggingFormat), p15.Cert.NotBefore.Format(timeLoggingFormat))
warned = true
}
if time.Now().After(p15.Cert.NotAfter) {
app.stdLogger.Printf("WARNING: %s: Current time (%s) is after certificate's NotAfter time (%s).",
parentCmdName, time.Now().Format(timeLoggingFormat), p15.Cert.NotAfter.Format(timeLoggingFormat))
warned = true
}
// check extensions against known working extensions
for _, extension := range p15.Cert.Extensions {
// critical or not?
okOIDs := knownSupportedCriticalOIDs
criticalLogMsg := "Critical "
if !extension.Critical {
okOIDs = knownSupportedOIDs
criticalLogMsg = ""
}
// validate OIDs
ok := false
for _, okOID := range okOIDs {
if okOID.Equal(extension.Id) {
ok = true
break
}
}
if !ok {
app.stdLogger.Printf("WARNING: %s: %sExtension %s may not be supported by NMC.", parentCmdName, criticalLogMsg, extension.Id.String())
}
}
// log a message about possible failure
if warned {
app.stdLogger.Printf("WARNING: %s: Possible certificate compatibility issues were detected. If the resulting p15 file "+
"does not work with your NMC (e.g., a self-signed certificate is regenerated after you try to install the p15), "+
"modify your certificate to resolve the warnings and try again.", parentCmdName)
}
// end compatibility warnings
app.stdLogger.Printf("%s: apc p15 file(s) data succesfully generated", parentCmdName) app.stdLogger.Printf("%s: apc p15 file(s) data succesfully generated", parentCmdName)
return keyFile, apcKeyCertFile, nil return keyFile, apcKeyCertFile, nil

View file

@ -25,7 +25,7 @@ const (
// params expected in the APC file. // params expected in the APC file.
func (p15 *pkcs15KeyCert) computeEncryptedKeyEnvelope() error { func (p15 *pkcs15KeyCert) computeEncryptedKeyEnvelope() error {
// if computation already performed, this is a no-op (keep existing envelope) // if computation already performed, this is a no-op (keep existing envelope)
if len(p15.envelopedPrivateKey) > 0 { if p15.envelopedPrivateKey != nil && len(p15.envelopedPrivateKey) != 0 {
return nil return nil
} }

View file

@ -15,7 +15,7 @@ func (p15 *pkcs15KeyCert) keyId() []byte {
// SHA-1 Hash // SHA-1 Hash
hasher := sha1.New() hasher := sha1.New()
_, err := hasher.Write(p15.Cert.RawSubjectPublicKeyInfo) _, err := hasher.Write(p15.cert.RawSubjectPublicKeyInfo)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -46,9 +46,9 @@ func (p15 *pkcs15KeyCert) keyIdInt3() []byte {
// object to hash // object to hash
hashObj := asn1obj.Sequence([][]byte{ hashObj := asn1obj.Sequence([][]byte{
// issuerDistinguishedName // issuerDistinguishedName
p15.Cert.RawIssuer, p15.cert.RawIssuer,
// serialNumber // serialNumber
asn1obj.Integer(p15.Cert.SerialNumber), asn1obj.Integer(p15.cert.SerialNumber),
}) })
// SHA-1 Hash // SHA-1 Hash
@ -74,7 +74,7 @@ func (p15 *pkcs15KeyCert) keyIdInt6() []byte {
// SHA-1 Hash // SHA-1 Hash
hasher := sha1.New() hasher := sha1.New()
_, err := hasher.Write(p15.Cert.RawIssuer) _, err := hasher.Write(p15.cert.RawIssuer)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -95,7 +95,7 @@ func (p15 *pkcs15KeyCert) keyIdInt7() []byte {
// SHA-1 Hash // SHA-1 Hash
hasher := sha1.New() hasher := sha1.New()
_, err := hasher.Write(p15.Cert.RawSubject) _, err := hasher.Write(p15.cert.RawSubject)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -168,7 +168,7 @@ func (p15 *pkcs15KeyCert) keyIdInt9() []byte {
// to be ~ 1 hour ish BEFORE the cert was even created. Key would also // to be ~ 1 hour ish BEFORE the cert was even created. Key would also
// obviously have to be created prior to the cert creation. // obviously have to be created prior to the cert creation.
time := make([]byte, 4) time := make([]byte, 4)
binary.BigEndian.PutUint32(time, uint32(p15.Cert.NotBefore.Unix())) binary.BigEndian.PutUint32(time, uint32(p15.cert.NotBefore.Unix()))
publicKeyPacket = append(publicKeyPacket, time...) publicKeyPacket = append(publicKeyPacket, time...)
// the next part is key type specific // the next part is key type specific

View file

@ -10,8 +10,8 @@ import (
// pkcs15KeyCert holds the data for a key and certificate pair; it provides // pkcs15KeyCert holds the data for a key and certificate pair; it provides
// various methods to transform pkcs15 data // various methods to transform pkcs15 data
type pkcs15KeyCert struct { type pkcs15KeyCert struct {
Cert *x509.Certificate
key crypto.PrivateKey key crypto.PrivateKey
cert *x509.Certificate
// store the encrypted enveloped Private Key for re-use // store the encrypted enveloped Private Key for re-use
envelopedPrivateKey []byte envelopedPrivateKey []byte
} }
@ -32,31 +32,6 @@ const (
KeyTypeUnknown KeyTypeUnknown
) )
// String returns the private key type in a log friendly string format.
func (keyType KeyType) String() string {
switch keyType {
case KeyTypeRSA1024:
return "RSA 1024-bit"
case KeyTypeRSA2048:
return "RSA 2048-bit"
case KeyTypeRSA3072:
return "RSA 3072-bit"
case KeyTypeRSA4096:
return "RSA 4096-bit"
case KeyTypeECP256:
return "ECDSA P-256"
case KeyTypeECP384:
return "ECDSA P-384"
case KeyTypeECP521:
return "ECDSA P-521"
default:
}
return "unknown key type"
}
// KeyType returns the private key type // KeyType returns the private key type
func (p15 *pkcs15KeyCert) KeyType() KeyType { func (p15 *pkcs15KeyCert) KeyType() KeyType {
switch pKey := p15.key.(type) { switch pKey := p15.key.(type) {
@ -110,7 +85,7 @@ func ParsePEMToPKCS15(keyPem, certPem []byte) (*pkcs15KeyCert, error) {
// create p15 struct // create p15 struct
p15 := &pkcs15KeyCert{ p15 := &pkcs15KeyCert{
key: key, key: key,
Cert: cert, cert: cert,
} }
// pre-calculate encrypted envelope // pre-calculate encrypted envelope

View file

@ -43,9 +43,9 @@ func (p15 *pkcs15KeyCert) ToP15KeyCert() (keyCert []byte, err error) {
// CommonKeyAttributes - accessFlags (trailing 0s will drop) // CommonKeyAttributes - accessFlags (trailing 0s will drop)
asn1obj.BitString([]byte{byte(0b10110000)}), asn1obj.BitString([]byte{byte(0b10110000)}),
// CommonKeyAttributes - startDate // CommonKeyAttributes - startDate
asn1obj.GeneralizedTime(p15.Cert.NotBefore), asn1obj.GeneralizedTime(p15.cert.NotBefore),
// CommonKeyAttributes - [0] endDate // CommonKeyAttributes - [0] endDate
asn1obj.GeneralizedTimeExplicitValue(0, p15.Cert.NotAfter), asn1obj.GeneralizedTimeExplicitValue(0, p15.cert.NotAfter),
}), }),
// ObjectValue - indirect-protected // ObjectValue - indirect-protected
asn1obj.ExplicitCompound(1, [][]byte{ asn1obj.ExplicitCompound(1, [][]byte{
@ -74,9 +74,9 @@ func (p15 *pkcs15KeyCert) ToP15KeyCert() (keyCert []byte, err error) {
// CommonKeyAttributes - accessFlags (trailing 0s will drop) // CommonKeyAttributes - accessFlags (trailing 0s will drop)
asn1obj.BitString([]byte{byte(0b10110000)}), asn1obj.BitString([]byte{byte(0b10110000)}),
// CommonKeyAttributes - startDate // CommonKeyAttributes - startDate
asn1obj.GeneralizedTime(p15.Cert.NotBefore), asn1obj.GeneralizedTime(p15.cert.NotBefore),
// CommonKeyAttributes - [0] endDate // CommonKeyAttributes - [0] endDate
asn1obj.GeneralizedTimeExplicitValue(0, p15.Cert.NotAfter), asn1obj.GeneralizedTimeExplicitValue(0, p15.cert.NotAfter),
}), }),
// ObjectValue - indirect-protected // ObjectValue - indirect-protected
asn1obj.ExplicitCompound(1, [][]byte{ asn1obj.ExplicitCompound(1, [][]byte{
@ -114,15 +114,15 @@ func (p15 *pkcs15KeyCert) ToP15KeyCert() (keyCert []byte, err error) {
p15.keyIdInt9(), p15.keyIdInt9(),
}), }),
// CommonKeyAttributes - startDate // CommonKeyAttributes - startDate
asn1obj.GeneralizedTime(p15.Cert.NotBefore), asn1obj.GeneralizedTime(p15.cert.NotBefore),
// CommonKeyAttributes - [4] endDate // CommonKeyAttributes - [4] endDate
asn1obj.GeneralizedTimeExplicitValue(4, p15.Cert.NotAfter), asn1obj.GeneralizedTimeExplicitValue(4, p15.cert.NotAfter),
}), }),
// actual certificate itself // actual certificate itself
asn1obj.ExplicitCompound(1, [][]byte{ asn1obj.ExplicitCompound(1, [][]byte{
asn1obj.Sequence([][]byte{ asn1obj.Sequence([][]byte{
asn1obj.ExplicitCompound(0, [][]byte{ asn1obj.ExplicitCompound(0, [][]byte{
p15.Cert.Raw, p15.cert.Raw,
}), }),
}), }),
}), }),