github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/grantae/certinfo/certinfo.go (about)

     1  package certinfo
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/dsa"
     6  	"crypto/rsa"
     7  	"encoding/asn1"
     8  	"errors"
     9  	"fmt"
    10  	"github.com/hellobchain/newcryptosm/ecdsa"
    11  	"github.com/hellobchain/newcryptosm/x509"
    12  	"github.com/hellobchain/newcryptosm/x509/pkix"
    13  	"math/big"
    14  	"net"
    15  	"time"
    16  )
    17  
    18  // Extra ASN1 OIDs that we may need to handle
    19  var (
    20  	oidEmailAddress                 = []int{1, 2, 840, 113549, 1, 9, 1}
    21  	oidExtensionAuthorityInfoAccess = []int{1, 3, 6, 1, 5, 5, 7, 1, 1}
    22  	oidNSComment                    = []int{2, 16, 840, 1, 113730, 1, 13}
    23  )
    24  
    25  // validity allows unmarshaling the certificate validity date range
    26  type validity struct {
    27  	NotBefore, NotAfter time.Time
    28  }
    29  
    30  // publicKeyInfo allows unmarshaling the public key
    31  type publicKeyInfo struct {
    32  	Algorithm pkix.AlgorithmIdentifier
    33  	PublicKey asn1.BitString
    34  }
    35  
    36  // tbsCertificate allows unmarshaling of the "To-Be-Signed" principle portion
    37  // of the certificate
    38  type tbsCertificate struct {
    39  	Version            int `asn1:"optional,explicit,default:1,tag:0"`
    40  	SerialNumber       *big.Int
    41  	SignatureAlgorithm pkix.AlgorithmIdentifier
    42  	Issuer             asn1.RawValue
    43  	Validity           validity
    44  	Subject            asn1.RawValue
    45  	PublicKey          publicKeyInfo
    46  	UniqueID           asn1.BitString   `asn1:"optional,tag:1"`
    47  	SubjectUniqueID    asn1.BitString   `asn1:"optional,tag:2"`
    48  	Extensions         []pkix.Extension `asn1:"optional,explicit,tag:3"`
    49  }
    50  
    51  // certUniqueIDs extracts the subject and issuer unique IDs which are
    52  // byte strings. These are not common but may be present in x509v2 certificates
    53  // or later under tags 1 and 2 (before x509v3 extensions).
    54  func certUniqueIDs(tbsAsnData []byte) (issuerUniqueID, subjectUniqueID []byte, err error) {
    55  	var tbs tbsCertificate
    56  	rest, err := asn1.Unmarshal(tbsAsnData, &tbs)
    57  	if err != nil {
    58  		return nil, nil, err
    59  	}
    60  	if len(rest) > 0 {
    61  		return nil, nil, asn1.SyntaxError{Msg: "trailing data"}
    62  	}
    63  	iuid := tbs.UniqueID.RightAlign()
    64  	suid := tbs.SubjectUniqueID.RightAlign()
    65  	return iuid, suid, err
    66  }
    67  
    68  // printName prints the fields of a distinguished name, which include such
    69  // things as its common name and locality.
    70  func printName(names []pkix.AttributeTypeAndValue, buf *bytes.Buffer) []string {
    71  	values := []string{}
    72  	for _, name := range names {
    73  		oid := name.Type
    74  		if len(oid) == 4 && oid[0] == 2 && oid[1] == 5 && oid[2] == 4 {
    75  			switch oid[3] {
    76  			case 3:
    77  				values = append(values, fmt.Sprintf("CN=%s", name.Value))
    78  			case 6:
    79  				values = append(values, fmt.Sprintf("C=%s", name.Value))
    80  			case 8:
    81  				values = append(values, fmt.Sprintf("ST=%s", name.Value))
    82  			case 10:
    83  				values = append(values, fmt.Sprintf("O=%s", name.Value))
    84  			case 11:
    85  				values = append(values, fmt.Sprintf("OU=%s", name.Value))
    86  			default:
    87  				values = append(values, fmt.Sprintf("UnknownOID=%s", name.Type.String()))
    88  			}
    89  		} else if oid.Equal(oidEmailAddress) {
    90  			values = append(values, fmt.Sprintf("emailAddress=%s", name.Value))
    91  		} else {
    92  			values = append(values, fmt.Sprintf("UnknownOID=%s", name.Type.String()))
    93  		}
    94  	}
    95  	if len(values) > 0 {
    96  		buf.WriteString(values[0])
    97  		for i := 1; i < len(values); i++ {
    98  			buf.WriteString("," + values[i])
    99  		}
   100  		buf.WriteString("\n")
   101  	}
   102  	return values
   103  }
   104  
   105  // dsaKeyPrinter formats the Y, P, Q, or G components of a DSA public key.
   106  func dsaKeyPrinter(name string, val *big.Int, buf *bytes.Buffer) {
   107  	buf.WriteString(fmt.Sprintf("%16s%s:", "", name))
   108  	for i, b := range val.Bytes() {
   109  		if (i % 15) == 0 {
   110  			buf.WriteString(fmt.Sprintf("\n%20s", ""))
   111  		}
   112  		buf.WriteString(fmt.Sprintf("%02x", b))
   113  		if i != len(val.Bytes())-1 {
   114  			buf.WriteString(":")
   115  		}
   116  	}
   117  	buf.WriteString("\n")
   118  }
   119  
   120  func printVersion(version int, buf *bytes.Buffer) {
   121  	hexVersion := version - 1
   122  	if hexVersion < 0 {
   123  		hexVersion = 0
   124  	}
   125  	buf.WriteString(fmt.Sprintf("%8sVersion: %d (%#x)\n", "", version, hexVersion))
   126  }
   127  
   128  func printSubjectInformation(subj *pkix.Name, pkAlgo x509.PublicKeyAlgorithm, pk interface{}, buf *bytes.Buffer) error {
   129  	buf.WriteString(fmt.Sprintf("%8sSubject: ", ""))
   130  	printName(subj.Names, buf)
   131  	buf.WriteString(fmt.Sprintf("%8sSubject Public Key Info:\n%12sPublic Key Algorithm: ", "", ""))
   132  	switch pkAlgo {
   133  	case x509.RSA:
   134  		buf.WriteString(fmt.Sprintf("RSA\n"))
   135  		if rsaKey, ok := pk.(*rsa.PublicKey); ok {
   136  			buf.WriteString(fmt.Sprintf("%16sPublic-Key: (%d bit)\n", "", rsaKey.N.BitLen()))
   137  			// Some implementations (notably OpenSSL) prepend 0x00 to the modulus
   138  			// if its most-significant bit is set. There is no need to do that here
   139  			// because the modulus is always unsigned and the extra byte can be
   140  			// confusing given the bit length.
   141  			buf.WriteString(fmt.Sprintf("%16sModulus:", ""))
   142  			for i, val := range rsaKey.N.Bytes() {
   143  				if (i % 15) == 0 {
   144  					buf.WriteString(fmt.Sprintf("\n%20s", ""))
   145  				}
   146  				buf.WriteString(fmt.Sprintf("%02x", val))
   147  				if i != len(rsaKey.N.Bytes())-1 {
   148  					buf.WriteString(":")
   149  				}
   150  			}
   151  			buf.WriteString(fmt.Sprintf("\n%16sExponent: %d (%#x)\n", "", rsaKey.E, rsaKey.E))
   152  		} else {
   153  			return errors.New("certinfo: Expected rsa.PublicKey for type x509.RSA")
   154  		}
   155  	case x509.DSA:
   156  		buf.WriteString(fmt.Sprintf("DSA\n"))
   157  		if dsaKey, ok := pk.(*dsa.PublicKey); ok {
   158  			dsaKeyPrinter("pub", dsaKey.Y, buf)
   159  			dsaKeyPrinter("P", dsaKey.P, buf)
   160  			dsaKeyPrinter("Q", dsaKey.Q, buf)
   161  			dsaKeyPrinter("G", dsaKey.G, buf)
   162  		} else {
   163  			return errors.New("certinfo: Expected dsa.PublicKey for type x509.DSA")
   164  		}
   165  	case x509.ECDSA:
   166  		buf.WriteString(fmt.Sprintf("ECDSA\n"))
   167  		if ecdsaKey, ok := pk.(*ecdsa.PublicKey); ok {
   168  			buf.WriteString(fmt.Sprintf("%16sPublic-Key: (%d bit)\n", "", ecdsaKey.Params().BitSize))
   169  			dsaKeyPrinter("X", ecdsaKey.X, buf)
   170  			dsaKeyPrinter("Y", ecdsaKey.Y, buf)
   171  			buf.WriteString(fmt.Sprintf("%16sCurve: %s\n", "", ecdsaKey.Params().Name))
   172  		} else {
   173  			return errors.New("certinfo: Expected ecdsa.PublicKey for type x509.DSA")
   174  		}
   175  	case x509.SM2:
   176  		buf.WriteString(fmt.Sprintf("SM2\n"))
   177  		if sm2Key, ok := pk.(*ecdsa.PublicKey); ok {
   178  			buf.WriteString(fmt.Sprintf("%16sPublic-Key: (%d bit)\n", "", sm2Key.Params().BitSize))
   179  			dsaKeyPrinter("X", sm2Key.X, buf)
   180  			dsaKeyPrinter("Y", sm2Key.Y, buf)
   181  			buf.WriteString(fmt.Sprintf("%16sCurve: %s\n", "", sm2Key.Params().Name))
   182  		} else {
   183  			return errors.New("certinfo: Expected ecdsa.PublicKey for type x509.DSA")
   184  		}
   185  	default:
   186  		return errors.New("certinfo: Unknown public key type")
   187  	}
   188  	return nil
   189  }
   190  
   191  func printSubjKeyId(ext pkix.Extension, buf *bytes.Buffer) error {
   192  	// subjectKeyIdentifier: RFC 5280, 4.2.1.2
   193  	buf.WriteString(fmt.Sprintf("%12sX509v3 Subject Key Identifier:", ""))
   194  	if ext.Critical {
   195  		buf.WriteString(" critical\n")
   196  	} else {
   197  		buf.WriteString("\n")
   198  	}
   199  	var subjectKeyId []byte
   200  	if _, err := asn1.Unmarshal(ext.Value, &subjectKeyId); err != nil {
   201  		return err
   202  	}
   203  	for i := 0; i < len(subjectKeyId); i++ {
   204  		if i == 0 {
   205  			buf.WriteString(fmt.Sprintf("%16s%02X", "", subjectKeyId[0]))
   206  		} else {
   207  			buf.WriteString(fmt.Sprintf(":%02X", subjectKeyId[i]))
   208  		}
   209  	}
   210  	buf.WriteString("\n")
   211  	return nil
   212  }
   213  
   214  func printSubjAltNames(ext pkix.Extension, dnsNames []string, emailAddresses []string, ipAddresses []net.IP, buf *bytes.Buffer) error {
   215  	// subjectAltName: RFC 5280, 4.2.1.6
   216  	// TODO: Currently crypto/x509 only extracts DNS, email, and IP addresses.
   217  	// We should add the others to it or implement them here.
   218  	buf.WriteString(fmt.Sprintf("%12sX509v3 Subject Alternative Name:", ""))
   219  	if ext.Critical {
   220  		buf.WriteString(" critical\n")
   221  	} else {
   222  		buf.WriteString("\n")
   223  	}
   224  	if len(dnsNames) > 0 {
   225  		buf.WriteString(fmt.Sprintf("%16sDNS:%s", "", dnsNames[0]))
   226  		for i := 1; i < len(dnsNames); i++ {
   227  			buf.WriteString(fmt.Sprintf(", DNS:%s", dnsNames[i]))
   228  		}
   229  		buf.WriteString("\n")
   230  	}
   231  	if len(emailAddresses) > 0 {
   232  		buf.WriteString(fmt.Sprintf("%16semail:%s", "", emailAddresses[0]))
   233  		for i := 1; i < len(emailAddresses); i++ {
   234  			buf.WriteString(fmt.Sprintf(", email:%s", emailAddresses[i]))
   235  		}
   236  		buf.WriteString("\n")
   237  	}
   238  	if len(ipAddresses) > 0 {
   239  		buf.WriteString(fmt.Sprintf("%16sIP Address:%s", "", ipAddresses[0].String())) // XXX verify string format
   240  		for i := 1; i < len(ipAddresses); i++ {
   241  			buf.WriteString(fmt.Sprintf(", IP Address:%s", ipAddresses[i].String()))
   242  		}
   243  		buf.WriteString("\n")
   244  	}
   245  	return nil
   246  }
   247  
   248  func printSignature(sigAlgo x509.SignatureAlgorithm, sig []byte, buf *bytes.Buffer) {
   249  	buf.WriteString(fmt.Sprintf("%4sSignature Algorithm: %s", "", sigAlgo))
   250  	for i, val := range sig {
   251  		if (i % 18) == 0 {
   252  			buf.WriteString(fmt.Sprintf("\n%9s", ""))
   253  		}
   254  		buf.WriteString(fmt.Sprintf("%02x", val))
   255  		if i != len(sig)-1 {
   256  			buf.WriteString(":")
   257  		}
   258  	}
   259  	buf.WriteString("\n")
   260  }
   261  
   262  // CertificateText returns a human-readable string representation
   263  // of the certificate cert. The format is similar (but not identical)
   264  // to the OpenSSL way of printing certificates.
   265  func CertificateText(cert *x509.Certificate) (string, error) {
   266  	var buf bytes.Buffer
   267  	buf.Grow(4096) // 4KiB should be enough
   268  
   269  	buf.WriteString(fmt.Sprintf("Certificate:\n"))
   270  	buf.WriteString(fmt.Sprintf("%4sData:\n", ""))
   271  	printVersion(cert.Version, &buf)
   272  	buf.WriteString(fmt.Sprintf("%8sSerial Number: %d (%#x)\n", "", cert.SerialNumber, cert.SerialNumber))
   273  	buf.WriteString(fmt.Sprintf("%4sSignature Algorithm: %s\n", "", cert.SignatureAlgorithm))
   274  
   275  	// Issuer information
   276  	buf.WriteString(fmt.Sprintf("%8sIssuer: ", ""))
   277  	printName(cert.Issuer.Names, &buf)
   278  
   279  	// Validity information
   280  	buf.WriteString(fmt.Sprintf("%8sValidity\n", ""))
   281  	buf.WriteString(fmt.Sprintf("%12sNot Before: %s\n", "", cert.NotBefore.Format("Jan 2 15:04:05 2006 MST")))
   282  	buf.WriteString(fmt.Sprintf("%12sNot After : %s\n", "", cert.NotAfter.Format("Jan 2 15:04:05 2006 MST")))
   283  
   284  	// Subject information
   285  	err := printSubjectInformation(&cert.Subject, cert.PublicKeyAlgorithm, cert.PublicKey, &buf)
   286  	if err != nil {
   287  		return "", err
   288  	}
   289  
   290  	// Issuer/Subject Unique ID, typically used in old v2 certificates
   291  	issuerUID, subjectUID, err := certUniqueIDs(cert.RawTBSCertificate)
   292  	if err != nil {
   293  		return "", errors.New(fmt.Sprintf("certinfo: Error parsing TBS unique attributes: %s\n", err.Error()))
   294  	}
   295  	if len(issuerUID) > 0 {
   296  		buf.WriteString(fmt.Sprintf("%8sIssuer Unique ID: %02x", "", issuerUID[0]))
   297  		for i := 1; i < len(issuerUID); i++ {
   298  			buf.WriteString(fmt.Sprintf(":%02x", issuerUID[i]))
   299  		}
   300  		buf.WriteString("\n")
   301  	}
   302  	if len(subjectUID) > 0 {
   303  		buf.WriteString(fmt.Sprintf("%8sSubject Unique ID: %02x", "", subjectUID[0]))
   304  		for i := 1; i < len(subjectUID); i++ {
   305  			buf.WriteString(fmt.Sprintf(":%02x", subjectUID[i]))
   306  		}
   307  		buf.WriteString("\n")
   308  	}
   309  
   310  	// Optional extensions for X509v3
   311  	if cert.Version == 3 && len(cert.Extensions) > 0 {
   312  		buf.WriteString(fmt.Sprintf("%8sX509v3 extensions:\n", ""))
   313  		for _, ext := range cert.Extensions {
   314  			if len(ext.Id) == 4 && ext.Id[0] == 2 && ext.Id[1] == 5 && ext.Id[2] == 29 {
   315  				switch ext.Id[3] {
   316  				case 14:
   317  					err = printSubjKeyId(ext, &buf)
   318  				case 15:
   319  					// keyUsage: RFC 5280, 4.2.1.3
   320  					buf.WriteString(fmt.Sprintf("%12sX509v3 Key Usage:", ""))
   321  					if ext.Critical {
   322  						buf.WriteString(" critical\n")
   323  					} else {
   324  						buf.WriteString("\n")
   325  					}
   326  					usages := []string{}
   327  					if cert.KeyUsage&x509.KeyUsageDigitalSignature > 0 {
   328  						usages = append(usages, "Digital Signature")
   329  					}
   330  					if cert.KeyUsage&x509.KeyUsageContentCommitment > 0 {
   331  						usages = append(usages, "Content Commitment")
   332  					}
   333  					if cert.KeyUsage&x509.KeyUsageKeyEncipherment > 0 {
   334  						usages = append(usages, "Key Encipherment")
   335  					}
   336  					if cert.KeyUsage&x509.KeyUsageDataEncipherment > 0 {
   337  						usages = append(usages, "Data Encipherment")
   338  					}
   339  					if cert.KeyUsage&x509.KeyUsageKeyAgreement > 0 {
   340  						usages = append(usages, "Key Agreement")
   341  					}
   342  					if cert.KeyUsage&x509.KeyUsageCertSign > 0 {
   343  						usages = append(usages, "Certificate Sign")
   344  					}
   345  					if cert.KeyUsage&x509.KeyUsageCRLSign > 0 {
   346  						usages = append(usages, "CRL Sign")
   347  					}
   348  					if cert.KeyUsage&x509.KeyUsageEncipherOnly > 0 {
   349  						usages = append(usages, "Encipher Only")
   350  					}
   351  					if cert.KeyUsage&x509.KeyUsageDecipherOnly > 0 {
   352  						usages = append(usages, "Decipher Only")
   353  					}
   354  					if len(usages) > 0 {
   355  						buf.WriteString(fmt.Sprintf("%16s%s", "", usages[0]))
   356  						for i := 1; i < len(usages); i++ {
   357  							buf.WriteString(fmt.Sprintf(", %s", usages[i]))
   358  						}
   359  						buf.WriteString("\n")
   360  					} else {
   361  						buf.WriteString(fmt.Sprintf("%16sNone\n", ""))
   362  					}
   363  				case 17:
   364  					err = printSubjAltNames(ext, cert.DNSNames, cert.EmailAddresses, cert.IPAddresses, &buf)
   365  				case 19:
   366  					// basicConstraints: RFC 5280, 4.2.1.9
   367  					if !cert.BasicConstraintsValid {
   368  						break
   369  					}
   370  					buf.WriteString(fmt.Sprintf("%12sX509v3 Basic Constraints:", ""))
   371  					if ext.Critical {
   372  						buf.WriteString(" critical\n")
   373  					} else {
   374  						buf.WriteString("\n")
   375  					}
   376  					if cert.IsCA {
   377  						buf.WriteString(fmt.Sprintf("%16sCA:TRUE", ""))
   378  					} else {
   379  						buf.WriteString(fmt.Sprintf("%16sCA:FALSE", ""))
   380  					}
   381  					if cert.MaxPathLenZero {
   382  						buf.WriteString(fmt.Sprintf(", pathlen:0\n"))
   383  					} else if cert.MaxPathLen > 0 {
   384  						buf.WriteString(fmt.Sprintf(", pathlen:%d\n", cert.MaxPathLen))
   385  					} else {
   386  						buf.WriteString("\n")
   387  					}
   388  				case 30:
   389  					// nameConstraints: RFC 5280, 4.2.1.10
   390  					// TODO: Currently crypto/x509 only supports "Permitted" and not "Excluded"
   391  					// subtrees. Furthermore it assumes all types are DNS names which is not
   392  					// necessarily true. This missing functionality should be implemented.
   393  					buf.WriteString(fmt.Sprintf("%12sX509v3 Name Constraints:", ""))
   394  					if ext.Critical {
   395  						buf.WriteString(" critical\n")
   396  					} else {
   397  						buf.WriteString("\n")
   398  					}
   399  					if len(cert.PermittedDNSDomains) > 0 {
   400  						buf.WriteString(fmt.Sprintf("%16sPermitted:\n%18s%s", "", "", cert.PermittedDNSDomains[0]))
   401  						for i := 1; i < len(cert.PermittedDNSDomains); i++ {
   402  							buf.WriteString(fmt.Sprintf(", %s", cert.PermittedDNSDomains[i]))
   403  						}
   404  						buf.WriteString("\n")
   405  					}
   406  				case 31:
   407  					// CRLDistributionPoints: RFC 5280, 4.2.1.13
   408  					// TODO: Currently crypto/x509 does not fully implement this section,
   409  					// including types and reason flags.
   410  					buf.WriteString(fmt.Sprintf("%12sX509v3 CRL Distribution Points:", ""))
   411  					if ext.Critical {
   412  						buf.WriteString(" critical\n")
   413  					} else {
   414  						buf.WriteString("\n")
   415  					}
   416  					if len(cert.CRLDistributionPoints) > 0 {
   417  						buf.WriteString(fmt.Sprintf("\n%16sFull Name:\n%18sURI:%s", "", "", cert.CRLDistributionPoints[0]))
   418  						for i := 1; i < len(cert.CRLDistributionPoints); i++ {
   419  							buf.WriteString(fmt.Sprintf(", URI:%s", cert.CRLDistributionPoints[i]))
   420  						}
   421  						buf.WriteString("\n\n")
   422  					}
   423  				case 32:
   424  					// certificatePoliciesExt: RFC 5280, 4.2.1.4
   425  					// TODO: Currently crypto/x509 does not fully impelment this section,
   426  					// including the Certification Practice Statement (CPS)
   427  					buf.WriteString(fmt.Sprintf("%12sX509v3 Certificate Policies:", ""))
   428  					if ext.Critical {
   429  						buf.WriteString(" critical\n")
   430  					} else {
   431  						buf.WriteString("\n")
   432  					}
   433  					for _, val := range cert.PolicyIdentifiers {
   434  						buf.WriteString(fmt.Sprintf("%16sPolicy: %s\n", "", val.String()))
   435  					}
   436  				case 35:
   437  					// authorityKeyIdentifier: RFC 5280, 4.2.1.1
   438  					buf.WriteString(fmt.Sprintf("%12sX509v3 Authority Key Identifier:", ""))
   439  					if ext.Critical {
   440  						buf.WriteString(" critical\n")
   441  					} else {
   442  						buf.WriteString("\n")
   443  					}
   444  					buf.WriteString(fmt.Sprintf("%16skeyid", ""))
   445  					for _, val := range cert.AuthorityKeyId {
   446  						buf.WriteString(fmt.Sprintf(":%02X", val))
   447  					}
   448  					buf.WriteString("\n")
   449  				case 37:
   450  					// extKeyUsage: RFC 5280, 4.2.1.12
   451  					buf.WriteString(fmt.Sprintf("%12sX509v3 Extended Key Usage:", ""))
   452  					if ext.Critical {
   453  						buf.WriteString(" critical\n")
   454  					} else {
   455  						buf.WriteString("\n")
   456  					}
   457  					var list []string
   458  					for _, val := range cert.ExtKeyUsage {
   459  						switch val {
   460  						case x509.ExtKeyUsageAny:
   461  							list = append(list, "Any Usage")
   462  						case x509.ExtKeyUsageServerAuth:
   463  							list = append(list, "TLS Web Server Authentication")
   464  						case x509.ExtKeyUsageClientAuth:
   465  							list = append(list, "TLS Web Client Authentication")
   466  						case x509.ExtKeyUsageCodeSigning:
   467  							list = append(list, "Code Signing")
   468  						case x509.ExtKeyUsageEmailProtection:
   469  							list = append(list, "E-mail Protection")
   470  						case x509.ExtKeyUsageIPSECEndSystem:
   471  							list = append(list, "IPSec End System")
   472  						case x509.ExtKeyUsageIPSECTunnel:
   473  							list = append(list, "IPSec Tunnel")
   474  						case x509.ExtKeyUsageIPSECUser:
   475  							list = append(list, "IPSec User")
   476  						case x509.ExtKeyUsageTimeStamping:
   477  							list = append(list, "Time Stamping")
   478  						case x509.ExtKeyUsageOCSPSigning:
   479  							list = append(list, "OCSP Signing")
   480  						default:
   481  							list = append(list, "UNKNOWN")
   482  						}
   483  					}
   484  					if len(list) > 0 {
   485  						buf.WriteString(fmt.Sprintf("%16s%s", "", list[0]))
   486  						for i := 1; i < len(list); i++ {
   487  							buf.WriteString(fmt.Sprintf(", %s", list[i]))
   488  						}
   489  						buf.WriteString("\n")
   490  					}
   491  				default:
   492  					buf.WriteString(fmt.Sprintf("Unknown extension 2.5.29.%d\n", ext.Id[3]))
   493  				}
   494  				if err != nil {
   495  					return "", err
   496  				}
   497  			} else if ext.Id.Equal(oidExtensionAuthorityInfoAccess) {
   498  				// authorityInfoAccess: RFC 5280, 4.2.2.1
   499  				buf.WriteString(fmt.Sprintf("%12sAuthority Information Access:", ""))
   500  				if ext.Critical {
   501  					buf.WriteString(" critical\n")
   502  				} else {
   503  					buf.WriteString("\n")
   504  				}
   505  				if len(cert.OCSPServer) > 0 {
   506  					buf.WriteString(fmt.Sprintf("%16sOCSP - URI:%s", "", cert.OCSPServer[0]))
   507  					for i := 1; i < len(cert.OCSPServer); i++ {
   508  						buf.WriteString(fmt.Sprintf(",URI:%s", cert.OCSPServer[i]))
   509  					}
   510  					buf.WriteString("\n")
   511  				}
   512  				if len(cert.IssuingCertificateURL) > 0 {
   513  					buf.WriteString(fmt.Sprintf("%16sCA Issuers - URI:%s", "", cert.IssuingCertificateURL[0]))
   514  					for i := 1; i < len(cert.IssuingCertificateURL); i++ {
   515  						buf.WriteString(fmt.Sprintf(",URI:%s", cert.IssuingCertificateURL[i]))
   516  					}
   517  					buf.WriteString("\n")
   518  				}
   519  				buf.WriteString("\n")
   520  			} else if ext.Id.Equal(oidNSComment) {
   521  				// Netscape comment
   522  				var comment string
   523  				rest, err := asn1.Unmarshal(ext.Value, &comment)
   524  				if err != nil || len(rest) > 0 {
   525  					return "", errors.New("certinfo: Error parsing OID " + ext.Id.String())
   526  				}
   527  				if ext.Critical {
   528  					buf.WriteString(fmt.Sprintf("%12sNetscape Comment: critical\n%16s%s\n", "", "", comment))
   529  				} else {
   530  					buf.WriteString(fmt.Sprintf("%12sNetscape Comment:\n%16s%s\n", "", "", comment))
   531  				}
   532  			} else {
   533  				buf.WriteString(fmt.Sprintf("%12sUnknown extension %s\n", "", ext.Id.String()))
   534  			}
   535  		}
   536  		buf.WriteString("\n")
   537  	}
   538  
   539  	// Signature
   540  	printSignature(cert.SignatureAlgorithm, cert.Signature, &buf)
   541  
   542  	// Optional: Print the full PEM certificate
   543  	/*
   544  		pemBlock := pem.Block{
   545  			Type:  "CERTIFICATE",
   546  			Bytes: cert.Raw,
   547  		}
   548  		buf.Write(pem.EncodeToMemory(&pemBlock))
   549  	*/
   550  
   551  	return buf.String(), nil
   552  }
   553  
   554  // CertificateRequestText returns a human-readable string representation
   555  // of the certificate request csr. The format is similar (but not identical)
   556  // to the OpenSSL way of printing certificates.
   557  func CertificateRequestText(csr *x509.CertificateRequest) (string, error) {
   558  	var buf bytes.Buffer
   559  	buf.Grow(4096) // 4KiB should be enough
   560  
   561  	buf.WriteString(fmt.Sprintf("Certificate Request:\n"))
   562  	buf.WriteString(fmt.Sprintf("%4sData:\n", ""))
   563  	printVersion(csr.Version, &buf)
   564  
   565  	// Subject information
   566  	err := printSubjectInformation(&csr.Subject, csr.PublicKeyAlgorithm, csr.PublicKey, &buf)
   567  	if err != nil {
   568  		return "", err
   569  	}
   570  
   571  	// Optional extensions for X509v3
   572  	if csr.Version == 3 && len(csr.Extensions) > 0 {
   573  		buf.WriteString(fmt.Sprintf("%8sRequested Extensions:\n", ""))
   574  		var err error
   575  		for _, ext := range csr.Extensions {
   576  			if len(ext.Id) == 4 && ext.Id[0] == 2 && ext.Id[1] == 5 && ext.Id[2] == 29 {
   577  				switch ext.Id[3] {
   578  				case 14:
   579  					err = printSubjKeyId(ext, &buf)
   580  				case 17:
   581  					err = printSubjAltNames(ext, csr.DNSNames, csr.EmailAddresses, csr.IPAddresses, &buf)
   582  				}
   583  			}
   584  			if err != nil {
   585  				return "", err
   586  			}
   587  		}
   588  		buf.WriteString("\n")
   589  	}
   590  
   591  	// Signature
   592  	printSignature(csr.SignatureAlgorithm, csr.Signature, &buf)
   593  
   594  	return buf.String(), nil
   595  }