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 }