...

Source file src/github.com/sassoftware/relic/lib/x509tools/names.go

Documentation: github.com/sassoftware/relic/lib/x509tools

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package x509tools
    18  
    19  import (
    20  	"crypto/x509"
    21  	"encoding/asn1"
    22  	"encoding/binary"
    23  	"fmt"
    24  	"strings"
    25  	"unicode/utf16"
    26  )
    27  
    28  type rdnAttr struct {
    29  	Type  asn1.ObjectIdentifier
    30  	Value asn1.RawValue
    31  }
    32  
    33  type rdnNameSet []rdnAttr
    34  
    35  type NameStyle int
    36  
    37  const (
    38  	NameStyleOpenSsl NameStyle = iota
    39  	NameStyleLdap
    40  	NameStyleMsOsco
    41  )
    42  
    43  type attrName struct {
    44  	Type asn1.ObjectIdentifier
    45  	Name string
    46  }
    47  
    48  var nameStyleLdap = []attrName{
    49  	attrName{asn1.ObjectIdentifier{2, 5, 4, 3}, "CN"},
    50  	attrName{asn1.ObjectIdentifier{2, 5, 4, 4}, "surname"},
    51  	attrName{asn1.ObjectIdentifier{2, 5, 4, 5}, "serialNumber"},
    52  	attrName{asn1.ObjectIdentifier{2, 5, 4, 6}, "C"},
    53  	attrName{asn1.ObjectIdentifier{2, 5, 4, 7}, "L"},
    54  	attrName{asn1.ObjectIdentifier{2, 5, 4, 8}, "ST"},
    55  	attrName{asn1.ObjectIdentifier{2, 5, 4, 9}, "street"},
    56  	attrName{asn1.ObjectIdentifier{2, 5, 4, 10}, "O"},
    57  	attrName{asn1.ObjectIdentifier{2, 5, 4, 11}, "OU"},
    58  	attrName{asn1.ObjectIdentifier{2, 5, 4, 12}, "title"},
    59  	attrName{asn1.ObjectIdentifier{2, 5, 4, 13}, "description"},
    60  	attrName{asn1.ObjectIdentifier{2, 5, 4, 17}, "postalCode"},
    61  	attrName{asn1.ObjectIdentifier{2, 5, 4, 18}, "postOfficeBox"},
    62  	attrName{asn1.ObjectIdentifier{2, 5, 4, 20}, "telephoneNumber"},
    63  	attrName{asn1.ObjectIdentifier{2, 5, 4, 42}, "givenName"},
    64  	attrName{asn1.ObjectIdentifier{2, 5, 4, 43}, "initials"},
    65  	attrName{asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 25}, "dc"},
    66  	attrName{asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, "emailAddress"},
    67  }
    68  
    69  // Per [MS-OSCO]
    70  // https://msdn.microsoft.com/en-us/library/dd947276(v=office.12).aspx
    71  var nameStyleMsOsco = []attrName{
    72  	attrName{asn1.ObjectIdentifier{2, 5, 4, 3}, "CN"},
    73  	attrName{asn1.ObjectIdentifier{2, 5, 4, 7}, "L"},
    74  	attrName{asn1.ObjectIdentifier{2, 5, 4, 10}, "O"},
    75  	attrName{asn1.ObjectIdentifier{2, 5, 4, 11}, "OU"},
    76  	attrName{asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, "E"},
    77  	attrName{asn1.ObjectIdentifier{2, 5, 4, 6}, "C"},
    78  	attrName{asn1.ObjectIdentifier{2, 5, 4, 8}, "S"},
    79  	attrName{asn1.ObjectIdentifier{2, 5, 4, 9}, "STREET"},
    80  	attrName{asn1.ObjectIdentifier{2, 5, 4, 12}, "T"},
    81  	attrName{asn1.ObjectIdentifier{2, 5, 4, 42}, "G"},
    82  	attrName{asn1.ObjectIdentifier{2, 5, 4, 43}, "I"},
    83  	attrName{asn1.ObjectIdentifier{2, 5, 4, 4}, "SN"},
    84  	attrName{asn1.ObjectIdentifier{2, 5, 4, 5}, "SERIALNUMBER"},
    85  	attrName{asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 25}, "DC"},
    86  	attrName{asn1.ObjectIdentifier{2, 5, 4, 13}, "Description"},
    87  	attrName{asn1.ObjectIdentifier{2, 5, 4, 17}, "PostalCode"},
    88  	attrName{asn1.ObjectIdentifier{2, 5, 4, 18}, "POBox"},
    89  	attrName{asn1.ObjectIdentifier{2, 5, 4, 20}, "Phone"},
    90  }
    91  
    92  // returned by the Format* functions in case there's something cripplingly
    93  // wrong with it
    94  const InvalidName = "<invalid>"
    95  
    96  // Format the name (RDN sequence) from its raw DER to a readable style.
    97  func FormatPkixName(der []byte, style NameStyle) string {
    98  	var seq asn1.RawValue
    99  	if _, err := asn1.Unmarshal(der, &seq); err != nil {
   100  		return InvalidName
   101  	}
   102  	seqbytes := seq.Bytes
   103  	var formatted []string
   104  	for len(seqbytes) > 0 {
   105  		var rdnSet rdnNameSet
   106  		var err error
   107  		seqbytes, err = asn1.UnmarshalWithParams(seqbytes, &rdnSet, "set")
   108  		if err != nil {
   109  			return InvalidName
   110  		}
   111  		for _, attr := range rdnSet {
   112  			formatted = append(formatted, fmt.Sprintf("%s=%s", attName(attr.Type, style), attValue(attr.Value, style)))
   113  		}
   114  	}
   115  	if len(formatted) == 0 {
   116  		return ""
   117  	}
   118  	switch style {
   119  	case NameStyleOpenSsl:
   120  		return "/" + strings.Join(formatted, "/") + "/"
   121  	case NameStyleLdap, NameStyleMsOsco:
   122  		// Per RFC 2253 2.1, reverse the order
   123  		for i := 0; i < len(formatted)/2; i++ {
   124  			j := len(formatted) - i - 1
   125  			formatted[i], formatted[j] = formatted[j], formatted[i]
   126  		}
   127  		return strings.Join(formatted, ", ")
   128  	default:
   129  		panic("invalid style argument")
   130  	}
   131  }
   132  
   133  func attName(t asn1.ObjectIdentifier, style NameStyle) string {
   134  	var names []attrName
   135  	var defaultPrefix string
   136  	switch style {
   137  	case NameStyleLdap, NameStyleOpenSsl:
   138  		names = nameStyleLdap
   139  	case NameStyleMsOsco:
   140  		names = nameStyleMsOsco
   141  		defaultPrefix = "OID."
   142  	default:
   143  		panic("invalid style argument")
   144  	}
   145  	for _, name := range names {
   146  		if name.Type.Equal(t) {
   147  			return name.Name
   148  		}
   149  	}
   150  	return defaultPrefix + t.String()
   151  }
   152  
   153  func attValue(raw asn1.RawValue, style NameStyle) string {
   154  	var value string
   155  	switch raw.Tag {
   156  	case asn1.TagUTF8String, asn1.TagIA5String, asn1.TagPrintableString:
   157  		var ret interface{}
   158  		if _, err := asn1.Unmarshal(raw.FullBytes, &ret); err != nil {
   159  			return InvalidName
   160  		}
   161  		value = ret.(string)
   162  	case Asn1TagBMPString:
   163  		value = ParseBMPString(raw)
   164  	default:
   165  		return InvalidName
   166  	}
   167  	switch style {
   168  	case NameStyleOpenSsl:
   169  		value = strings.Replace(value, "/", "\\/", -1)
   170  	case NameStyleLdap, NameStyleMsOsco:
   171  		quote := false
   172  		if len(value) == 0 {
   173  			quote = true
   174  		}
   175  		if strings.HasPrefix(value, " ") || strings.HasSuffix(value, " ") {
   176  			quote = true
   177  		}
   178  		if i := strings.IndexAny(value, ",+=\n<>#;'\""); i >= 0 {
   179  			quote = true
   180  		}
   181  		value = strings.Replace(value, "\"", "\"\"", -1)
   182  		if quote {
   183  			value = "\"" + value + "\""
   184  		}
   185  	}
   186  	return value
   187  }
   188  
   189  func ParseBMPString(raw asn1.RawValue) string {
   190  	runes := make([]uint16, len(raw.Bytes)/2)
   191  	for i := range runes {
   192  		runes[i] = binary.BigEndian.Uint16(raw.Bytes[i*2:])
   193  	}
   194  	return string(utf16.Decode(runes))
   195  }
   196  
   197  func ToBMPString(value string) asn1.RawValue {
   198  	runes := utf16.Encode([]rune(value))
   199  	raw := make([]byte, 2*len(runes))
   200  	for i, r := range runes {
   201  		binary.BigEndian.PutUint16(raw[i*2:], r)
   202  	}
   203  	return asn1.RawValue{Tag: Asn1TagBMPString, Bytes: raw}
   204  }
   205  
   206  // Format the certificate subject name in LDAP style
   207  func FormatSubject(cert *x509.Certificate) string {
   208  	return FormatPkixName(cert.RawSubject, NameStyleLdap)
   209  }
   210  
   211  // Format the certificate issuer name in LDAP style
   212  func FormatIssuer(cert *x509.Certificate) string {
   213  	return FormatPkixName(cert.RawIssuer, NameStyleLdap)
   214  }
   215  

View as plain text