Skip to content

Commit

Permalink
specify extended key usage attributes via x509.ExtraExtensions (#292)
Browse files Browse the repository at this point in the history
* specify extended key usage attributes via x509.ExtraExtensions

Directly specifying extended key usage attributes means Critical flag
is always set to false. But as seen with timestamping - https://www.ietf.org/rfc/rfc3161.txt ,
there may be a need to set Critical flag for certain EKUs. Using x509.ExtraExtensions field
provides this flexibility. Currently all EKUs except timestamping one have Critical flag set
to false, but we can revise other EKUs or make them configurable if deemed necessary later.
  • Loading branch information
maditya authored Jul 19, 2023
1 parent d7bcf69 commit 62cf49b
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
steps:
- uses: actions/setup-go@v4
with:
go-version: 1.19
go-version: 1.20.6
- uses: actions/[email protected]
- name: golangci-lint
uses: golangci/[email protected]
2 changes: 1 addition & 1 deletion .github/workflows/license.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: 1.19
go-version: 1.20.6
- name: checkout
uses: actions/[email protected]
- name: Install addlicense
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '1.18.x', '1.19', '1.20' ]
go: [ '1.18.x', '1.19.x', '1.20.6' ]
name: Go ${{ matrix.go }} build
steps:
- name: checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: install go
uses: actions/setup-go@v4
with:
go-version: 1.19
go-version: 1.20.6

- name: get version
id: v
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/reuse.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ jobs:
steps:
- name: Checkout release
if: ${{ inputs.tag == 'release'}}
uses: actions/[email protected] # v2.4.0
uses: actions/[email protected] # v3.5.3
with:
fetch-depth: 0

- name: Checkout image
if: ${{ inputs.tag == 'image'}}
uses: actions/[email protected] # v2.4.0
uses: actions/[email protected] # v3.5.3

- name: Unshallow
if: ${{ inputs.tag == 'image'}}
Expand All @@ -58,7 +58,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4 # v2.1.5
with:
go-version: ~1.18.6
go-version: ~1.20.6

- name: Install Cosign
uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 # v3.1.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:

steps:
- name: "Checkout code"
uses: actions/[email protected] # v2.4.0
uses: actions/[email protected] # v3.5.3
with:
persist-credentials: false

Expand Down
6 changes: 3 additions & 3 deletions docker-softhsm/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ WORKDIR ${CRYPKI_DIR}
RUN go get -v ./... && go test ./... && go build -o crypki-bin ${CRYPKI_DIR}/cmd/crypki && \
go build -o gen-cacert ${CRYPKI_DIR}/cmd/gen-cacert

FROM debian:sid-slim
FROM debian:bookworm-slim
ENV CRYPKI_DIR /go/src/github.com/theparanoids/crypki
WORKDIR /opt/crypki

Expand All @@ -29,8 +29,8 @@ COPY ./docker-softhsm/init_hsm.sh /opt/crypki
COPY ./docker-softhsm/crypki.conf.sample /opt/crypki

RUN mkdir -p /var/log/crypki /opt/crypki /opt/crypki/slot_pubkeys \
&& apt-get update \
&& apt-get install -y softhsm opensc openssl \
&& apt update \
&& apt install -y softhsm2 opensc openssl \
&& /bin/bash -x /opt/crypki/init_hsm.sh

