Support for CPS URLs in Custom Policy Identifiers. (#15751)
* Support for CPS URLs in Custom Policy Identifiers. * go fmt * Add Changelog * Fix panic in test-cases. * Update builtin/logical/pki/path_roles.go Fix intial nil identifiers. Co-authored-by: Steven Clark <steven.clark@hashicorp.com> * Make valid policy OID so don't break ASN parse in test. * Add test cases. * go fmt. Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
This commit is contained in:
parent
f293d112e4
commit
4f532ecc4d
|
@ -3,6 +3,7 @@ package pki
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -385,7 +386,9 @@ for "generate_lease".`,
|
||||||
|
|
||||||
"policy_identifiers": {
|
"policy_identifiers": {
|
||||||
Type: framework.TypeCommaStringSlice,
|
Type: framework.TypeCommaStringSlice,
|
||||||
Description: `A comma-separated string or list of policy OIDs.`,
|
Description: `A comma-separated string or list of policy OIDs, or a JSON list of qualified policy
|
||||||
|
information, which must include an oid, and may include a notice and/or cps url, using the form
|
||||||
|
[{"oid"="1.3.6.1.4.1.7.8","notice"="I am a user Notice"}, {"oid"="1.3.6.1.4.1.44947.1.2.4 ","cps"="https://example.com"}].`,
|
||||||
},
|
},
|
||||||
|
|
||||||
"basic_constraints_valid_for_non_ca": {
|
"basic_constraints_valid_for_non_ca": {
|
||||||
|
@ -658,7 +661,7 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data
|
||||||
NoStore: data.Get("no_store").(bool),
|
NoStore: data.Get("no_store").(bool),
|
||||||
RequireCN: data.Get("require_cn").(bool),
|
RequireCN: data.Get("require_cn").(bool),
|
||||||
AllowedSerialNumbers: data.Get("allowed_serial_numbers").([]string),
|
AllowedSerialNumbers: data.Get("allowed_serial_numbers").([]string),
|
||||||
PolicyIdentifiers: data.Get("policy_identifiers").([]string),
|
PolicyIdentifiers: getPolicyIdentifier(data, nil),
|
||||||
BasicConstraintsValidForNonCA: data.Get("basic_constraints_valid_for_non_ca").(bool),
|
BasicConstraintsValidForNonCA: data.Get("basic_constraints_valid_for_non_ca").(bool),
|
||||||
NotBeforeDuration: time.Duration(data.Get("not_before_duration").(int)) * time.Second,
|
NotBeforeDuration: time.Duration(data.Get("not_before_duration").(int)) * time.Second,
|
||||||
NotAfter: data.Get("not_after").(string),
|
NotAfter: data.Get("not_after").(string),
|
||||||
|
@ -747,11 +750,9 @@ func validateRole(b *backend, entry *roleEntry, ctx context.Context, s logical.S
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(entry.PolicyIdentifiers) > 0 {
|
if len(entry.PolicyIdentifiers) > 0 {
|
||||||
for _, oidstr := range entry.PolicyIdentifiers {
|
_, err := certutil.CreatePolicyInformationExtensionFromStorageStrings(entry.PolicyIdentifiers)
|
||||||
_, err := certutil.StringToOid(oidstr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return logical.ErrorResponse(fmt.Sprintf("%q could not be parsed as a valid oid for a policy identifier", oidstr)), nil
|
return nil, err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,7 +849,7 @@ func (b *backend) pathRolePatch(ctx context.Context, req *logical.Request, data
|
||||||
NoStore: getWithExplicitDefault(data, "no_store", oldEntry.NoStore).(bool),
|
NoStore: getWithExplicitDefault(data, "no_store", oldEntry.NoStore).(bool),
|
||||||
RequireCN: getWithExplicitDefault(data, "require_cn", oldEntry.RequireCN).(bool),
|
RequireCN: getWithExplicitDefault(data, "require_cn", oldEntry.RequireCN).(bool),
|
||||||
AllowedSerialNumbers: getWithExplicitDefault(data, "allowed_serial_numbers", oldEntry.AllowedSerialNumbers).([]string),
|
AllowedSerialNumbers: getWithExplicitDefault(data, "allowed_serial_numbers", oldEntry.AllowedSerialNumbers).([]string),
|
||||||
PolicyIdentifiers: getWithExplicitDefault(data, "policy_identifiers", oldEntry.PolicyIdentifiers).([]string),
|
PolicyIdentifiers: getPolicyIdentifier(data, &oldEntry.PolicyIdentifiers),
|
||||||
BasicConstraintsValidForNonCA: getWithExplicitDefault(data, "basic_constraints_valid_for_non_ca", oldEntry.BasicConstraintsValidForNonCA).(bool),
|
BasicConstraintsValidForNonCA: getWithExplicitDefault(data, "basic_constraints_valid_for_non_ca", oldEntry.BasicConstraintsValidForNonCA).(bool),
|
||||||
NotBeforeDuration: getTimeWithExplicitDefault(data, "not_before_duration", oldEntry.NotBeforeDuration),
|
NotBeforeDuration: getTimeWithExplicitDefault(data, "not_before_duration", oldEntry.NotBeforeDuration),
|
||||||
NotAfter: getWithExplicitDefault(data, "not_after", oldEntry.NotAfter).(string),
|
NotAfter: getWithExplicitDefault(data, "not_after", oldEntry.NotAfter).(string),
|
||||||
|
@ -1116,3 +1117,45 @@ const pathListRolesHelpDesc = `Roles will be listed by the role name.`
|
||||||
const pathRoleHelpSyn = `Manage the roles that can be created with this backend.`
|
const pathRoleHelpSyn = `Manage the roles that can be created with this backend.`
|
||||||
|
|
||||||
const pathRoleHelpDesc = `This path lets you manage the roles that can be created with this backend.`
|
const pathRoleHelpDesc = `This path lets you manage the roles that can be created with this backend.`
|
||||||
|
|
||||||
|
const policyIdentifiersParam = "policy_identifiers"
|
||||||
|
|
||||||
|
func getPolicyIdentifier(data *framework.FieldData, defaultIdentifiers *[]string) []string {
|
||||||
|
policyIdentifierEntry, ok := data.GetOk(policyIdentifiersParam)
|
||||||
|
if !ok {
|
||||||
|
// No Entry for policy_identifiers
|
||||||
|
if defaultIdentifiers != nil {
|
||||||
|
return *defaultIdentifiers
|
||||||
|
}
|
||||||
|
return data.Get(policyIdentifiersParam).([]string)
|
||||||
|
}
|
||||||
|
// Could Be A JSON Entry
|
||||||
|
policyIdentifierJsonEntry := data.Raw[policyIdentifiersParam]
|
||||||
|
policyIdentifierJsonString, ok := policyIdentifierJsonEntry.(string)
|
||||||
|
if ok {
|
||||||
|
policyIdentifiers, err := parsePolicyIdentifiersFromJson(policyIdentifierJsonString)
|
||||||
|
if err == nil {
|
||||||
|
return policyIdentifiers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Else could Just Be A List of OIDs
|
||||||
|
return policyIdentifierEntry.([]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePolicyIdentifiersFromJson(policyIdentifiers string) ([]string, error) {
|
||||||
|
var entries []certutil.PolicyIdentifierWithQualifierEntry
|
||||||
|
var policyIdentifierList []string
|
||||||
|
err := json.Unmarshal([]byte(policyIdentifiers), &entries)
|
||||||
|
if err != nil {
|
||||||
|
return policyIdentifierList, err
|
||||||
|
}
|
||||||
|
policyIdentifierList = make([]string, 0, len(entries))
|
||||||
|
for _, entry := range entries {
|
||||||
|
policyString, err := json.Marshal(entry)
|
||||||
|
if err != nil {
|
||||||
|
return policyIdentifierList, err
|
||||||
|
}
|
||||||
|
policyIdentifierList = append(policyIdentifierList, string(policyString))
|
||||||
|
}
|
||||||
|
return policyIdentifierList, nil
|
||||||
|
}
|
||||||
|
|
|
@ -2,13 +2,19 @@ package pki
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-errors/errors"
|
||||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -928,8 +934,8 @@ func TestPki_RolePatch(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Field: "policy_identifiers",
|
Field: "policy_identifiers",
|
||||||
Before: []string{"1.2.3.4.5"},
|
Before: []string{"1.3.6.1.4.1.1.1"},
|
||||||
Patched: []string{"5.4.3.2.1"},
|
Patched: []string{"1.3.6.1.4.1.1.2"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Field: "basic_constraints_valid_for_non_ca",
|
Field: "basic_constraints_valid_for_non_ca",
|
||||||
|
@ -1019,3 +1025,141 @@ func TestPki_RolePatch(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPKI_RolePolicyInformation_Flat(t *testing.T) {
|
||||||
|
type TestCase struct {
|
||||||
|
Input interface{}
|
||||||
|
ASN interface{}
|
||||||
|
OidList []string
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedSimpleAsnExtension := "MBYwCQYHKwYBBAEBATAJBgcrBgEEAQEC"
|
||||||
|
expectedSimpleOidList := append(*new([]string), "1.3.6.1.4.1.1.1", "1.3.6.1.4.1.1.2")
|
||||||
|
|
||||||
|
testCases := []TestCase{
|
||||||
|
{
|
||||||
|
Input: "1.3.6.1.4.1.1.1,1.3.6.1.4.1.1.2",
|
||||||
|
ASN: expectedSimpleAsnExtension,
|
||||||
|
OidList: expectedSimpleOidList,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "[{\"oid\":\"1.3.6.1.4.1.1.1\"},{\"oid\":\"1.3.6.1.4.1.1.2\"}]",
|
||||||
|
ASN: expectedSimpleAsnExtension,
|
||||||
|
OidList: expectedSimpleOidList,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "[{\"oid\":\"1.3.6.1.4.1.7.8\",\"notice\":\"I am a user Notice\"},{\"oid\":\"1.3.6.1.44947.1.2.4\",\"cps\":\"https://example.com\"}]",
|
||||||
|
ASN: "MF8wLQYHKwYBBAEHCDAiMCAGCCsGAQUFBwICMBQMEkkgYW0gYSB1c2VyIE5vdGljZTAuBgkrBgGC3xMBAgQwITAfBggrBgEFBQcCARYTaHR0cHM6Ly9leGFtcGxlLmNvbQ==",
|
||||||
|
OidList: append(*new([]string), "1.3.6.1.4.1.7.8", "1.3.6.1.44947.1.2.4"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b, storage := createBackendWithStorage(t)
|
||||||
|
|
||||||
|
caData := map[string]interface{}{
|
||||||
|
"common_name": "myvault.com",
|
||||||
|
"ttl": "5h",
|
||||||
|
"ip_sans": "127.0.0.1",
|
||||||
|
}
|
||||||
|
caReq := &logical.Request{
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Path: "root/generate/internal",
|
||||||
|
Storage: storage,
|
||||||
|
Data: caData,
|
||||||
|
}
|
||||||
|
caResp, err := b.HandleRequest(context.Background(), caReq)
|
||||||
|
if err != nil || (caResp != nil && caResp.IsError()) {
|
||||||
|
t.Fatalf("bad: err: %v resp: %#v", err, caResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, testCase := range testCases {
|
||||||
|
var roleResp *logical.Response
|
||||||
|
var issueResp *logical.Response
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Create/update the role
|
||||||
|
roleData := map[string]interface{}{}
|
||||||
|
roleData[policyIdentifiersParam] = testCase.Input
|
||||||
|
|
||||||
|
roleReq := &logical.Request{
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Path: "roles/testrole",
|
||||||
|
Storage: storage,
|
||||||
|
Data: roleData,
|
||||||
|
}
|
||||||
|
|
||||||
|
roleResp, err = b.HandleRequest(context.Background(), roleReq)
|
||||||
|
if err != nil || (roleResp != nil && roleResp.IsError()) {
|
||||||
|
t.Fatalf("bad [%d], setting policy identifier %v err: %v resp: %#v", index, testCase.Input, err, roleResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue Using this role
|
||||||
|
issueData := map[string]interface{}{}
|
||||||
|
issueData["common_name"] = "localhost"
|
||||||
|
issueData["ttl"] = "2s"
|
||||||
|
|
||||||
|
issueReq := &logical.Request{
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Path: "issue/testrole",
|
||||||
|
Storage: storage,
|
||||||
|
Data: issueData,
|
||||||
|
}
|
||||||
|
|
||||||
|
issueResp, err = b.HandleRequest(context.Background(), issueReq)
|
||||||
|
if err != nil || (issueResp != nil && issueResp.IsError()) {
|
||||||
|
t.Fatalf("bad [%d], setting policy identifier %v err: %v resp: %#v", index, testCase.Input, err, issueResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the OIDs
|
||||||
|
policyIdentifiers, err := getPolicyIdentifiersOffCertificate(*issueResp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("bad [%d], getting policy identifier from %v err: %v resp: %#v", index, testCase.Input, err, issueResp)
|
||||||
|
}
|
||||||
|
if len(policyIdentifiers) != len(testCase.OidList) {
|
||||||
|
t.Fatalf("bad [%d], wrong certificate policy identifier from %v len expected: %d got %d", index, testCase.Input, len(testCase.OidList), len(policyIdentifiers))
|
||||||
|
}
|
||||||
|
for i, identifier := range policyIdentifiers {
|
||||||
|
if identifier != testCase.OidList[i] {
|
||||||
|
t.Fatalf("bad [%d], wrong certificate policy identifier from %v expected: %v got %v", index, testCase.Input, testCase.OidList[i], policyIdentifiers[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Validate the ASN
|
||||||
|
certificateAsn, err := getPolicyInformationExtensionOffCertificate(*issueResp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("bad [%d], getting extension from %v err: %v resp: %#v", index, testCase.Input, err, issueResp)
|
||||||
|
}
|
||||||
|
certificateB64 := make([]byte, len(certificateAsn)*2)
|
||||||
|
base64.StdEncoding.Encode(certificateB64, certificateAsn)
|
||||||
|
certificateString := string(certificateB64[:])
|
||||||
|
assert.Contains(t, certificateString, testCase.ASN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPolicyIdentifiersOffCertificate(resp logical.Response) ([]string, error) {
|
||||||
|
stringCertificate := resp.Data["certificate"].(string)
|
||||||
|
block, _ := pem.Decode([]byte(stringCertificate))
|
||||||
|
certificate, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
policyIdentifierStrings := make([]string, len(certificate.PolicyIdentifiers))
|
||||||
|
for index, asnOid := range certificate.PolicyIdentifiers {
|
||||||
|
policyIdentifierStrings[index] = asnOid.String()
|
||||||
|
}
|
||||||
|
return policyIdentifierStrings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPolicyInformationExtensionOffCertificate(resp logical.Response) ([]byte, error) {
|
||||||
|
stringCertificate := resp.Data["certificate"].(string)
|
||||||
|
block, _ := pem.Decode([]byte(stringCertificate))
|
||||||
|
certificate, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, extension := range certificate.Extensions {
|
||||||
|
if extension.Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 32}) {
|
||||||
|
return extension.Value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *new([]byte), errors.New("No Policy Information Extension Found")
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
secrets/pki: Add support for CPS URLs and User Notice to Policy Information
|
||||||
|
```
|
|
@ -446,18 +446,30 @@ func ParsePublicKeyPEM(data []byte) (interface{}, error) {
|
||||||
return nil, errors.New("data does not contain any valid public keys")
|
return nil, errors.New("data does not contain any valid public keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
// addPolicyIdentifiers adds certificate policies extension
|
// AddPolicyIdentifiers adds certificate policies extension, based on CreationBundle
|
||||||
//
|
|
||||||
func AddPolicyIdentifiers(data *CreationBundle, certTemplate *x509.Certificate) {
|
func AddPolicyIdentifiers(data *CreationBundle, certTemplate *x509.Certificate) {
|
||||||
for _, oidstr := range data.Params.PolicyIdentifiers {
|
oidOnly := true
|
||||||
oid, err := StringToOid(oidstr)
|
for _, oidStr := range data.Params.PolicyIdentifiers {
|
||||||
|
oid, err := StringToOid(oidStr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
certTemplate.PolicyIdentifiers = append(certTemplate.PolicyIdentifiers, oid)
|
certTemplate.PolicyIdentifiers = append(certTemplate.PolicyIdentifiers, oid)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
oidOnly = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !oidOnly { // Because all policy information is held in the same extension, when we use an extra extension to
|
||||||
|
// add policy qualifier information, that overwrites any information in the PolicyIdentifiers field on the Cert
|
||||||
|
// Template, so we need to reparse all the policy identifiers here
|
||||||
|
extension, err := CreatePolicyInformationExtensionFromStorageStrings(data.Params.PolicyIdentifiers)
|
||||||
|
if err == nil {
|
||||||
|
// If this errors out, don't add it, rely on the OIDs parsed into PolicyIdentifiers above
|
||||||
|
certTemplate.ExtraExtensions = append(certTemplate.ExtraExtensions, *extension)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// addExtKeyUsageOids adds custom extended key usage OIDs to certificate
|
// AddExtKeyUsageOids adds custom extended key usage OIDs to certificate
|
||||||
func AddExtKeyUsageOids(data *CreationBundle, certTemplate *x509.Certificate) {
|
func AddExtKeyUsageOids(data *CreationBundle, certTemplate *x509.Certificate) {
|
||||||
for _, oidstr := range data.Params.ExtKeyUsageOIDs {
|
for _, oidstr := range data.Params.ExtKeyUsageOIDs {
|
||||||
oid, err := StringToOid(oidstr)
|
oid, err := StringToOid(oidstr)
|
||||||
|
|
|
@ -17,7 +17,10 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
@ -894,3 +897,114 @@ func (p *KeyBundle) ToPrivateKeyPemString() (string, error) {
|
||||||
|
|
||||||
return "", errutil.InternalError{Err: "No Private Key Bytes to Wrap"}
|
return "", errutil.InternalError{Err: "No Private Key Bytes to Wrap"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PolicyIdentifierWithQualifierEntry Structure for Internal Storage
|
||||||
|
type PolicyIdentifierWithQualifierEntry struct {
|
||||||
|
PolicyIdentifierOid string `json:"oid",mapstructure:"oid"`
|
||||||
|
CPS string `json:"cps,omitempty",mapstructure:"cps"`
|
||||||
|
Notice string `json:"notice,omitempty",mapstructure:"notice"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPolicyIdentifierFromString parses out the internal structure of a Policy Identifier
|
||||||
|
func GetPolicyIdentifierFromString(policyIdentifier string) (*PolicyIdentifierWithQualifierEntry, error) {
|
||||||
|
if policyIdentifier == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
entry := &PolicyIdentifierWithQualifierEntry{}
|
||||||
|
// Either a OID, or a JSON Entry: First check OID:
|
||||||
|
_, err := StringToOid(policyIdentifier)
|
||||||
|
if err == nil {
|
||||||
|
entry.PolicyIdentifierOid = policyIdentifier
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
// Now Check If JSON Entry
|
||||||
|
jsonErr := json.Unmarshal([]byte(policyIdentifier), &entry)
|
||||||
|
if jsonErr != nil { // Neither, if we got here
|
||||||
|
return entry, errors.New(fmt.Sprintf("Policy Identifier %q is neither a valid OID: %s, Nor JSON Policy Identifier: %s", policyIdentifier, err.Error(), jsonErr.Error()))
|
||||||
|
}
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Policy Identifier with Qualifier Structure for ASN Marshalling:
|
||||||
|
|
||||||
|
var policyInformationOid = asn1.ObjectIdentifier{2, 5, 29, 32}
|
||||||
|
|
||||||
|
type policyInformation struct {
|
||||||
|
PolicyIdentifier asn1.ObjectIdentifier
|
||||||
|
Qualifiers []interface{} `asn1:"tag:optional,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var cpsPolicyQualifierID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 1}
|
||||||
|
|
||||||
|
type cpsUrlPolicyQualifier struct {
|
||||||
|
PolicyQualifierID asn1.ObjectIdentifier
|
||||||
|
Qualifier string `asn1:"tag:optional,ia5"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var userNoticePolicyQualifierID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 2}
|
||||||
|
|
||||||
|
type userNoticePolicyQualifier struct {
|
||||||
|
PolicyQualifierID asn1.ObjectIdentifier
|
||||||
|
Qualifier userNotice
|
||||||
|
}
|
||||||
|
|
||||||
|
type userNotice struct {
|
||||||
|
ExplicitText string `asn1:"tag:optional,utf8"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPolicyIdentifierWithQualifier(entry PolicyIdentifierWithQualifierEntry) (*policyInformation, error) {
|
||||||
|
// Each Policy is Identified by a Unique ID, as designated here:
|
||||||
|
policyOid, err := StringToOid(entry.PolicyIdentifierOid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pi := policyInformation{
|
||||||
|
PolicyIdentifier: policyOid,
|
||||||
|
}
|
||||||
|
if entry.CPS != "" {
|
||||||
|
qualifier := cpsUrlPolicyQualifier{
|
||||||
|
PolicyQualifierID: cpsPolicyQualifierID,
|
||||||
|
Qualifier: entry.CPS,
|
||||||
|
}
|
||||||
|
pi.Qualifiers = append(pi.Qualifiers, qualifier)
|
||||||
|
}
|
||||||
|
if entry.Notice != "" {
|
||||||
|
qualifier := userNoticePolicyQualifier{
|
||||||
|
PolicyQualifierID: userNoticePolicyQualifierID,
|
||||||
|
Qualifier: userNotice{
|
||||||
|
ExplicitText: entry.Notice,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pi.Qualifiers = append(pi.Qualifiers, qualifier)
|
||||||
|
}
|
||||||
|
return &pi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePolicyInformationExtensionFromStorageStrings parses the stored policyIdentifiers, which might be JSON Policy
|
||||||
|
// Identifier with Qualifier Entries or String OIDs, and returns an extension if everything parsed correctly, and an
|
||||||
|
// error if constructing
|
||||||
|
func CreatePolicyInformationExtensionFromStorageStrings(policyIdentifiers []string) (*pkix.Extension, error) {
|
||||||
|
var policyInformationList []policyInformation
|
||||||
|
for _, policyIdentifierStr := range policyIdentifiers {
|
||||||
|
policyIdentifierEntry, err := GetPolicyIdentifierFromString(policyIdentifierStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if policyIdentifierEntry != nil { // Okay to skip empty entries if there is no error
|
||||||
|
policyInformationStruct, err := createPolicyIdentifierWithQualifier(*policyIdentifierEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
policyInformationList = append(policyInformationList, *policyInformationStruct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asn1Bytes, err := asn1.Marshal(policyInformationList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pkix.Extension{
|
||||||
|
Id: policyInformationOid,
|
||||||
|
Critical: false,
|
||||||
|
Value: asn1Bytes,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue