dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/credentials/certgenerate/san.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  /*
    19   *
    20   * Copyright Istio Authors
    21   *
    22   */
    23  
    24  package certgenerate
    25  
    26  import (
    27  	"crypto/x509/pkix"
    28  	"encoding/asn1"
    29  	"fmt"
    30  	"net"
    31  	"strings"
    32  )
    33  
    34  // IdentityType represents type of an identity. This is used to properly encode
    35  // an identity into a SAN extension.
    36  type IdentityType int
    37  
    38  const (
    39  	// TypeDNS represents a DNS name.
    40  	TypeDNS IdentityType = iota
    41  	// TypeIP represents an IP address.
    42  	TypeIP
    43  	// TypeURI represents a universal resource identifier.
    44  	TypeURI
    45  
    46  	Scheme       = "spiffe"
    47  	URIPrefix    = Scheme + "://"
    48  	URIPrefixLen = len(URIPrefix)
    49  )
    50  
    51  var (
    52  	// Mapping from the type of an identity to the OID tag value for the X.509
    53  	// SAN field (see https://tools.ietf.org/html/rfc5280#appendix-A.2)
    54  	//
    55  	// SubjectAltName ::= GeneralNames
    56  	//
    57  	// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
    58  	//
    59  	// GeneralName ::= CHOICE {
    60  	//      dNSName                         [2]     IA5String,
    61  	//      uniformResourceIdentifier       [6]     IA5String,
    62  	//      iPAddress                       [7]     OCTET STRING,
    63  	// }
    64  	oidTagMap = map[IdentityType]int{
    65  		TypeDNS: 2,
    66  		TypeURI: 6,
    67  		TypeIP:  7,
    68  	}
    69  
    70  	// A reversed map that maps from an OID tag to the corresponding identity
    71  	// type.
    72  	identityTypeMap = generateReversedMap(oidTagMap)
    73  
    74  	// The OID for the SAN extension (See
    75  	// http://www.alvestrand.no/objectid/2.5.29.17.html).
    76  	oidSubjectAlternativeName = asn1.ObjectIdentifier{2, 5, 29, 17}
    77  )
    78  
    79  // Identity is an object holding both the encoded identifier bytes as well as
    80  // the type of the identity.
    81  type Identity struct {
    82  	Type  IdentityType
    83  	Value []byte
    84  }
    85  
    86  // BuildSubjectAltNameExtension builds the SAN extension for the certificate.
    87  func BuildSubjectAltNameExtension(hosts string) (*pkix.Extension, error) {
    88  	ids := []Identity{}
    89  	for _, host := range strings.Split(hosts, ",") {
    90  		if ip := net.ParseIP(host); ip != nil {
    91  			// Use the 4-byte representation of the IP address when possible.
    92  			if eip := ip.To4(); eip != nil {
    93  				ip = eip
    94  			}
    95  			ids = append(ids, Identity{Type: TypeIP, Value: ip})
    96  		} else if strings.HasPrefix(host, URIPrefix) {
    97  			ids = append(ids, Identity{Type: TypeURI, Value: []byte(host)})
    98  		} else {
    99  			ids = append(ids, Identity{Type: TypeDNS, Value: []byte(host)})
   100  		}
   101  	}
   102  
   103  	san, err := BuildSANExtension(ids)
   104  	if err != nil {
   105  		return nil, fmt.Errorf("SAN extension building failure (%v)", err)
   106  	}
   107  
   108  	return san, nil
   109  }
   110  
   111  // BuildSANExtension builds a `pkix.Extension` of type "Subject
   112  // Alternative Name" based on the given identities.
   113  func BuildSANExtension(identites []Identity) (*pkix.Extension, error) {
   114  	rawValues := []asn1.RawValue{}
   115  	for _, i := range identites {
   116  		tag, ok := oidTagMap[i.Type]
   117  		if !ok {
   118  			return nil, fmt.Errorf("unsupported identity type: %v", i.Type)
   119  		}
   120  
   121  		rawValues = append(rawValues, asn1.RawValue{
   122  			Bytes: i.Value,
   123  			Class: asn1.ClassContextSpecific,
   124  			Tag:   tag,
   125  		})
   126  	}
   127  
   128  	bs, err := asn1.Marshal(rawValues)
   129  	if err != nil {
   130  		return nil, fmt.Errorf("failed to marshal the raw values for SAN field (err: %s)", err)
   131  	}
   132  
   133  	// SAN is Critical because the subject is empty. This is compliant with X.509 and SPIFFE standards.
   134  	return &pkix.Extension{Id: oidSubjectAlternativeName, Critical: true, Value: bs}, nil
   135  }
   136  
   137  // ExtractIDsFromSAN takes a SAN extension and extracts the identities.
   138  // The logic is mostly borrowed from
   139  // https://github.com/golang/go/blob/master/src/crypto/x509/x509.go, with the
   140  // addition of supporting extracting URIs.
   141  func ExtractIDsFromSAN(sanExt *pkix.Extension) ([]Identity, error) {
   142  	if !sanExt.Id.Equal(oidSubjectAlternativeName) {
   143  		return nil, fmt.Errorf("the input is not a SAN extension")
   144  	}
   145  
   146  	var sequence asn1.RawValue
   147  	if rest, err := asn1.Unmarshal(sanExt.Value, &sequence); err != nil {
   148  		return nil, err
   149  	} else if len(rest) != 0 {
   150  		return nil, fmt.Errorf("the SAN extension is incorrectly encoded")
   151  	}
   152  
   153  	// Check the rawValue is a sequence.
   154  	if !sequence.IsCompound || sequence.Tag != asn1.TagSequence || sequence.Class != asn1.ClassUniversal {
   155  		return nil, fmt.Errorf("the SAN extension is incorrectly encoded")
   156  	}
   157  
   158  	ids := []Identity{}
   159  	for bytes := sequence.Bytes; len(bytes) > 0; {
   160  		var rawValue asn1.RawValue
   161  		var err error
   162  
   163  		bytes, err = asn1.Unmarshal(bytes, &rawValue)
   164  		if err != nil {
   165  			return nil, err
   166  		}
   167  		ids = append(ids, Identity{Type: identityTypeMap[rawValue.Tag], Value: rawValue.Bytes})
   168  	}
   169  
   170  	return ids, nil
   171  }
   172  
   173  // ExtractSANExtension extracts the "Subject Alternative Name" externsion from
   174  // the given PKIX extension set.
   175  func ExtractSANExtension(exts []pkix.Extension) *pkix.Extension {
   176  	for _, ext := range exts {
   177  		if ext.Id.Equal(oidSubjectAlternativeName) {
   178  			// We don't need to examine other extensions anymore since a certificate
   179  			// must not include more than one instance of a particular extension. See
   180  			// https://tools.ietf.org/html/rfc5280#section-4.2.
   181  			return &ext
   182  		}
   183  	}
   184  	return nil
   185  }
   186  
   187  // ExtractIDs first finds the SAN extension from the given extension set, then
   188  // extract identities from the SAN extension.
   189  func ExtractIDs(exts []pkix.Extension) ([]string, error) {
   190  	sanExt := ExtractSANExtension(exts)
   191  	if sanExt == nil {
   192  		return nil, fmt.Errorf("the SAN extension does not exist")
   193  	}
   194  
   195  	idsWithType, err := ExtractIDsFromSAN(sanExt)
   196  	if err != nil {
   197  		return nil, fmt.Errorf("failed to extract identities from SAN extension (error %v)", err)
   198  	}
   199  
   200  	ids := []string{}
   201  	for _, id := range idsWithType {
   202  		ids = append(ids, string(id.Value))
   203  	}
   204  	return ids, nil
   205  }
   206  
   207  func generateReversedMap(m map[IdentityType]int) map[int]IdentityType {
   208  	reversed := make(map[int]IdentityType)
   209  	for key, value := range m {
   210  		reversed[value] = key
   211  	}
   212  	return reversed
   213  }