CMD ["/usr/bin/crypki-bin", "-config", "/opt/crypki/crypki-softhsm.json"]
2 changes: 1 addition & 1 deletion docker-softhsm/crypki.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"UserPinPath": "/dev/shm/slot_pwd.txt"
}
],
"ModulePath": "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so",
"ModulePath": "/usr/lib/softhsm/libsofthsm2.so",
"TLSCACertPath": "/opt/crypki/tls-crt/ca.crt",
"TLSClientAuthMode": 4,
"TLSServerCertPath": "/opt/crypki/tls-crt/server.crt",
Expand Down
4 changes: 2 additions & 2 deletions docker-softhsm/init_hsm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ user_ssh_label="user_ssh"
host_x509_label="host_x509"
host_ssh_label="host_ssh"
sign_blob_label="sign_blob"
user_ssh_keytype="EC:prime384v1"
host_x509_keytype="EC:prime384v1"
user_ssh_keytype="EC:secp256r1"
host_x509_keytype="EC:secp256r1"
host_ssh_keytype="rsa:4096"
sign_blob_keytype="rsa:4096"
user_ssh_cipher_cmd="ec"
Expand Down
1 change: 1 addition & 0 deletions docker-softhsm/x509_csr_eku.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"validity": 432000,"csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIICZjCCAU4CAQAwITELMAkGA1UEBhMCVVMxEjAQBgNVBAMMCWxvY2FsaG9zdDCC\nASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANycK6JNgdOromSB6Ym90+Ss\nr+k5to+os0sshmpgXRf/4/rbq5IYafulyrKPZ4Ymj9kuvqkpp99d7gbzndee8vQi\nxv7hEvfkCHzPbLjHkf7pNYHSFVJSpBn3ySxfq0/Ie9A6XHkFeLD3J/exFJ4Vlv4K\nQl4xher7ie5sFSYw47b0IhCzcnTaGUwbLnnhnRga7Ly4NC4zSk3KjEQYGg8QFOY0\nPlQ5Zvq3d7zldAs8MypGgxlyurz/UyorFdTQ2cF+c8o2SvpvBD9EbbmUSVh7KTZi\nduLIefRhV+VsHG8EIcM7fhLrTRUdO9KESL76GvUG77f96cU3ZeoRlMF/+jgwRoEC\nAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBY6Ff3Eb6H0AEjaz8+aum7H5MkGNeF\n8et2zVSRErPEQ5hkjB6nI9qBQ9TAyI99RdltJE5IP443bPmNLjZvRCdihC2EgDvf\n3TYEAAlHh4ihtIn7vKi9Rw9M09uAi3yJRs3zhBmN2rZvV3JmwqSdd/4dmiCaa+Y0\nTAESbwGv7fURGlsJJ6xa3/6osxb6DmbaGFOK7a1+2onHi4Xmlmmc3GawzW8Q/mXJ\nayRjLZGdwbOa5fMfWWlLtlSBaPKo/zTYuRl0idYIUFIuInJDx1ydQQQQ/opl4Qpo\nynt0uhRtvugQ+TYN0Yjp5ONK8oinmT6AEVckIfJ4aKIOCf3snUkPfQBi\n-----END CERTIFICATE REQUEST-----", "ext_key_usage": [1,2,3,4,8]}
138 changes: 127 additions & 11 deletions x509cert/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package x509cert

import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
Expand Down Expand Up @@ -43,25 +45,139 @@ func DecodeRequest(req *proto.X509CertificateSigningRequest) (*x509.Certificate,
EmailAddresses: csr.EmailAddresses,
URIs: csr.URIs,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: x509ExtKeyUsage,
ExtraExtensions: x509ExtKeyUsage,
BasicConstraintsValid: true,
}, nil
}

