github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/x509/certinfo.go (about)

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