mirror of
https://github.com/emmansun/gmsm.git
synced 2025-06-28 16:27:51 +08:00
smx509: add new OID type and use it in Certificate #209
This commit is contained in:
parent
4abeaf929a
commit
f14097864c
87
smx509/oid.go
Normal file
87
smx509/oid.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package smx509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setDer(oid *x509.OID, der []byte) {
|
||||||
|
oidValue := reflect.ValueOf(oid).Elem()
|
||||||
|
derField := oidValue.FieldByName("der")
|
||||||
|
|
||||||
|
derPointer := (*[]byte)(unsafe.Pointer(derField.UnsafeAddr()))
|
||||||
|
*derPointer = der
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDer(oid *x509.OID) []byte {
|
||||||
|
oidValue := reflect.ValueOf(oid).Elem()
|
||||||
|
derField := oidValue.FieldByName("der")
|
||||||
|
|
||||||
|
derPointer := (*[]byte)(unsafe.Pointer(derField.UnsafeAddr()))
|
||||||
|
return *derPointer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOIDFromDER(der []byte) (x509.OID, bool) {
|
||||||
|
if len(der) == 0 || der[len(der)-1]&0x80 != 0 {
|
||||||
|
return x509.OID{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
start := 0
|
||||||
|
for i, v := range der {
|
||||||
|
// ITU-T X.690, section 8.19.2:
|
||||||
|
// The subidentifier shall be encoded in the fewest possible octets,
|
||||||
|
// that is, the leading octet of the subidentifier shall not have the value 0x80.
|
||||||
|
if i == start && v == 0x80 {
|
||||||
|
return x509.OID{}, false
|
||||||
|
}
|
||||||
|
if v&0x80 == 0 {
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oid := x509.OID{}
|
||||||
|
setDer(&oid, der)
|
||||||
|
return oid, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func toASN1OID(oid x509.OID) (asn1.ObjectIdentifier, bool) {
|
||||||
|
der := getDer(&oid)
|
||||||
|
out := make([]int, 0, len(der)+1)
|
||||||
|
|
||||||
|
const (
|
||||||
|
valSize = 31 // amount of usable bits of val for OIDs.
|
||||||
|
bitsPerByte = 7
|
||||||
|
maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
val := 0
|
||||||
|
|
||||||
|
for _, v := range der {
|
||||||
|
if val > maxValSafeShift {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
val <<= bitsPerByte
|
||||||
|
val |= int(v & 0x7F)
|
||||||
|
|
||||||
|
if v&0x80 == 0 {
|
||||||
|
if len(out) == 0 {
|
||||||
|
if val < 80 {
|
||||||
|
out = append(out, val/40)
|
||||||
|
out = append(out, val%40)
|
||||||
|
} else {
|
||||||
|
out = append(out, 2)
|
||||||
|
out = append(out, val-80)
|
||||||
|
}
|
||||||
|
val = 0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, val)
|
||||||
|
val = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, true
|
||||||
|
}
|
107
smx509/oid_test.go
Normal file
107
smx509/oid_test.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package smx509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOID(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
raw []byte
|
||||||
|
valid bool
|
||||||
|
str string
|
||||||
|
ints []uint64
|
||||||
|
}{
|
||||||
|
{[]byte{}, false, "", nil},
|
||||||
|
{[]byte{0x80, 0x01}, false, "", nil},
|
||||||
|
{[]byte{0x01, 0x80, 0x01}, false, "", nil},
|
||||||
|
|
||||||
|
{[]byte{1, 2, 3}, true, "0.1.2.3", []uint64{0, 1, 2, 3}},
|
||||||
|
{[]byte{41, 2, 3}, true, "1.1.2.3", []uint64{1, 1, 2, 3}},
|
||||||
|
{[]byte{86, 2, 3}, true, "2.6.2.3", []uint64{2, 6, 2, 3}},
|
||||||
|
|
||||||
|
{[]byte{41, 255, 255, 255, 127}, true, "1.1.268435455", []uint64{1, 1, 268435455}},
|
||||||
|
{[]byte{41, 0x87, 255, 255, 255, 127}, true, "1.1.2147483647", []uint64{1, 1, 2147483647}},
|
||||||
|
{[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367", []uint64{1, 1, 34359738367}},
|
||||||
|
{[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807", []uint64{1, 2, 9223372036854775807}},
|
||||||
|
{[]byte{43, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.3.18446744073709551615", []uint64{1, 3, 18446744073709551615}},
|
||||||
|
{[]byte{44, 0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.4.36893488147419103231", nil},
|
||||||
|
{[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.1180591620717411303423", nil},
|
||||||
|
{[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.19342813113834066795298815", nil},
|
||||||
|
|
||||||
|
{[]byte{255, 255, 255, 127}, true, "2.268435375", []uint64{2, 268435375}},
|
||||||
|
{[]byte{0x87, 255, 255, 255, 127}, true, "2.2147483567", []uint64{2, 2147483567}},
|
||||||
|
{[]byte{255, 127}, true, "2.16303", []uint64{2, 16303}},
|
||||||
|
{[]byte{255, 255, 255, 255, 127}, true, "2.34359738287", []uint64{2, 34359738287}},
|
||||||
|
{[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727", []uint64{2, 9223372036854775727}},
|
||||||
|
{[]byte{0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.18446744073709551535", []uint64{2, 18446744073709551535}},
|
||||||
|
{[]byte{0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.36893488147419103151", nil},
|
||||||
|
{[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.1180591620717411303343", nil},
|
||||||
|
{[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.19342813113834066795298735", nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range tests {
|
||||||
|
oid, ok := newOIDFromDER(v.raw)
|
||||||
|
if ok != v.valid {
|
||||||
|
if ok {
|
||||||
|
t.Errorf("%v: unexpected success while parsing: %v", v.raw, oid)
|
||||||
|
} else {
|
||||||
|
t.Errorf("%v: unexpected failure while parsing", v.raw)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if str := oid.String(); str != v.str {
|
||||||
|
t.Errorf("%v: oid.String() = %v, want; %v", v.raw, str, v.str)
|
||||||
|
}
|
||||||
|
|
||||||
|
var asn1OID asn1.ObjectIdentifier
|
||||||
|
for _, v := range v.ints {
|
||||||
|
if v > math.MaxInt32 {
|
||||||
|
asn1OID = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
asn1OID = append(asn1OID, int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
o, ok := toASN1OID(oid)
|
||||||
|
if shouldOk := asn1OID != nil; shouldOk != ok {
|
||||||
|
if ok {
|
||||||
|
t.Errorf("%v: oid.toASN1OID() unexpected success", v.raw)
|
||||||
|
} else {
|
||||||
|
t.Errorf("%v: oid.toASN1OID() unexpected fauilure", v.raw)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if asn1OID != nil {
|
||||||
|
if !o.Equal(asn1OID) {
|
||||||
|
t.Errorf("%v: oid.toASN1OID(asn1OID).Equal(oid) = false, want: true", v.raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.ints != nil {
|
||||||
|
oid2, err := x509.OIDFromInts(v.ints)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v: OIDFromInts() unexpected error: %v", v.raw, err)
|
||||||
|
}
|
||||||
|
if !oid2.Equal(oid) {
|
||||||
|
t.Errorf("%v: %#v.Equal(%#v) = false, want: true", v.raw, oid2, oid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustNewOIDFromInts(t *testing.T, ints []uint64) x509.OID {
|
||||||
|
oid, err := x509.OIDFromInts(ints)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OIDFromInts(%v) unexpected error: %v", ints, err)
|
||||||
|
}
|
||||||
|
return oid
|
||||||
|
}
|
@ -549,18 +549,19 @@ func parseExtKeyUsageExtension(der cryptobyte.String) ([]ExtKeyUsage, []asn1.Obj
|
|||||||
return extKeyUsages, unknownUsages, nil
|
return extKeyUsages, unknownUsages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCertificatePoliciesExtension(der cryptobyte.String) ([]asn1.ObjectIdentifier, error) {
|
func parseCertificatePoliciesExtension(der cryptobyte.String) ([]x509.OID, error) {
|
||||||
var oids []asn1.ObjectIdentifier
|
var oids []x509.OID
|
||||||
if !der.ReadASN1(&der, cryptobyte_asn1.SEQUENCE) {
|
if !der.ReadASN1(&der, cryptobyte_asn1.SEQUENCE) {
|
||||||
return nil, errors.New("x509: invalid certificate policies")
|
return nil, errors.New("x509: invalid certificate policies")
|
||||||
}
|
}
|
||||||
for !der.Empty() {
|
for !der.Empty() {
|
||||||
var cp cryptobyte.String
|
var cp cryptobyte.String
|
||||||
if !der.ReadASN1(&cp, cryptobyte_asn1.SEQUENCE) {
|
var OIDBytes cryptobyte.String
|
||||||
|
if !der.ReadASN1(&cp, cryptobyte_asn1.SEQUENCE) || !cp.ReadASN1(&OIDBytes, cryptobyte_asn1.OBJECT_IDENTIFIER) {
|
||||||
return nil, errors.New("x509: invalid certificate policies")
|
return nil, errors.New("x509: invalid certificate policies")
|
||||||
}
|
}
|
||||||
var oid asn1.ObjectIdentifier
|
oid, ok := newOIDFromDER(OIDBytes)
|
||||||
if !cp.ReadASN1ObjectIdentifier(&oid) {
|
if !ok {
|
||||||
return nil, errors.New("x509: invalid certificate policies")
|
return nil, errors.New("x509: invalid certificate policies")
|
||||||
}
|
}
|
||||||
oids = append(oids, oid)
|
oids = append(oids, oid)
|
||||||
@ -870,10 +871,16 @@ func processExtensions(out *Certificate) error {
|
|||||||
}
|
}
|
||||||
out.SubjectKeyId = skid
|
out.SubjectKeyId = skid
|
||||||
case 32:
|
case 32:
|
||||||
out.PolicyIdentifiers, err = parseCertificatePoliciesExtension(e.Value)
|
out.Policies, err = parseCertificatePoliciesExtension(e.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, 0, len(out.Policies))
|
||||||
|
for _, oid := range out.Policies {
|
||||||
|
if oid, ok := toASN1OID(oid); ok {
|
||||||
|
out.PolicyIdentifiers = append(out.PolicyIdentifiers, oid)
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// Unknown extensions are recorded if critical.
|
// Unknown extensions are recorded if critical.
|
||||||
unhandled = true
|
unhandled = true
|
||||||
|
@ -1149,7 +1149,7 @@ func buildCertExtensions(template *x509.Certificate, subjectIsEmpty bool, author
|
|||||||
|
|
||||||
if len(template.PolicyIdentifiers) > 0 &&
|
if len(template.PolicyIdentifiers) > 0 &&
|
||||||
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
|
!oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) {
|
||||||
ret[n], err = marshalCertificatePolicies(template.PolicyIdentifiers)
|
ret[n], err = marshalCertificatePolicies(template.Policies, template.PolicyIdentifiers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1334,14 +1334,27 @@ func marshalBasicConstraints(isCA bool, maxPathLen int, maxPathLenZero bool) (pk
|
|||||||
return ext, err
|
return ext, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalCertificatePolicies(policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) {
|
func marshalCertificatePolicies(policies []x509.OID, policyIdentifiers []asn1.ObjectIdentifier) (pkix.Extension, error) {
|
||||||
ext := pkix.Extension{Id: oidExtensionCertificatePolicies}
|
ext := pkix.Extension{Id: oidExtensionCertificatePolicies}
|
||||||
policies := make([]policyInformation, len(policyIdentifiers))
|
|
||||||
for i, policy := range policyIdentifiers {
|
b := cryptobyte.NewBuilder(make([]byte, 0, 128))
|
||||||
policies[i].Policy = policy
|
b.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
|
||||||
}
|
for _, v := range policies {
|
||||||
|
child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
|
||||||
|
child.AddASN1(cryptobyte_asn1.OBJECT_IDENTIFIER, func(child *cryptobyte.Builder) {
|
||||||
|
child.AddBytes(getDer(&v))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, v := range policyIdentifiers {
|
||||||
|
child.AddASN1(cryptobyte_asn1.SEQUENCE, func(child *cryptobyte.Builder) {
|
||||||
|
child.AddASN1ObjectIdentifier(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ext.Value, err = asn1.Marshal(policies)
|
ext.Value, err = b.Bytes()
|
||||||
return ext, err
|
return ext, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,11 +17,13 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -490,6 +492,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
|
|||||||
URIs: []*url.URL{parseURI("https://foo.com/wibble#foo")},
|
URIs: []*url.URL{parseURI("https://foo.com/wibble#foo")},
|
||||||
|
|
||||||
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
|
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
|
||||||
|
Policies: []x509.OID{mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxUint32, math.MaxUint64})},
|
||||||
PermittedDNSDomains: []string{".example.com", "example.com"},
|
PermittedDNSDomains: []string{".example.com", "example.com"},
|
||||||
ExcludedDNSDomains: []string{"bar.example.com"},
|
ExcludedDNSDomains: []string{"bar.example.com"},
|
||||||
PermittedIPRanges: []*net.IPNet{parseCIDR("192.168.1.1/16"), parseCIDR("1.2.3.4/8")},
|
PermittedIPRanges: []*net.IPNet{parseCIDR("192.168.1.1/16"), parseCIDR("1.2.3.4/8")},
|
||||||
@ -3679,3 +3682,49 @@ func TestCreateCertificateNegativeMaxPathLength(t *testing.T) {
|
|||||||
t.Fatalf(`CreateCertificate() = %v; want = "x509: invalid MaxPathLen, must be greater or equal to -1"`, err)
|
t.Fatalf(`CreateCertificate() = %v; want = "x509: invalid MaxPathLen, must be greater or equal to -1"`, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCertificateOIDPolicies(t *testing.T) {
|
||||||
|
template := Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
Subject: pkix.Name{CommonName: "Cert"},
|
||||||
|
NotBefore: time.Unix(1000, 0),
|
||||||
|
NotAfter: time.Unix(100000, 0),
|
||||||
|
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
|
||||||
|
Policies: []x509.OID{
|
||||||
|
mustNewOIDFromInts(t, []uint64{1, 2, 3, 4, 5}),
|
||||||
|
mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxInt32}),
|
||||||
|
mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxUint32, math.MaxUint64}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectPolicyIdentifiers = []asn1.ObjectIdentifier{
|
||||||
|
[]int{1, 2, 3, 4, 5},
|
||||||
|
[]int{1, 2, 3, math.MaxInt32},
|
||||||
|
[]int{1, 2, 3},
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectPolicies = []x509.OID{
|
||||||
|
mustNewOIDFromInts(t, []uint64{1, 2, 3, 4, 5}),
|
||||||
|
mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxInt32}),
|
||||||
|
mustNewOIDFromInts(t, []uint64{1, 2, 3, math.MaxUint32, math.MaxUint64}),
|
||||||
|
mustNewOIDFromInts(t, []uint64{1, 2, 3}),
|
||||||
|
}
|
||||||
|
|
||||||
|
certDER, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateCertificate() unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := ParseCertificate(certDER)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseCertificate() unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualFunc(cert.PolicyIdentifiers, expectPolicyIdentifiers, slices.Equal) {
|
||||||
|
t.Errorf("cert.PolicyIdentifiers = %v, want: %v", cert.PolicyIdentifiers, expectPolicyIdentifiers)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.EqualFunc(cert.Policies, expectPolicies, x509.OID.Equal) {
|
||||||
|
t.Errorf("cert.Policies = %v, want: %v", cert.Policies, expectPolicies)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user