// getX509ExtKeyUsage returns []x509.ExtKeyUsage from []int32
func getX509ExtKeyUsage(x509ExtKeyUsages []int32) ([]x509.ExtKeyUsage, error) {
// extKeyUsageToExtension returns []pkix.Extension from []x509.ExtKeyUsage.
// This allows us to add EKU with a specific Critical bit instead of the default false value.

func extKeyUsageToExtension(extKeyUsage []x509.ExtKeyUsage) ([]pkix.Extension, error) {

var critMap = map[x509.ExtKeyUsage]bool{
x509.ExtKeyUsageAny: false,
x509.ExtKeyUsageServerAuth: false,
x509.ExtKeyUsageClientAuth: false,
x509.ExtKeyUsageCodeSigning: false,
x509.ExtKeyUsageEmailProtection: false,
x509.ExtKeyUsageIPSECEndSystem: false,
x509.ExtKeyUsageIPSECTunnel: false,
x509.ExtKeyUsageIPSECUser: false,
// https://www.ietf.org/rfc/rfc3161.txt "id-kp-timeStamping. This extension MUST be critical."
x509.ExtKeyUsageTimeStamping: true,
x509.ExtKeyUsageOCSPSigning: false,
x509.ExtKeyUsageMicrosoftServerGatedCrypto: false,
x509.ExtKeyUsageNetscapeServerGatedCrypto: false,
x509.ExtKeyUsageMicrosoftCommercialCodeSigning: false,
x509.ExtKeyUsageMicrosoftKernelCodeSigning: false,
}

// values copied from Go crypto/x509 library - https://cs.opensource.google/go/go/+/refs/tags/go1.20.6:src/crypto/x509/x509.go;l=599
var (
oidExtensionExtendedKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37}
oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5}
oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6}
oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7}
oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3}
oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1}
oidExtKeyUsageMicrosoftCommercialCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 22}
oidExtKeyUsageMicrosoftKernelCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 61, 1, 1}
)
var extCritTrue, extCritFalse []asn1.ObjectIdentifier
var extensions []pkix.Extension
for _, eku := range extKeyUsage {
var oid asn1.ObjectIdentifier
switch eku {
case x509.ExtKeyUsageAny:
oid = oidExtKeyUsageAny
case x509.ExtKeyUsageServerAuth:
oid = oidExtKeyUsageServerAuth
case x509.ExtKeyUsageClientAuth:
oid = oidExtKeyUsageClientAuth
case x509.ExtKeyUsageCodeSigning:
oid = oidExtKeyUsageCodeSigning
case x509.ExtKeyUsageEmailProtection:
oid = oidExtKeyUsageEmailProtection
case x509.ExtKeyUsageIPSECEndSystem:
oid = oidExtKeyUsageIPSECEndSystem
case x509.ExtKeyUsageIPSECTunnel:
oid = oidExtKeyUsageIPSECTunnel
case x509.ExtKeyUsageIPSECUser:
oid = oidExtKeyUsageIPSECUser
case x509.ExtKeyUsageTimeStamping:
oid = oidExtKeyUsageTimeStamping
case x509.ExtKeyUsageOCSPSigning:
oid = oidExtKeyUsageOCSPSigning
case x509.ExtKeyUsageMicrosoftServerGatedCrypto:
oid = oidExtKeyUsageMicrosoftServerGatedCrypto
case x509.ExtKeyUsageNetscapeServerGatedCrypto:
oid = oidExtKeyUsageNetscapeServerGatedCrypto
case x509.ExtKeyUsageMicrosoftCommercialCodeSigning:
oid = oidExtKeyUsageMicrosoftCommercialCodeSigning
case x509.ExtKeyUsageMicrosoftKernelCodeSigning:
oid = oidExtKeyUsageMicrosoftKernelCodeSigning
}
// Group all EKUs based on whether Critical flag is set.
// Each set forms a separate pkix.Extension.
if critMap[eku] {
extCritTrue = append(extCritTrue, oid)
} else {
extCritFalse = append(extCritFalse, oid)
}
}
if len(extCritTrue) != 0 {
oidM, err := asn1.Marshal(extCritTrue)
if err != nil {
return nil, err
}
extension := pkix.Extension{
Id: oidExtensionExtendedKeyUsage,
Critical: true,
Value: oidM,
}
extensions = append(extensions, extension)
}
if len(extCritFalse) != 0 {
oidM, err := asn1.Marshal(extCritFalse)
if err != nil {
return nil, err
}
extension := pkix.Extension{
Id: oidExtensionExtendedKeyUsage,
Critical: false,
Value: oidM,
}
extensions = append(extensions, extension)
}
return extensions, nil

}

// getX509ExtKeyUsage returns []pkix.Extension from []int32
func getX509ExtKeyUsage(x509ExtKeyUsages []int32) ([]pkix.Extension, error) {
// value of last EKU x509.ExtKeyUsageMicrosoftKernelCodeSigning
// per https://go.dev/src/crypto/x509/x509.go?s=18153:18173#L634
const maxValidEKU = 13
if len(x509ExtKeyUsages) == 0 {
return []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, nil
return extKeyUsageToExtension([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth})
}
var x509ExtKeyUsagesRet []x509.ExtKeyUsage
for _, x509ExtKeyUsage := range x509ExtKeyUsages {
// validate if extUsage falls under https://golang.org/src/crypto/x509/x509.go?s=18153:18173#L558
if x509ExtKeyUsage < 0 || x509ExtKeyUsage > 11 {
return nil, fmt.Errorf("invalid x509 ExtKeyUsage value: %d, valid values are [0,1,...11]", x509ExtKeyUsage)
ekuRet := make([]x509.ExtKeyUsage, len(x509ExtKeyUsages))
for i, eku := range x509ExtKeyUsages {
// extKeyUsage valid values - https://golang.org/src/crypto/x509/x509.go?s=18153:18173#L621
if eku < 0 || eku > maxValidEKU {
return nil, fmt.Errorf("invalid x509 ExtKeyUsage value: %d, valid values are [0,1,...13]", eku)
}
x509ExtKeyUsagesRet = append(x509ExtKeyUsagesRet, x509.ExtKeyUsage(x509ExtKeyUsage))
ekuRet[i] = x509.ExtKeyUsage(eku)
}
return x509ExtKeyUsagesRet, nil
return extKeyUsageToExtension(ekuRet)
}

// decodeCSR decodes CSR and returns x509 CertificateRequest struct.
Expand Down
Loading

0 comments on commit 62cf49b

Please sign in to comment.