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 }