1"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([["5578"],{657(e,s,n){n.r(s),n.d(s,{metadata:()=>i,default:()=>A,frontMatter:()=>b,contentTitle:()=>f,toc:()=>w,assets:()=>v});var i=JSON.parse('{"id":"file/detail-constructed","title":"Deep Dive: ASN1\\\\Constructed Objects","description":"Overview","source":"@site/versioned_docs/version-4.0/file/detail-constructed.mdx","sourceDirName":"file","slug":"/file/detail-constructed","permalink":"/docs/file/detail-constructed","draft":false,"unlisted":false,"editUrl":"https://github.com/phpseclib/phpseclib.github.io/tree/source/versioned_docs/version-4.0/file/detail-constructed.mdx","tags":[],"version":"4.0","frontMatter":{"title":"Deep Dive: ASN1\\\\Constructed Objects"},"sidebar":"tutorialSidebar","previous":{"title":"SPKAC","permalink":"/docs/file/spkac"},"next":{"title":"Deep Dive: Distinguished Names (DNs)","permalink":"/docs/file/detail-dns"}}'),r=n(4848),t=n(8453);function c(e){let s={a:"a",...(0,t.R)(),...e.components},{Details:n}=s;return n||function(e,s){throw Error("Expected "+(s?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,r.jsxs)("div",{class:"tree",children:[(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"tbsCertificate"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"version"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Integer"}),(0,r.jsx)("div",{children:"v3"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"serialNumber"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Integer"}),(0,r.jsx)("div",{children:"105827261859531100510423749949966875981"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"signature"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"algorithm"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"sha1WithRSAEncryption"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"issuer"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"rdnSequence"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-countryName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\PrintableString"}),(0,r.jsx)("div",{children:"ZA"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"1"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-organizationName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\PrintableString"}),(0,r.jsx)("div",{children:"Thawte Consulting (Pty) Ltd."})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"2"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-commonName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\PrintableString"}),(0,r.jsx)("div",{children:"Thawte SGC CA"})]})]})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"validity"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"notBefore"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"utcTime"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\UTCTime"}),(0,r.jsx)("div",{children:"2011-10-26 00:00:00"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"notAfter"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"utcTime"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\UTCTime"}),(0,r.jsx)("div",{children:"2013-09-30 23:59:59"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"subject"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"rdnSequence"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-countryName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\PrintableString"}),(0,r.jsx)("div",{children:"US"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"1"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-stateOrProvinceName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\PrintableString"}),(0,r.jsx)("div",{children:"California"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"2"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-localityName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\TeletexString"}
1),(0,r.jsx)("div",{children:"Mountain View"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"3"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-organizationName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\TeletexString"}),(0,r.jsx)("div",{children:"Google Inc"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"4"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-commonName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\TeletexString"}),(0,r.jsx)("div",{children:(0,r.jsx)(s.a,{href:"http://www.google.com",children:"www.google.com"})})]})]})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"subjectPublicKeyInfo"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"algorithm"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"algorithm"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"rsaEncryption"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"subjectPublicKey"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\BitString"}),(0,r.jsx)("div",{children:"0030818902818100deb72643a69985cd38a71509b9cf0fc9c3558c88ee8c8d2827244b2a5ea0d816fa61184bcf6d6080d335403272c08f12d8e54e8fb9b2f6d9155e5a8631a3ba86aa6bc8d9718ccccd27131e9d425d38f6a7aceffa62f31881d424467f01777cc62a891499bb98391da819fb3900447d1b946a782d69adc07a2cfad0da201298d30203010001"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extensions"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnId"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-ce-basicConstraints"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"critical"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"true"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnValue"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OctetString"}),(0,r.jsx)("div",{children:"3000"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"1"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnId"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-ce-cRLDistributionPoints"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"critical"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"false"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnValue"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OctetString"}),(0,r.jsx)("div",{children:"302d302ba029a0278625687474703a2f2f63726c2e7468617774652e636f6d2f54686177746553474343412e63726c"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"2"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnId"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-ce-extKeyUsage"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"critical"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"false"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnValue"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OctetString"}),(0,r.jsx)("div",{children:"301f06082b0601050507030106082b0601050507030206096086480186f8420401"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"3"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnId"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-pe-authorityInfoAccess"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"critical"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"false"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnValue"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OctetString"}),(0,r.jsx)("div",{children:"3064302206082b060105050730018616687474703a2f2f6f6373702e7468617774652e636f6d303e06082b060105050730028632687474703a2f2f7777772e7468617774652e636f6d2f7265706f7369746f72792f5468617774655f5347435f43412e637274"})]})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"signatureAlgorithm"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"algorithm"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"sha1WithRSAEncryption"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"signature"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\BitString"}),(0,r.jsx)("div",{children:"0021acd5aeca34895ac2ab52d2b234669d7aabeee67cd57ec25c28bb7400c9101f4213fc698a1e24a00200e9ba5bca1904b2d3af01b27e5f14dba6db52b99af3127f7ca29c3b6f997dea500d762312fff7667329b7950aadd88bb2de20e90a70641108c85af17d9eec69a5a5d582d7271e9e56cdd276d5792bf725431c69f0b8f9"})]})]})]})}function l(e={}){let{wrapper:s}={...(0,t.R)(),...e.components};return s?(0,r.jsx)(s,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}function a(e){let s={a:"a",...(0,t.R)(),...e.components},{Details:n}=s;return n||function(e,s){throw Error("Expected "+(s?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,r.jsxs)("div",{class:"tree",children:[(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"tbsCertificate"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"version"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Integer"}),(0,r.jsx)("div",{children:"v3"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"serialNumber"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Integer"}),(0,r.jsx)("div",{children:"105827261859531100510423749949966875981"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"signature"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"algorithm"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"sha1WithRSAEncryption"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"issuer"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\MalformedData"}),(0,r.jsx)("div",{children:"310b3009060355040613025a4131253023060355040a131c54686177746520436f6e73756c74696e67202850747929204c74642e2e1630140603550403130d54686177746520534743204341"})]})]}
1),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"validity"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"notBefore"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"utcTime"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\UTCTime"}),(0,r.jsx)("div",{children:"2011-10-26 00:00:00"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"notAfter"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"utcTime"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\UTCTime"}),(0,r.jsx)("div",{children:"2013-09-30 23:59:59"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"subject"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"rdnSequence"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-countryName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\PrintableString"}),(0,r.jsx)("div",{children:"US"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"1"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-stateOrProvinceName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\PrintableString"}),(0,r.jsx)("div",{children:"California"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"2"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-localityName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\TeletexString"}
1),(0,r.jsx)("div",{children:"Mountain View"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"3"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-organizationName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\TeletexString"}),(0,r.jsx)("div",{children:"Google Inc"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"4"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"type"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-at-commonName"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"value"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\TeletexString"}),(0,r.jsx)("div",{children:(0,r.jsx)(s.a,{href:"http://www.google.com",children:"www.google.com"})})]})]})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"subjectPublicKeyInfo"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\Crypt\\RSA\\PublicKey"}),(0,r.jsx)("div",{children:(0,r.jsx)("pre",{children:"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDetyZDppmFzTinFQm5zw/Jw1WM\niO6MjSgnJEsqXqDYFvphGEvPbWCA0zVAMnLAjxLY5U6PubL22RVeWoYxo7qGqmvI\n2XGMzM0nEx6dQl049qes7/pi8xiB1CRGfwF3fMYqiRSZu5g5HagZ+zkARH0blGp4\nLWmtwHos+tDaIBKY0wIDAQAB\n-----END PUBLIC KEY-----"})})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extensions"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnId"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-ce-basicConstraints"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"critical"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"true"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnValue"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"cA"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"false"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"1"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnId"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-ce-cRLDistributionPoints"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"critical"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"false"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnValue"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"distributionPoint"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"fullName"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"uniformResourceIdentifier"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\IA5String"}),(0,r.jsx)("div",{children:(0,r.jsx)(s.a,{href:"http://crl.thawte.com/ThawteSGCCA.crl",children:"http://crl.thawte.com/ThawteSGCCA.crl"})})]})]})]})]})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"2"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnId"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-ce-extKeyUsage"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"critical"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"false"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnValue"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-kp-serverAuth"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"1"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-kp-clientAuth"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"2"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"2.16.840.1.113730.4.1"})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"3"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnId"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-pe-authorityInfoAccess"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"critical"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\Boolean"}),(0,r.jsx)("div",{children:"false"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"extnValue"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"0"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"accessMethod"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-ad-ocsp"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"accessLocation"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"uniformResourceIdentifier"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\IA5String"}),(0,r.jsx)("div",{children:(0,r.jsx)(s.a,{href:"http://ocsp.thawte.com",children:"http://ocsp.thawte.com"})})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"1"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"accessMethod"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"id-ad-caIssuers"})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"accessLocation"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"uniformResourceIdentifier"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\IA5String"}),(0,r.jsx)("div",{children:(0,r.jsx)(s.a,{href:"http://www.thawte.com/repository/Thawte_SGC_CA.crt",children:"http://www.thawte.com/repository/Thawte_SGC_CA.crt"})})]})]})]})]})]})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"signatureAlgorithm"}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"algorithm"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\OID"}),(0,r.jsx)("div",{children:"sha1WithRSAEncryption"})]})]})]}),(0,r.jsxs)(n,{children:[(0,r.jsx)("summary",{children:"signature"}),(0,r.jsxs)(n,{open:!0,children:[(0,r.jsx)("summary",{children:"phpseclib4\\File\\ASN1\\Types\\BitString"}),(0,r.jsx)("div",{children:"0021acd5aeca34895ac2ab52d2b234669d7aabeee67cd57ec25c28bb7400c9101f4213fc698a1e24a00200e9ba5bca1904b2d3af01b27e5f14dba6db52b99af3127f7ca29c3b6f997dea500d762312fff7667329b7950aadd88bb2de20e90a70641108c85af17d9eec69a5a5d582d7271e9e56cdd276d5792bf725431c69f0b8f9"})]})]})]})}function d(e={}){let{wrapper:s}={...(0,t.R)(),...e.components};return s?(0,r.jsx)(s,{...e,children:(0,r.jsx)(a,{...e})}):a(e)}var h=n(6540);let o=e=>"number"==typeof e&&Number.isFinite(e),x=e=>e instanceof Date||"string"==typeof e&&!Number.isNaN(Date.parse(e));function p(e,s){return e.accessor?e.accessor(s):s[e.key]}function u({columns:e,data:s,initialSortKey:n,initialSortDir:i="asc",caption:t,striped:c=!0,stickyHeader:l=!1,className:a,stickyOffset:d=0,rowHeight:j=44,nowrapCells:y=!0,getRowId:g,onRowSelect:b}){let[f,v]=(0,h.useState)(n),[w,S]=(0,h.useState)(i),[A,N]=(0,h.useState)(void 0),T=h.useRef(new WeakMap).current,C=h.useRef(1),E=(0,h.useMemo)(()=>
1{if(!f)return s;let n=e.find(e=>e.key===f);if(!n)return s;let i=s.map((e,s)=>({row:e,idx:s}));return i.sort((e,s)=>{var i,r,t;let c=p(n,e.row),l=p(n,s.row),a=n.compare?n.compare(c,l,e.row,s.row):(i=c,r=l,"number"===(t=n.typeHint)?(("string"==typeof i?Number(i):i)??0)-(("string"==typeof r?Number(r):r)??0):"date"===t?((i instanceof Date?i.getTime():Date.parse(String(i??"")))||0)-((r instanceof Date?r.getTime():Date.parse(String(r??"")))||0):o(i)&&o(r)?i-r:x(i)&&x(r)?(new Date(i).getTime()||0)-(new Date(r).getTime()||0):String(i??"").localeCompare(String(r??""),void 0,{numeric:!0,sensitivity:"base"}));return 0!==a?"asc"===w?a:-a:e.idx-s.idx}),i.map(e=>e.row)},[s,f,w,e]);return(0,r.jsx)("div",{className:"overflow-x-auto",children:(0,r.jsxs)("table",{className:["table",c?"table--striped":"",a].filter(Boolean).join(" "),style:{tableLayout:"fixed"},children:[t&&(0,r.jsx)("caption",{style:{captionSide:"top",textAlign:"left"},children:t}),(0,r.jsxs)("thead",{style:l?{position:"sticky",top:d,zIndex:1,background:"var(--ifm-table-stripe-background)"}:void 0,children:[(()=>{let s=e.map(e=>e.bandLabel??null);if(!s.some(e=>null!=e))return null;let n=[],i=0;for(;i<e.length;){let t=s[i],c=i+1;for(;c<e.length&&s[c]===t;)c++;n.push((0,r.jsx)("th",{colSpan:c-i,style:{textAlign:"center",height:j},children:t??""},`band-${i}`)),i=c}return(0,r.jsx)("tr",{children:n})})(),(0,r.jsx)("tr",{children:e.map(e=>{let s=f===e.key?w:void 0,n={textAlign:e.align??"left",cursor:!1===e.sortable?"default":"pointer",width:e.width,whiteSpace:"nowrap",userSelect:"none",height:j};return(0,r.jsx)("th",{scope:"col","aria-sort":s?"asc"===s?"ascending":"descending":"none",style:n,children:(0,r.jsxs)("button",{type:"button",onClick:()=>(function(e,s=!0){s&&v(s=>s===e?(S(e=>e&&"asc"===e?"desc":"asc"),s):(S("asc"),e))})(e.key,!1!==e.sortable),title:!1===e.sortable?void 0:"Click to sort","aria-label":"string"==typeof e.header&&""===e.header.trim()?`Sort by ${e.key}`:void 0,style:{all:"unset",display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%",height:"100%",padding:"0 8px",boxSizing:"border-box"},children:[(0,r.jsx)("span",{style:{flex:1},children:e.header}),!1===e.sortable?null:(0,r.jsx)(m,{dir:s})]})},e.key)})})]}),(0,r.jsx)("tbody",{children:E.map((s,n)=>{let i=g?g(s,n):(T.has(s)||T.set(s,C.current++),T.get(s)),t=A===i;return(0,r.jsx)("tr",{role:"button",tabIndex:0,onClick:()=>{N(i),b?.(s,i)},onKeyDown:e=>{("Enter"===e.key||" "===e.key)&&(e.preventDefault(),N(i),b?.(s,i))},style:{height:j,background:t?"var(--ifm-color-emphasis-200)":void 0,cursor:"pointer"},children:e.map(e=>{let n=p(e,s),i={textAlign:e.align??"left",height:j,verticalAlign:"middle",...y?{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}:{}};return(0,r.jsx)("td",{style:i,title:String(n??""),children:e.render?e.render(n,s):String(n??"")},e.key)})},String(i))})})]})})}function m({dir:e}){return(0,r.jsxs)("svg",{width:"14",height:"14",viewBox:"0 0 24 24",role:"img","aria-label":e?`Sorted ${e}`:"Not sorted",children:[(0,r.jsx)("path",{d:"M7 10l5-5 5 5H7z",fill:"currentColor",opacity:e?1:.35}),(0,r.jsx)("path",{d:"M7 14h10l-5 5-5-5z",fill:"currentColor",opacity:e?1:.35})]})}let j=[{name:"Eager Loading",memory:"323mb",search1:"2.01s",search2:"0.01s"},{name:"Lazy Loading",memory:"151mb",search1:"0.44s",search2:"0.08s"},{name:"Lazy Loading (with cache clearing)",memory:"43mb",search1:"0.44s",search2:"0.36s"},{name:"phpseclib v3",memory:"290mb",search1:"1.30s",search2:"0.01s"}];function y(e){if("string"!=typeof e)return Number(e??0);let s=parseFloat(e.replace(/[^0-9.]/g,""));return Number.isFinite(s)?s:0}let g=[{key:"name",header:"",bandLabel:""},{key:"memory",header:"Peak Memory",bandLabel:"",align:"right",typeHint:"number",accessor:e=>y(e.memory),render:(e,s)=>s.memory},{key:"search1",header:"Initial",bandLabel:"Search",typeHint:"number",accessor:e=>y(e.search1),render:(e,s)=>s.search1},{key:"search2",header:"Subsequent",bandLabel:"Search",typeHint:"number",accessor:e=>y(e.search2),render:(e,s)=>s.search2}],b={title:"Deep Dive: ASN1Constructed Objects"},f,v={},w=[{value:"Overview",id:"overview",level:2},{value:"getEncoded()",id:"getencoded",level:2},{value:"Cache Invalidation",id:"cache-invalidation",level:3},{value:"Loading through ASN1:",id:"loading-through-asn1",level:4},{value:"Loading through X509:",id:"loading-through-x509",level:4},{value:"Holding a reference into the structure:",id:"holding-a-reference-into-the-structure",level:4},{value:"Lazy and Eager Loading",id:"lazy-and-eager-loading",level:2},{value:"Strategy 1: Eager Loading",id:"strategy-1-eager-loading",level:4},{value:"Strategy 2: Standard Lazy Loading",id:"strategy-2-standard-lazy-loading",level:4},{value:"Strategy 3: Optimized Lazy Loading",id:"strategy-3-optimized-lazy-loading",level:4},{value:"Comparison Summary",id:"comparison-summary",level:3},{value:"Security Implications",id:"security-implications",level:3},{value:"Example Attack Payload",id:"example-attack-payload",level:4},{value:"Error Concealment",id:"error-concealment",level:3},{value:"Error Pinpointing",id:"error-pinpointing",level:2},{value:"What MalformedData Can and Can't Tell You",id:"what-malformeddata-can-and-cant-tell-you",level:3},{value:"A blob needs a key to live under",id:"a-blob-needs-a-key-to-live-under",level:4},{value:"A type mismatch can silently truncate everything after it",id:"a-type-mismatch-can-silently-truncate-everything-after-it",level:4},{value:"toArray() is not a transcript of the DER",id:"toarray-is-not-a-transcript-of-the-der",level:4},{value:"Cross-Checking with openssl asn1parse",id:"cross-checking-with-openssl-asn1parse",level:3},{value:"Bypassing Encoding with ASN1\\Element",id:"bypassing-encoding-with-asn1element",level:2},{value:"Fuzzing with malformed values",id:"fuzzing-with-malformed-values",level:3},{value:"Array-Like Interface",id:"array-like-interface",level:2},{value:"Wrapping Constructed",id:"wrapping-constructed",level:2}];function S(e){let s={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(s.h2,{id:"overview",children:"Overview"}),"\n",(0,r.jsxs)(s.p,{children:["A ",(0,r.jsx)(s.code,{children:"\\phpseclib4\\File\\ASN1\\Constructed"})," object (hereinafter referred to as ",(0,r.jsx)(s.code,{children:"Constructed"}),") most typically corresponds to either an ",(0,r.jsx)(s.a,{href:"https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequence.html",children:"ASN.1 SEQUENCE"})," or an ",(0,r.jsx)(s.a,{href:"https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/set.html",children:"ASN.1 SET"})," and contains one or more child elements. They're essentially ",(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Tree_(abstract_data_type)",children:"trees"})," whose leaf nodes are non-",(0,r.jsx)(s.code,{children:"Constructed"})," instances of ",(0,r.jsx)(s.code,{children:"\\phpseclib4\\File\\ASN1\\Types\\BaseType"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:["The following code sample shows how to load an X.509 certificate as a ",(0,r.jsx)(s.code,{children:"Constructed"})," object:"]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\ASN1;\nuse phpseclib4\\File\\ASN1\\Maps\\Certificate;\n\n$x509 = ASN1::extractBER(file_get_contents('google.crt'));\n$x509 = ASN1::decodeBER($x509);\n$x509 = ASN1::map($x509, Certificate::MAP);\n\nprint_r($x509);\n"})}),"\n",(0,r.jsxs)("sup",{children:["(",(0,r.jsx)(s.a,{href:"pathname:///asn1/google.crt.txt",children:"download google.crt"}),")"]}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"ASN1::extractBER()"})," is only needed because the X.509 certificate is a base64-encoded ",(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail",children:"PEM"}),". If it were a DER (",(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/X.690#DER_encoding",children:"Distinguished Encoding Rules"}),", a subset of the ",(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/X.690#BER_encoding",children:"Basic Encoding Rules"}),") the call would be unnecessary."]}),"\n",(0,r.jsx)(s.p,{children:"The output looks like this:"}),"\n",(0,r.jsx)(l,{}),"\n",(0,r.jsxs)(s.p,{children:["Superficially this looks identical to the ",(0,r.jsx)(s.a,{href:"x509#reading-certificates",children:"X509: Reading Certificates"})," example, but there are a few key differences:"]}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsx)(s.p,{children:(0,r.jsx)(s.strong,{children:"Helper methods are not defined."})}),"\n",(0,r.jsxs)(s.p,{children:["None of the helper methods (",(0,r.jsx)(s.code,{children:"getPublicKey()"}),", ",(0,r.jsx)(s.code,{children:"listExtensions()"}),", ",(0,r.jsx)(s.code,{children:"getExtension()"}),", ",(0,r.jsx)(s.code,{children:"setExtension()"}
1),", etc.) exist on a Constructed object. Constructed is intentionally generic and doesn't know enough about any specific data structure to define them."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsx)(s.p,{children:(0,r.jsx)(s.strong,{children:"subjectPublicKey is a BitString."})}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']"})," is an instance of ",(0,r.jsx)(s.code,{children:"\\phpseclib4\\File\\ASN1\\Types\\BitString"})," rather than a ",(0,r.jsx)(s.code,{children:"phpseclib4\\Crypt\\Common\\PublicKey"}),". ",(0,r.jsx)(s.code,{children:"Constructed"})," doesn't know how to decode the bit string into a structured public key."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsx)(s.p,{children:(0,r.jsx)(s.strong,{children:"Extensions are OctetStrings."})}),"\n",(0,r.jsxs)(s.p,{children:["None of the entries in ",(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']['extensions']"})," are fleshed out. For example, ",(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']['extensions'][1]['extnValue']"})," (",(0,r.jsx)(s.code,{children:"id-ce-cRLDistributionPoints"}),") is a ",(0,r.jsx)(s.code,{children:"\\phpseclib4\\File\\ASN1\\Types\\OctetString"})," rather than a parsed distribution-points structure - again, because ",(0,r.jsx)(s.code,{children:"Constructed"})," has no knowledge of individual extension formats."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsx)(s.p,{children:(0,r.jsx)(s.strong,{children:"getEncoded() returns an empty string after changes."})}),"\n",(0,r.jsxs)(s.p,{children:["Modifying any value invalidates the cached encoding, and ",(0,r.jsx)(s.code,{children:"Constructed"})," has no way to regenerate it. See ",(0,r.jsx)(s.a,{href:"#getencoded",children:"getEncoded()"})," and ",(0,r.jsx)(s.a,{href:"#cache-invalidation",children:"Cache Invalidation"})," for details."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsx)(s.p,{children:(0,r.jsx)(s.strong,{children:"Unmapped substructures stay as Constructed."})}),"\n",(0,r.jsxs)(s.p,{children:["When the schema marks a field as ",(0,r.jsx)(s.code,{children:"ASN1::TYPE_ANY"})," - meaning the bytes inside could be any valid ASN.1 structure - the decoder has no map to apply, so it leaves the field as a raw ",(0,r.jsx)(s.code,{children:"Constructed"}),". This rarely surfaces in X.509 (most ",(0,r.jsx)(s.code,{children:"ANY"})," fields there are leaf-shaped) but it's pervasive in ",(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Cryptographic_Message_Syntax",children:"CMS"}),", where the format is built around content-type-tagged payloads that can only be decoded once you know which map to apply."]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:["Despite these differences, ",(0,r.jsx)(s.code,{children:"phpseclib4\\File\\X509"})," is described as a thin wrapper around ",(0,r.jsx)(s.code,{children:"Constructed"}),". That means both ",(0,r.jsx)(s.code,{children:"X509"})," - and any class built the same way - share the following features:"]}),"\n",(0,r.jsx)(s.h2,{id:"getencoded",children:"getEncoded()"}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"getEncoded()"})," returns the original BER-encoded bytes of an object. For our earlier X.509 certificate, that's just:"]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"$x509->getEncoded();\n"})}),"\n",(0,r.jsx)(s.p,{children:"The interesting use case is signature validation. Let's start with a CSR, since CSRs avoid the complications of extensions, validity periods, and the rest of what comes with X.509."}),"\n",(0,r.jsxs)(s.p,{children:["The straightforward path is ",(0,r.jsx)(s.a,{href:"/docs/file/csr#validating-signatures",children:"$csr->validateSignature()"}),". The manual equivalent looks like this:"]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\CSR;\n\n$csr = CSR::load(file_get_contents('csr.csr'));\n\n$result = $csr->getPublicKey()->withHash('sha1')->verify(\n $csr['certificationRequestInfo']->getEncoded(),\n substr($csr['signature'], 1)\n);
1\n\necho $result ? 'valid' : 'invalid';\n"})}),"\n",(0,r.jsxs)("sup",{children:["(",(0,r.jsx)(s.a,{href:"pathname:///asn1/csr.txt",children:"download csr.csr"}),")"]}),"\n",(0,r.jsxs)(s.p,{children:["The output is ",(0,r.jsx)(s.code,{children:"valid"}),"."]}),"\n",(0,r.jsx)(s.p,{children:"A few notes on the example:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"CSR::load() is used rather than the raw ASN1 methods."})," The resultant CSR object exposes ",(0,r.jsx)(s.code,{children:"getPublicKey()"})," directly, which keeps the example short."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"The hash is hardcoded to 'sha1'."})," In principle it could be read from ",(0,r.jsx)(s.code,{children:"$csr['signatureAlgorithm']['algorithm']"}),", but that pattern doesn't generalize. With X.509 certificates, for example, the relevant algorithm lives on the \u2217signing\u2217 certificate, not the one being verified - and at that point you should be using ",(0,r.jsx)(s.code,{children:"$x509->validateSignature()"})," anyway."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"substr($csr['signature'], 1) strips the leading octet."})," Per ",(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/X.690",children:"X.690"})," \xa7 8.6.2.2:"]}),"\n",(0,r.jsxs)(s.blockquote,{children:["\n",(0,r.jsx)(s.p,{children:"The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven."}),"\n"]}),"\n",(0,r.jsx)(s.p,{children:"Strictly speaking the leading octet could be nonzero, but I've never seen a real-world DER signature where it is, and handling the general case would only bloat the example."}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(s.h3,{id:"cache-invalidation",children:"Cache Invalidation"}),"\n",(0,r.jsxs)(s.p,{children:["What happens to ",(0,r.jsx)(s.code,{children:"->getEncoded()"})," if you change a value inside an already-encoded structure ultimately depends on which class owns the data."]}),"\n",(0,r.jsx)(s.h4,{id:"loading-through-asn1",children:"Loading through ASN1:"}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\ASN1;\nuse phpseclib4\\File\\ASN1\\Maps\\Certificate;\n\n$x509 = ASN1::extractBER(file_get_contents('google.crt'));\n$x509 = ASN1::decodeBER($x509);\n$x509 = ASN1::map($x509, Certificate::MAP);\n\necho strlen($x509->getEncoded()) . \"\\n\";\n$x509['tbsCertificate']['serialNumber'] = new BigInteger('deadbeef', 16);\necho strlen($x509->getEncoded()) . \"\\n\";\n"})}),"\n",(0,r.jsxs)("sup",{children:["(",(0,r.jsx)(s.a,{href:"pathname:///asn1/google.crt.txt",children:"download google.crt"}),")"]}),"\n",(0,r.jsxs)(s.p,{children:["Output: ",(0,r.jsx)(s.code,{children:"805"}),", then ",(0,r.jsx)(s.code,{children:"0"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:["Changing the serial number invalidates the cached encoding, and the resulting object - an instance of ",(0,r.jsx)(s.code,{children:"Constructed"})," - has no machinery to regenerate it."]}),"\n",(0,r.jsx)(s.h4,{id:"loading-through-x509",children:"Loading through X509:"}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\X509;\n\n$x509 = X509::load(file_get_contents('google.crt'));\n\necho strlen($x509->getEncoded()) . \"\\n\";\n$x509['tbsCertificate']['serialNumber'] = new BigInteger('deadbeef', 16);\necho strlen($x509->getEncoded()) . \"\\n\";\n"})}),"\n",(0,r.jsxs)(s.p,{children:["Output: ",(0,r.jsx)(s.code,{children:"805"}),", then ",(0,r.jsx)(s.code,{children:"794"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:["The X509 class re-encodes changed sub-structures on every ",(0,r.jsx)(s.a,{href:"https://www.php.net/manual/en/arrayaccess.offsetget.php",children:(0,r.jsx)(s.code,{children:"offsetGet()"})})," call."]}),"\n",(0,r.jsx)(s.h4,{id:"holding-a-reference-into-the-structure",children:"Holding a reference into the structure:"}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\X509;\n\n$x509 = X509::load(file_get_contents('google.crt'));\n\n$cert = &$x509['tbsCertificate'];\necho strlen($cert->getEncoded()) . \"\\n\"; // outputs 654\n$cert['serialNumber'] = new BigInteger('deadbeef', 16);\necho strlen($cert->getEncoded()) . \"\\n\"; // outputs 0\necho strlen($x509['tbsCertificate']->getEncoded()) . \"\\n\"; // outputs 643\n$cert = &$x509['tbsCertificate'];\necho strlen($cert->getEncoded()) . \"\\n\"; // outputs 643\n"})}),"\n",(0,r.jsxs)(s.p,{children:["Once you take a reference, you're operating on a ",(0,r.jsx)(s.code,{children:"Constructed"})," directly, and the ",(0,r.jsx)(s.code,{children:"X509"})," re-encoding logic is bypassed. Going back through ",(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']"})," re-enters ",(0,r.jsx)(s.code,{children:"X509::offsetGet()"})," and the value is rebuilt; refreshing ",(0,r.jsx)(s.code,{children:"$cert"})," then picks up the rebuilt copy."]}),"\n",(0,r.jsxs)(s.p,{children:[":::caution Why the encoding is cached\n",(0,r.jsx)(s.a,{href:"/docs/file/constructed-example",children:"Constructed: A Case Study"})," covers the gotchas this caching creates, so it's fair to question why we even do it. Avoiding needless re-encoding is one reason, but the decisive one is signature validation. The CSR example above hints at it: ",(0,r.jsx)(s.code,{children:"$csr['certificationRequestInfo']->getEncoded()"})," returns the bytes the signature was computed over, and those bytes must exactly match what the signer signed. Re-encoding doesn't always reproduce the original byte sequence - even under DER - so cac
1hing the original is what keeps that round trip reliable."]}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.a,{href:"https://datatracker.ietf.org/doc/html/rfc4055#page-6",children:"RFC 4055 pp. 6"})," gives a concrete example involving the AlgorithmIdentifier parameters of RSASSA-PSS and RSAES-OAEP:"]}),"\n",(0,r.jsxs)(s.blockquote,{children:["\n",(0,r.jsx)(s.p,{children:"[...] some implementations encode parameters as a NULL element while others omit them entirely. The correct encoding is to omit the parameters field; however, when RSASSA-PSS and RSAES-OAEP were defined, it was done using the NULL parameters rather than absent parameters."}),"\n",(0,r.jsx)(s.p,{children:"All implementations MUST accept both NULL and absent parameters as legal and equivalent encodings.\n:::"}),"\n"]}),"\n",(0,r.jsx)(s.h2,{id:"lazy-and-eager-loading",children:"Lazy and Eager Loading"}),"\n",(0,r.jsxs)(s.p,{children:["By default, ",(0,r.jsx)(s.code,{children:"Constructed"})," objects ",(0,r.jsx)(s.strong,{children:(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Lazy_loading",children:"lazy loaded"})}),". This means the library identifies the boundaries of the ASN.1 structure but defers the actual decoding of values until they are accessed. Calling ",(0,r.jsx)(s.code,{children:"->toArray()"})," on a ",(0,r.jsx)(s.code,{children:"Constructed"})," object will ",(0,r.jsx)(s.strong,{children:"eager load"})," it, converting the entire structure into a standard PHP array immediately."]}),"\n",(0,r.jsxs)(s.p,{children:["To compare these approaches, we'll use ",(0,r.jsx)(s.a,{href:"pathname:///asn1/bigcrl.crl",children:"bigcrl.bin"}),", a ",(0,r.jsx)(s.strong,{children:"2.2MB"})," CRL containing ",(0,r.jsx)(s.strong,{children:"41,326"})," revoked serial numbers, and we'll search for the 40,000th entry (",(0,r.jsx)(s.code,{children:"120cd8"}),")."]}),"\n",(0,r.jsxs)(s.p,{children:["The following function is used for all tests. The performance changes based on whether the input is an array or an object, and whether the ",(0,r.jsx)(s.code,{children:"unset()"})," line is active (which the ",(0,r.jsx)(s.code,{children:"$optimize"})," parameter toggles)."]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\CRL;\nuse phpseclib4\\Math\\BigInteger;\n\nfunction findSN(CRL|array $crl, BigInteger $sn, bool $optimize = false): ?int\n{\n $list = $crl['tbsCertList']['revokedCertificates'];\n $total = count($list);\n for ($i = 0; $i < $total; $i++) {\n if ($list[$i]['userCertificate']->equals($sn)) {\n return $i;\n }\n if ($optimize) {\n // Immediately delete the decoded result to save RAM\n unset($list[$i]->decoded);\n }\n }\n return null;\n}\n"})}),"\n",(0,r.jsx)(s.h4,{id:"strategy-1-eager-loading",children:"Strategy 1: Eager Loading"}),"\n",(0,r.jsx)(s.p,{children:"This strategy decodes every single entry in the CRL before the search even begins."}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:'$sn = new BigInteger(\'120cd8\', 16);\n$crl = CRL::load(file_get_contents(\'bigcrl.bin\'));\n\n$start = microtime(true);\n$eagerCrl = $crl->toArray(); // Decode everything now\nfindSN($eagerCrl, $sn);\n\necho "Time: " . (microtime(true) - $start) . "s\\n";\necho "Peak Memory: " . (memory_get_peak_usage() / 1024 / 1024) . "MB\\n";\n'})}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Time:"})," ~2.0s to load, 0.01s to search."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Peak Memory:"})," ~323MB"]}),"\n"]}),"\n",(0,r.jsx)(s.h4,{id:"strategy-2-standard-lazy-loading",children:"Strategy 2: Standard Lazy Loading"}),"\n",(0,r.jsx)(s.p,{children:"Here, phpseclib only decodes entries as the loop hits them. Once decoded, entries are cached for future access."}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:'$crl = CRL::load(file_get_contents(\'bigcrl.bin\'));\n\n$start = microtime(true);\nfindSN($crl, $sn); // Lazy loading happens inside the loop\n\necho "Time: " . (microtime(true) - $start) . "s\\n";\necho "Peak Memory: " . (memory_get_peak_usage() / 1024 / 1024) . "MB\\n";\n'})}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Time:"})," ~0.44s for the first search, ~0.08s for the second."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Peak Memory:"})," ~151MB"]}),"\n"]}),"\n",(0,r.jsx)(s.h4,{id:"strategy-3-optimized-lazy-loading",children:"Strategy 3: Optimized Lazy Loading"}),"\n",(0,r.jsx)(s.p,{children:"By manually unsetting the decoded property, we prevent the cache from growing."}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:'$crl = CRL::load(file_get_contents(\'bigcrl.bin\'));\n\n$start = microtime(true);\nfindSN($crl, $sn, true); // Use the optimize flag\n\necho "Time: " . (microtime(true) - $start) . "s\\n";\necho "Peak Memory: " . (memory_get_peak_usage() / 1024 / 1024) . "MB\\n";\n'})}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Time:"})," ~0.44s for the first search, ~0.36s for the second."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Peak Memory:"})," ~43MB (less than ",(0,r.jsx)(s.a,{href:"https://www.php.net/manual/en/ini.core.php#ini.sect.resource-limits",children:"PHP's default 128M memory limit"}),")"]}),"\n"]}),"\n",(0,r.jsx)(s.h3,{id:"comparison-summary",children:"Comparison Summary"}),"\n",(0,r.jsx)(s.p,{children:(0,r.jsx)(s.em,{children:"Benchmarks performed on PHP 8.3.14"})}),"\n",(0,r.jsx)(u,{columns:g,data:j,stickyHeader:!0,rowHeight:44,nowrapCells:!1,getRowId:e=>e.name,onRowSelect:(e,s)=>console.log("selected",s,e)}),"\n",(0,r.jsx)(s.h3,{id:"security-implications",children:"Security Implications"}),"\n",(0,r.jsxs)(s.p,{children:["phpseclib v1 - v3 utilize a two-step parsing process for ASN.1 structures. First, they ",(0,r.jsx)(s.strong,{children:"fully decode"}
1)," the entire ASN.1 blob into a nested PHP array. Only after the entire structure is in memory does it attempt to map that data to a specific schema (such as an X.509 Certificate, CRL, or CMS)."]}),"\n",(0,r.jsx)(s.p,{children:'This creates a potential denial-of-service vector. An attacker can craft a technically "valid" ASN.1 blob that is packed with high-complexity elements - like many large Object Identifiers (OIDs) - but is ultimately invalid as a certificate.'}),"\n",(0,r.jsx)(s.h4,{id:"example-attack-payload",children:"Example Attack Payload"}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\ASN1;\nuse phpseclib4\\Math\\BigInteger;\n\n$map = [\n 'type' => ASN1::TYPE_SEQUENCE,\n 'min' => 1,\n 'max' => -1,\n 'children' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]\n];\n\n$raw = [];\nfor ($i = 0; $i < 50; $i++) {\n // floor(3583 * 8 / 7) + 2 == 4096\n $oid = '2.25.' . BigInteger::random(3583 << 3);\n $raw[] = $oid;\n}\n\n$encoded = ASN1::encodeDER($raw, $map);\n"})}),"\n",(0,r.jsxs)(s.p,{children:["Prior to the security patches in ",(0,r.jsx)(s.strong,{children:"3.0.52, 2.0.54, and 1.0.29"}),", attempting to load this payload via ",(0,r.jsx)(s.code,{children:"$x509->loadX509($encoded)"})," could hang the CPU for ",(0,r.jsx)(s.strong,{children:"30 seconds or more"}),'. Even though the structure isn\'t a valid certificate, the "eager" parser decodes every malicious OID before the mapping phase fails.']}),"\n",(0,r.jsxs)(s.p,{children:["The fix, for those versions of phpseclib, was to reduce the maximum allowed size for a single OID from 4096 bytes to ",(0,r.jsx)(s.strong,{children:"128 bytes"}),", which is more than sufficient for any legitimate OID while preventing the bitwise shifting overhead from becoming a DoS vector."]}),"\n",(0,r.jsxs)(s.p,{children:["In contrast, ",(0,r.jsx)(s.strong,{children:"phpseclib v4"})," is immune to this class of issue by design because it decodes ",(0,r.jsx)(s.strong,{children:"on demand"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:["When you call ",(0,r.jsx)(s.code,{children:"X509::load($encoded)"}),', the library does not decode the interior elements immediately. Instead, it creates a "map" of the structure\'s offsets. An OID is only decoded if the application explicitly attempts to access that specific field (e.g., fetching the signature algorithm).']}),"\n",(0,r.jsxs)(s.p,{children:["With the above example payload, which does not match the structural template of an X509 certificate, an exception is thrown ",(0,r.jsx)(s.strong,{children:"instantly"}),' before it ever reaches the OID decoding logic. This "fail-fast" behavior provides a massive security boost, as malicious interior data is never even processed.']}),"\n",(0,r.jsxs)(s.p,{children:[":::caution Why OID parsing is expensive\nIn an OID like ",(0,r.jsx)(s.code,{children:"1.2.840.10045.3.1.7"}),", each number is called an ",(0,r.jsx)(s.strong,{children:"arc"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:["Unlike a fixed-width integer, arc length is arbitrary. Using a ",(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Variable-length_quantity",children:"Variable Length Quantity (Base 128)"})," encoding, the parser must inspect the 8th bit of every byte:"]}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["If it's ",(0,r.jsx)(s.strong,{children:"1"}),", the arc continues to the next byte."]}),"\n",(0,r.jsxs)(s.li,{children:["If it's ",(0,r.jsx)(s.strong,{children:"0"}),", the arc ends."]}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:['To calculate the final value, the parser must strip those 8th bits and "stitch" the remaining 7-bit chunks together. For massive, attacker-crafted OIDs, this requires intensive bit-shifting and BigInteger math. While most common arcs are small, the parser must be prepared for much larger values, like ',(0,r.jsx)(s.a,{href:"https://healthcaresecprivacy.blogspot.com/2011/02/creating-and-using-unique-id-uuid-oid.html",children:"128-bit UUIDs encoded under the 2.25 arc"}),". Because these values exceed the 64-bit limit of native PHP integers, phpseclib uses BigInteger to ensure standards compliance and prevent overflow errors.\n:::"]}),"\n",(0,r.jsx)(s.h3,{id:"error-concealment",children:"Error Concealment"}),"\n",(0,r.jsx)(s.p,{children:"A drawback of lazy loading is that errors can go unnoticed."}),"\n",(0,r.jsx)(s.p,{children:"Consider this lazy-loaded X.509 certificate:"}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\ASN1;\nuse phpseclib4\\File\\X509;\n\n$x509 = file_get_contents('google.crt');\n$x509 = ASN1::extractBER($x509);\n$x509[100] = '.';\n$x509 = X509::load($x509);\nprint_r($x509['tbsCertificate']['extensions'][1]->toArray());\n"})}),"\n",(0,r.jsxs)("sup",{children:["(",(0,r.jsx)(s.a,{href:"pathname:///asn1/google.crt.txt",children:"download google.crt"}),")"]}),"\n",(0,r.jsxs)(s.p,{children:["The ",(0,r.jsx)(s.code,{children:"id-ce-cRLDistributionPoints"})," extension is displayed without issue, despite the corruption introduced at byte 100."]}),"\n",(0,r.jsx)(s.p,{children:"Now consider the eagerly loaded equivalent:"}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\ASN1;\nuse phpseclib4\\File\\X509;\n\n$x509 = file_get_contents('google.crt');\n$x509 = ASN1::extractBER($x509);\n$x509[100] = '.';\nprint_r(X509::load($x509)->toArray());\n"})}),"\n",(0,r.jsx)(s.p,{children:"This throws an exception."}),"\n",(0,r.jsxs)(s.p,{children:["With lazy loading, an exception is raised only if you actually touch the malformed part of the certificate. ",(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']['extensions'][1]->toArray()"})," succeeds because byte 100 falls outside that subtree; ",(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']->toArray()"})," fails because the corruption is somewhere within ",(0,r.jsx)(s.code,{children:"tbsCertificate"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:["But ",(0,r.jsx)(s.em,{children:"where"}),", exactly? That's the subject of the next section."]}),"\n",(0,r.jsx)(s.h2,{id:"error-pinpointing",children:"Error Pinpointing"}),"\n",(0,r.jsx)(s.p,{children:"Consider the following code:"}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\ASN1;\nuse phpseclib4\\File\\X509;\n\nASN1::enableBlobsOnBadDecodes();\n\n$x509 = file_get_contents('google.crt');\n$x509 = ASN1::extractBER($x509);\n$x509[100] = '.';\n$x509 = X509::load($x509)->toArray();\nprint_r($x509);\n"})}),"\n",(0,r.jsxs)(s.p,{children:["Without the ",(0,r.jsx)(s.code,{children:"ASN1::enableBlobsOnBadDecodes()"})," call, this would throw an exception. With it, we get:"]}),"\n",(0,r.jsx)(d,{}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']['issuer']"})," is now an instance of ",(0,r.jsx)(s.code,{children:"phpseclib4\\File\\ASN1\\MalformedData"})," - pinpointing exactly where the corruption is."]}),"\n",(0,r.jsx)(s.h3,{id:"what-malformeddata-can-and-cant-tell-you",children:"What MalformedData Can and Can't Tell You"}),"\n",(0,r.jsxs)(s.p,{children:["The example above is the happy path: one corrupted field, cleanly identified. ",(0,r.jsx)(s.code,{children:"MalformedData"})," is genuinely useful, but it's a narrower tool than \"show me everything wrong with this file,\" and it's worth understanding why before you rely on it."]}),"\n",(0,r.jsxs)(s.p,{children:["The mental model that predicts its behavior is this: ",(0,r.jsx)(s.strong,{children:"the decoder is trying as hard as it can to fit the bytes into the shape it was given, and it will never invent a new shape."})," Everything below follows from that."]}),"\n",(0,r.jsx)(s.h4,{id:"a-blob-needs-a-key-to-live-under",children:"A blob needs a key to live under"}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"MalformedData"})," is substituted ",(0,r.jsx)(s.em,{children:"into the mapped structure"}),", which means it can only appear where the map has a slot to put it. ",(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']['issuer']"})," worked in the example above because ",(0,r.jsx)(s.code,{children:"issuer"})," is a field the map knows about."]}),"\n",(0,r.jsxs)(s.p,{children:["Data that fails to decode but doesn't correspond to any expected field has no array key to be associated with, so it simply doesn't appear. There is no ",(0,r.jsx)(s.code,{children:"[unexpected data]"})," entry in the output - extra bytes are silently absent rather than flagged."]}),"\n",(0,r.jsx)(s.p,{children:"The degenerate case is an element with no valid tag or length at all, which can't be delimited even as a blob:"}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\{ASN1\\Element, X509};\n\n$x509 = new X509();
1\n$x509['tbsCertificate']['extensions'] = new Element('zzzzzzzzzzzz');\n$x509 = \"$x509\";\n\nX509::load($x509)->toArray(); // throws UnexpectedValueException\n"})}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"Element('zzzzzzzzzzzz')"})," has no valid tag and - more importantly - no valid length, so the parser has nothing to measure. ",(0,r.jsx)(s.code,{children:"ASN1::enableBlobsOnBadDecodes()"})," doesn't help here."]}),"\n",(0,r.jsx)(s.h4,{id:"a-type-mismatch-can-silently-truncate-everything-after-it",children:"A type mismatch can silently truncate everything after it"}),"\n",(0,r.jsxs)(s.p,{children:["When the map expects one tag and the encoding has another, the decoder doesn't blob the mismatch. It treats the element as ",(0,r.jsx)(s.em,{children:"not a match"}),", fails to find what it wanted, and stops - and because the unmatched element has no key to map to, it isn't reported either. Enabling blobs mode does not change this."]}),"\n",(0,r.jsxs)(s.p,{children:["Here's a ",(0,r.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Kerberos_(protocol)",children:"Kerberos"})," ",(0,r.jsx)(s.code,{children:"AS-REP"})," where ",(0,r.jsx)(s.code,{children:"crealm"})," is declared as an ",(0,r.jsx)(s.code,{children:"OCTET STRING"})," but the encoding actually uses a ",(0,r.jsx)(s.code,{children:"GENERALSTRING"}),":"]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\ASN1;\n\n$KDC_REP = [\n 'type' => ASN1::TYPE_SEQUENCE,\n 'children' => [\n 'pvno' => [\n 'constant' => 0,\n 'optional' => true,\n 'explicit' => true,\n 'type' => ASN1::TYPE_INTEGER,\n ],\n 'msg-type' => [\n 'constant' => 1,\n 'optional' => true,\n 'explicit' => true,\n 'type' => ASN1::TYPE_INTEGER,\n ],\n 'padata' => [\n 'constant' => 2,\n 'optional' => true,\n 'explicit' => true,\n 'min' => 0,\n 'max' => -1,\n 'type' => ASN1::TYPE_SEQUENCE,\n 'children' => $PA_DATA,\n ],\n 'crealm' => [ // the DER has a GENERALSTRING here, not an OCTET STRING\n 'constant' => 3,\n 'optional' => true,\n 'explicit' => true,\n 'type' => ASN1::TYPE_OCTET_STRING,\n ],\n // ... cname, ticket, enc-part ...\n ],\n];\n\nASN1::enableBlobsOnBadDecodes();\n\n$decoded = ASN1::decodeBER($der);\nprint_r(ASN1::map($decoded, $AS_REP)->toArray());\n"})}),"\n",(0,r.jsxs)(s.p,{children:["The output contains ",(0,r.jsx)(s.code,{children:"pvno"}),", ",(0,r.jsx)(s.code,{children:"msg-type"})," and ",(0,r.jsx)(s.code,{children:"padata"})," - and then nothing. The decoder never finds an ",(0,r.jsx)(s.code,{children:"OCTET STRING"})," under an explicit ",(0,r.jsx)(s.code,{children:"cont [3]"}),", so it considers ",(0,r.jsx)(s.code,{children:"cont [3]"})," to be extra data, stops, and never looks at ",(0,r.jsx)(s.code,{children:"cname"}),", ",(0,r.jsx)(s.code,{children:"ticket"})," or ",(0,r.jsx)(s.code,{children:"enc-part"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:["Note what the diagnostic signal is in this case. There's no ",(0,r.jsx)(s.code,{children:"MalformedData"})," anywhere. The clue is that the output is ",(0,r.jsx)(s.em,{children:"short"}),": the last field that decoded successfully sits immediately before the mismatch."]}),"\n",(0,r.jsx)(s.h4,{id:"toarray-is-not-a-transcript-of-the-der",children:"toArray() is not a transcript of the DER"}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"toArray()"})," and ",(0,r.jsx)(s.code,{children:"__debugInfo()"})," render the ",(0,r.jsx)(s.em,{children:"mapped"})," view, and part of mapping is populating defaults for fields the schema declares a default for. Those defaults are written into the array output rather than being applied silently behind the scenes."]}),"\n",(0,r.jsxs)(s.p,{children:["That has a confusing consequence when a field fails to decode: the decoder skips ahead looking for something that ",(0,r.jsx)(s.em,{children:"does"})," match, and a skipped-over field with a default shows up carrying that default."]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\{ASN1, ASN1\\Element, X509};\n\n$x509 = new X509();
1\n$x509['tbsCertificate']['version'] = new Element('zzzzzzz');\n$x509 = \"$x509\";\n\nASN1::enableBlobsOnBadDecodes();\n\nprint_r(X509::load($x509)->toArray());\n"})}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{children:"Array\n(\n [tbsCertificate] => Array\n (\n [version] => phpseclib4\\File\\ASN1\\Types\\Integer Object\n (\n [value] => v1\n )\n\n [serialNumber] => phpseclib4\\File\\ASN1\\MalformedData Object\n (\n [value] => 7a7a7a7a7a7a7a\n )\n\n [signature] => phpseclib4\\File\\ASN1\\MalformedData Object\n (\n [value] => 34323437393530343431303534333239363136...\n )\n\n [issuer] => phpseclib4\\File\\ASN1\\MalformedData Object\n (\n [value] => 060100\n )\n\n [validity] => Array\n (\n )\n ...\n )\n ...\n)\n"})}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"version"})," reads as ",(0,r.jsx)(s.code,{children:"v1"})," despite having been set to ",(0,r.jsx)(s.code,{children:"zzzzzzz"}),", because the decoder couldn't read it, moved on until it found a matching element, and then filled the slot with the schema default."]}),"\n",(0,r.jsxs)(s.p,{children:["Notice also that the corruption ",(0,r.jsx)(s.em,{children:"shifts"})," the fields after it: the ",(0,r.jsx)(s.code,{children:"7a7a\u2026"})," bytes surface under ",(0,r.jsx)(s.code,{children:"serialNumber"}),", not under ",(0,r.jsx)(s.code,{children:"version"}),". ",(0,r.jsx)(s.code,{children:"MalformedData"})," tells you roughly where the trouble starts, not precisely which field was corrupted."]}),"\n",(0,r.jsxs)(s.p,{children:[":::caution Don't infer presence from output\nBecause defaults are materialized into the array, you can't conclude that a field was present in the underlying DER just because it appears in ",(0,r.jsx)(s.code,{children:"toArray()"})," output. If you need to know what's actually encoded, look at the bytes.\n:::"]}),"\n",(0,r.jsx)(s.h3,{id:"cross-checking-with-openssl-asn1parse",children:"Cross-Checking with openssl asn1parse"}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"MalformedData"})," is one instrument, not the whole panel. The natural second opinion is a schema-less structural dump, and ",(0,r.jsx)(s.code,{children:"openssl asn1parse"})," is the usual choice:"]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{children:"openssl asn1parse -in test.pem -inform DER\n"})}),"\n",(0,r.jsxs)(s.p,{children:["For the Kerberos example above, the way to find the bug is to look for ",(0,r.jsx)(s.code,{children:"cont [3]"})," at a depth of 2:"]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{children:" 67:d=2 hl=2 l= 15 cons: cont [ 3 ]\n 69:d=3 hl=2 l= 13 prim: GENERALSTRING\n"})}),"\n",(0,r.jsxs)(s.p,{children:["which shows immediately that the field is a ",(0,r.jsx)(s.code,{children:"GENERALSTRING"}),", not the ",(0,r.jsx)(s.code,{children:"OCTET STRING"})," the map declared."]}),"\n",(0,r.jsxs)(s.p,{children:["The two tools cover different failure modes, and the split follows from where each one sits in the pipeline. OpenSSL, like phpseclib, works in two passes: first it decodes the ASN.1 tag/length structure, then it maps that structure onto X.509 (or whatever the target schema is). ",(0,r.jsx)(s.code,{children:"asn1parse"})," exposes only the first pass. So:"]}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"When the ASN.1 decode itself fails"})," - bad tags, bogus lengths, truncated elements - ",(0,r.jsx)(s.code,{children:"asn1parse"})," is failing on the same pass and has little to show you. This is where ",(0,r.jsx)(s.code,{children:"MalformedData"})," earns its keep: phpseclib keeps going and hands you the surrounding structure plus the raw bytes of the part that didn't decode."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"When the ASN.1 decodes cleanly but doesn't fit the schema"}
1)," - wrong type, missing required field, unexpected tag - ",(0,r.jsx)(s.code,{children:"asn1parse"})," is likely the easier tool, because the structure it prints is complete and correct and you only have to compare it against what the map expects. This is also exactly the case where blobs mode is least informative, per the truncation behavior described above."]}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:["Staying inside PHP, a schema-less ",(0,r.jsx)(s.code,{children:"ASN1::decodeBER()"})," followed by ",(0,r.jsx)(s.code,{children:"toArray()"})," is the closest analogue to ",(0,r.jsx)(s.code,{children:"asn1parse"})," - phpseclib's own view of the raw tag structure with no mapping imposed on it."]}),"\n",(0,r.jsx)(s.h2,{id:"bypassing-encoding-with-asn1element",children:"Bypassing Encoding with ASN1\\Element"}),"\n",(0,r.jsxs)(s.p,{children:["Everything discussed so far has been about ",(0,r.jsx)(s.em,{children:"reading"})," ASN.1 - lazy decoding, error pinpointing, walking a structure that phpseclib already understands. There's a corresponding tool for the ",(0,r.jsx)(s.em,{children:"write"})," side: ",(0,r.jsx)(s.code,{children:"\\phpseclib4\\File\\ASN1\\Element"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.code,{children:"Element"}),' is a wrapper that tells phpseclib "these bytes are already encoded - don\'t touch them." Anywhere phpseclib would otherwise encode a value (a DN, an extension value, even a top-level field via ',(0,r.jsx)(s.a,{href:"#array-like-interface",children:"ArrayAccess"}),"), an ",(0,r.jsx)(s.code,{children:"Element"})," is written through verbatim. That makes it the universal escape hatch for:"]}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Writing custom extensions without registering them."})," ",(0,r.jsx)(s.a,{href:"/docs/file/x509#custom-extensions",children:(0,r.jsx)(s.code,{children:"X509::registerExtension()"})})," is the standard path for custom extensions and gives you structured read access in addition to write support. ",(0,r.jsx)(s.code,{children:"Element"})," is the shorter path when you only need to write and don't care about structured read-back - eg. producing a fixture cert. Encode the bytes yourself, wrap them in ",(0,r.jsx)(s.code,{children:"Element"}),", pass to ",(0,r.jsx)(s.code,{children:"setExtension()"}),"."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Fuzzing parsers."})," Real-world parsers disagree about ASN.1 edge cases, and you may want to produce intentionally-malformed certs to see how each one reacts. ",(0,r.jsx)(s.code,{children:"Element"})," lets you put arbitrary bytes anywhere, including bytes that don't decode as valid ASN.1 or bytes whose outer ASN.1 type is wrong for the slot."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Interop testing against external implementations"})," that have their own opinion about a structure."]}),"\n"]}),"\n",(0,r.jsx)(s.h3,{id:"fuzzing-with-malformed-values",children:"Fuzzing with malformed values"}),"\n",(0,r.jsxs)(s.p,{children:["Because ",(0,r.jsx)(s.code,{children:"Element"})," bypasses encoding, you can put ",(0,r.jsx)(s.em,{children:"anything"})," in any slot - including bytes that violate the schema for that field. As an extreme example, here's how to swap a certificate's signature from a ",(0,r.jsx)(s.code,{children:"BIT STRING"})," to an ",(0,r.jsx)(s.code,{children:"OCTET STRING"})," to see how downstream parsers react:"]}),"\n",(0,r.jsx)(s.pre,{children:(0,r.jsx)(s.code,{className:"language-php",children:"use phpseclib4\\File\\{ASN1, ASN1\\Element, X509};\n\n$x509 = X509::load($pem);\n\n// Replace the signature with the same bytes but tagged as OCTET STRING:\n$x509['signature'] = new Element(\n ASN1::encodeDER(\"$x509[signature]\", ['type' => ASN1::TYPE_OCTET_STRING])\n);\n\necho $x509;\n// Outputs a PEM-encoded cert whose signature field is the wrong ASN.1 type.\n// Most parsers will reject it; some will silently accept it; some will crash.\n// Useful data either way.\n"})}),"\n",(0,r.jsxs)(s.p,{children:["This composes with the ",(0,r.jsx)(s.a,{href:"#error-pinpointing",children:"Error Pinpointing"})," tooling on the read side: produce a deliberately-broken cert with ",(0,r.jsx)(s.code,{children:"Element"}),", hand it to ",(0,r.jsx)(s.code,{children:"X509::load()"}),", then turn on ",(0,r.jsx)(s.code,{children:"ASN1::enableBlobsOnBadDecodes()"})," to see exactly which slot a strict parser objects to."]}),"\n",(0,r.jsxs)(s.p,{children:["Use ",(0,r.jsx)(s.code,{children:"Element"})," sparingly in production code - you lose all of phpseclib's validation - but it's the right tool for fuzzing, interop testing, and one-off custom-structure work where registration would be overkill."]}),"\n",(0,r.jsx)(s.h2,{id:"array-like-interface",children:"Array-Like Interface"}),"\n",(0,r.jsxs)(s.p,{children:["Despite being objects, ",(0,r.jsx)(s.code,{children:"Constructed"})," instances expose their dynamic elements through array syntax - e.g., ",(0,r.jsx)(s.code,{children:"$x509['tbsCertificate']['extensions']"}),". This is because ",(0,r.jsx)(s.code,{children:"Constructed"})," implements the ",(0,r.jsx)(s.a,{href:"https://www.php.net/manual/en/class.arrayaccess.php",children:(0,r.jsx)(s.code,{children:"ArrayAccess"})})," interface."]}),"\n",(0,r.jsxs)(s.p,{children:["It also implements ",(0,r.jsx)(s.a,{href:"https://www.php.net/manual/en/class.countable.php",children:(0,r.jsx)(s.code,{children:"Countable"})})," and ",(0,r.jsx)(s.a,{href:"https://www.php.net/manual/en/class.iterator.php",children:(0,r.jsx)(s.code,{children:"Iterator"})}),", so ",(0,r.jsx)(s.code,{children:"count($x509['tbsCertificate']['extensions']"}),") works as expected and foreach iterates over child elements directly."]}),"\n",(0,r.jsxs)(s.p,{children:["What Constructed does ",(0,r.jsx)(s.em,{children:"not"})," implement is ",(0,r.jsx)(s.a,{href:"https://www.php.net/manual/en/class.arrayobject.php",children:(0,r.jsx)(s.code,{children:"ArrayObject"})}),". PHP can do many things with arrays that don't translate sensibly to a ",(0,r.jsx)(s.code,{children:"Constructed"})," - it's unclear, for example, what it would mean to ",(0,r.jsx)(s.a,{href:"https://www.php.net/sort",children:(0,r.jsx)(s.code,{children:"sort()"})})," one - so those operations are deliberately left out."]}),"\n",(0,r.jsxs)(s.p,{children:["In the other direction, there are array operations that would make sense for ",(0,r.jsx)(s.code,{children:"Constructed"})," but for which PHP exposes no overridable hook. The most notable is ",(0,r.jsx)(s.a,{href:"https://www.php.net/array-keys",children:(0,r.jsx)(s.code,{children:"array_keys()"})}),"; the closest equivalent is the ",(0,r.jsx)(s.code,{children:"->keys()"})," method on the object itself."]}),"\n",(0,r.jsx)(s.h2,{id:"wrapping-constructed",children:"Wrapping Constructed"}),"\n",(0,r.jsxs)(s.p,{children:["As noted earlier, ",(0,r.jsx)(s.code,{children:"Constructed"})," objects are deliberately bare-bones. If you want to build a class that adds higher-level conveniences on top of one - the way ",(0,r.jsx)(s.code,{children:"X509"})," does - there's a walkthrough at ",(0,r.jsx)(s.a,{href:"/docs/file/constructed-example",children:"Constructed: A Case Study"}),"."]})]})}function A(e={}){let{wrapper:s}={...(0,t.R)(),...e.components};return s?(0,r.jsx)(s,{...e,children:(0,r.jsx)(S,{...e})}):S(e)}},8453(e,s,n){n.d(s,{R:()=>c,x:()=>l});var i=n(6540);let r={},t=i.createContext(r);function c(e){let s=i.useContext(t);return i.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:c(e.components),i.createElement(t.Provider,{value:s},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.