From 6cd79b8043f1c04ec49106c52ff1b49bd4fb5e63 Mon Sep 17 00:00:00 2001 From: Dmitry Radchuk Date: Thu, 3 Oct 2024 18:25:50 +0000 Subject: [PATCH 1/4] Add API for getting PdfLayers from page DEVSIX-8576 Autoported commit. Original commit hash: [532081a7a] --- .../itext/kernel/pdf/layer/PdfLayerTest.cs | 122 ++++++++++++++++++ .../PdfLayerTest/cmp_output_layered_2.pdf | Bin 0 -> 3110 bytes .../PdfLayerTest/input_complex_layers.pdf | Bin 0 -> 3054 bytes .../input_layers_in_resources_xobject.pdf | Bin 0 -> 51032 bytes .../itext/kernel/pdf/OcgPropertiesCopier.cs | 70 ++++++---- .../itext.kernel/itext/kernel/pdf/PdfPage.cs | 19 +++ port-hash | 2 +- 7 files changed, 190 insertions(+), 23 deletions(-) create mode 100644 itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/cmp_output_layered_2.pdf create mode 100644 itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_complex_layers.pdf create mode 100644 itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_layers_in_resources_xobject.pdf diff --git a/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs b/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs index 5a1073eb2d..3a519215a9 100644 --- a/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs +++ b/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs @@ -27,8 +27,11 @@ You should have received a copy of the GNU Affero General Public License using iText.IO.Source; using iText.Kernel.Exceptions; using iText.Kernel.Font; +using iText.Kernel.Geom; using iText.Kernel.Pdf; +using iText.Kernel.Pdf.Annot; using iText.Kernel.Pdf.Canvas; +using iText.Kernel.Pdf.Xobject; using iText.Kernel.Utils; using iText.Test; @@ -357,6 +360,125 @@ public virtual void TestInStamperMode2() { sourceFolder + "cmp_output_layered.pdf", destinationFolder, "diff")); } + [NUnit.Framework.Test] + public virtual void TestReadAllLayersFromPage1() { + PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFolder + "input_layered.pdf"), CompareTool.CreateTestPdfWriter + (destinationFolder + "output_layered_2.pdf")); + PdfCanvas canvas = new PdfCanvas(pdfDoc, 1); + //create layer on page + PdfLayer newLayer = new PdfLayer("appended", pdfDoc); + canvas.SetFontAndSize(PdfFontFactory.CreateFont(StandardFonts.HELVETICA), 18); + PdfLayerTestUtils.AddTextInsideLayer(newLayer, canvas, "APPENDED CONTENT", 200, 600); + IList layersFromCatalog = pdfDoc.GetCatalog().GetOCProperties(true).GetLayers(); + NUnit.Framework.Assert.AreEqual(13, layersFromCatalog.Count); + PdfPage page = pdfDoc.GetPage(1); + ICollection layersFromPage = page.GetPdfLayers(); + NUnit.Framework.Assert.AreEqual(11, layersFromPage.Count); + pdfDoc.Close(); + NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(destinationFolder + "output_layered_2.pdf" + , sourceFolder + "cmp_output_layered_2.pdf", destinationFolder, "diff")); + } + + [NUnit.Framework.Test] + public virtual void TestReadAllLayersFromPage2() { + PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFolder + "input_layers_in_resources_xobject.pdf") + ); + IList layersFromCatalog = pdfDoc.GetCatalog().GetOCProperties(true).GetLayers(); + NUnit.Framework.Assert.AreEqual(16, layersFromCatalog.Count); + PdfPage page = pdfDoc.GetPage(2); + ICollection layersFromPage = page.GetPdfLayers(); + //There is 8 ocgs nested under the resources xobject on 2nd page + NUnit.Framework.Assert.AreEqual(8, layersFromPage.Count); + pdfDoc.Close(); + } + + [NUnit.Framework.Test] + public virtual void TestReadAllLayersFromDocumentWithComplexOCG() { + PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFolder + "input_complex_layers.pdf"), CompareTool + .CreateTestPdfWriter(destinationFolder + "output_complex_layers.pdf")); + IList layersFromCatalog = pdfDoc.GetCatalog().GetOCProperties(true).GetLayers(); + NUnit.Framework.Assert.AreEqual(12, layersFromCatalog.Count); + PdfPage page = pdfDoc.GetPage(1); + ICollection layersFromPage = page.GetPdfLayers(); + NUnit.Framework.Assert.AreEqual(10, layersFromPage.Count); + pdfDoc.Close(); + } + + //Read OCGs from different locations (annotations, content streams, xObjects) test block + [NUnit.Framework.Test] + public virtual void TestReadOcgFromStreamProperties() { + using (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + using (PdfDocument document = new PdfDocument(new PdfWriter(outputStream))) { + PdfPage page = document.AddNewPage(); + PdfResources pdfResource = page.GetResources(); + pdfResource.AddProperties(new PdfLayer("name", document).GetPdfObject()); + pdfResource.MakeIndirect(document); + ICollection layersFromPage = page.GetPdfLayers(); + NUnit.Framework.Assert.AreEqual(1, layersFromPage.Count); + } + } + } + + [NUnit.Framework.Test] + public virtual void TestReadOcgFromAnnotation() { + using (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + using (PdfDocument fromDocument = new PdfDocument(new PdfWriter(outputStream))) { + PdfPage page = fromDocument.AddNewPage(); + PdfAnnotation annotation = new PdfTextAnnotation(new Rectangle(50, 10)); + annotation.SetLayer(new PdfLayer("name", fromDocument)); + page.AddAnnotation(annotation); + ICollection layersFromPage = page.GetPdfLayers(); + NUnit.Framework.Assert.AreEqual(1, layersFromPage.Count); + } + } + } + + [NUnit.Framework.Test] + public virtual void TestReadOcgFromFlushedAnnotation() { + using (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + using (PdfDocument fromDocument = new PdfDocument(new PdfWriter(outputStream))) { + PdfPage page = fromDocument.AddNewPage(); + PdfAnnotation annotation = new PdfTextAnnotation(new Rectangle(50, 10)); + annotation.SetLayer(new PdfLayer("name", fromDocument)); + page.AddAnnotation(annotation); + annotation.Flush(); + ICollection layersFromPage = page.GetPdfLayers(); + NUnit.Framework.Assert.AreEqual(1, layersFromPage.Count); + } + } + } + + [NUnit.Framework.Test] + public virtual void TestReadOcgFromApAnnotation() { + using (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + using (PdfDocument fromDocument = new PdfDocument(new PdfWriter(outputStream))) { + PdfPage page = fromDocument.AddNewPage(); + PdfAnnotation annotation = new PdfTextAnnotation(new Rectangle(50, 10)); + PdfFormXObject formXObject = new PdfFormXObject(new Rectangle(50, 10)); + formXObject.SetLayer(new PdfLayer("someName1", fromDocument)); + formXObject.MakeIndirect(fromDocument); + PdfDictionary nDict = new PdfDictionary(); + nDict.Put(PdfName.ON, formXObject.GetPdfObject()); + annotation.SetAppearance(PdfName.N, nDict); + formXObject = new PdfFormXObject(new Rectangle(50, 10)); + formXObject.SetLayer(new PdfLayer("someName2", fromDocument)); + PdfResources formResources = formXObject.GetResources(); + formResources.AddProperties(new PdfLayer("someName3", fromDocument).GetPdfObject()); + formXObject.MakeIndirect(fromDocument); + PdfDictionary rDict = new PdfDictionary(); + rDict.Put(PdfName.OFF, formXObject.GetPdfObject()); + annotation.SetAppearance(PdfName.R, rDict); + formXObject = new PdfFormXObject(new Rectangle(50, 10)); + formXObject.SetLayer(new PdfLayer("someName4", fromDocument)); + formXObject.MakeIndirect(fromDocument); + annotation.SetAppearance(PdfName.D, formXObject.GetPdfObject()); + page.AddAnnotation(annotation); + ICollection layersFromPage = page.GetPdfLayers(); + NUnit.Framework.Assert.AreEqual(4, layersFromPage.Count); + } + } + } + //TODO DEVSIX-8490 remove this test when implemented [NUnit.Framework.Test] public virtual void AddSecondParentlayerTest() { diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/cmp_output_layered_2.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/cmp_output_layered_2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..92af7c8e89c3efbddab153a8df0108577a5707f2 GIT binary patch literal 3110 zcmcImYitx%6t1GCED=z77?I?LSlb5Mx%bY@&P*+o&qhXz#Q1JmM*sF6ZMg5d`y)F=_}y*u08?JnvcCu#T2 zx#xY(cg|FWTZ46|Uh!3ppE@??lK}))e7&!+QD~)qP2ExYzeRUR%CbkI!Wzr!j|pvq zB<%{TOfn>dTH2i`(x_A<76^bM=CEz1-5fW6@z_<5JtlNoi4DYvDx4s6>HVZ8)Y4+5 zlV*y&z*->`3`UWk-2>#Yg=~Y^QHj%{*{kdgh}N{*)#9BWvu|jlK`@*%b5%Wd?anK z{9{Bt^9m6(`&?oR!9H4sR+6v`B6N~;%3TK#3XLf9#XdXOzX!$448ZXU#XX68_?|ZZI-sOvGo;iDT+pP=ahOc&87uVk$IxX(G zZBP34&pI}3n|b5rt}PedzG`0Z!^mH~PggI$^xmksw{qvoyB3}}w4}Ci=EZ^S-?l$- ze)sIhKfUbCf9~9w6FbMgKD^Q|9&L-$DQjsXMA+z zp4FGHxvu}k>7^ON@V}%?kqa_5_2>`cZ7S z6nsvUH%J6<-XkG2jrOClPQw(v##tfgWXm zI0)&{??Hkd6%Ty$sCodWN6mvrfIdRcNQTfDQY4QKrYM&X+WoF+rCTXDYFZmGL^8xs zMo3a*v>1xj5LVX;D=cG@0ZRx%5n@rn$|M7g))+_-yTR~;s>+Wg_ zcP|a~fVDME?cq)^NCyiwxV9GT!4Sea46y{7GWLi=03R5jYgKLD*8(7iTJlK+JkLBX z$14pFl+Iec&r0Rxc|p8jn{|hDkO^1XW??y}4= zttgvBCR(7lAc@uhZ4e}Mn1&P0PUn2uoxBKZ0nZgatGkHf=7()ogl>eq0PBb=2>?`E zMuK(D6(w*v3EsQ^M}qk7DD;I_w;{V*%6rdS{E8yw-7i?VJgIy`@dfqIrl#h$RIa?e zsTnSn6U(N#UlK-PpJ2I+4aPuC^y|ZbaR~ipn)4UyVh zr|6SZ3e;j6QcCqvyE(pmY$_|m6j@a%wM*xx$CsDY*ixDztCi*oLop{inLiBG61y0w zw8x9}K}9R|MS>;uz*xz}HfaO0LCw^md@Z^5DKsQW1#!5xZuSwIeGxBf1<}T292zK@ zgjf}c>`y4VN|20Y5i5zLs9463 z-bh9=5m(}pX2ew(ha@hkx}hQ+iJ~SESxZ2oV}BwMS9MJMNKL4Uq9$b}PDEl5Nfe=$ zRFi(gkVzbCFrg#^F=1IcX@TszjaU??Yzak#ZgV4{txdbVim$mkw&m)Ix17&$b=&1I Oz#`IoRaI@Fpzm+r$c;Jx literal 0 HcmV?d00001 diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_complex_layers.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_complex_layers.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4dbe49cf999905e0896da696a606ee9ac36a9074 GIT binary patch literal 3054 zcmcImYitx%6t1KQY$PC3NidO%Nwo#b%$?bp*@b3FX}eG@?7Ca3bZO#r=WZF?-Dzf~ z&>|`?!JrT$_(FoV#sCIIQ6dDxOQN8J@)9%{!b>5b{*h>6O!VA4)9$vjh<}`9XYbrO z-+A5hoinA;ns5c4r1(lNUG6#M!#v`V(K6pxS;^JFuPUB^d$nqlR>SN}a7~7hY36Dd zkgUx$>SQ4osh(~n1OZU7=1>Ur6ONjC*6x*N53V?N#hbZ$Bl#5364+6|%~CU@EK*%< zWK()8ec;QvNI0CpL5e^E^)A-TMNEyD31=^XnKtCAK#8*HrqSL;G?Z35iD{K1EHN;a z85jg&s-_#Lm5RaJfb+JWg=Q``jj6E`iNXS*kQ0jo>*qs%g9QyB!Ew4TaXymOXxgbW zZu-M|+9oC!PDAo*NYc;-E?))uy3m? zsPxli`YxVaC^qo^*wDJmf*j{R^M5PKjYI1$3Q<1FHRoCP*pYV-`f86ou>FPFX*RrK!i^>;|S>tt4`7bUC z!TfmFoIDU)&W|2^{F%L%G8<3SjvqFB>3bh+X`Z+Bry=u(ja{|Uwj%r^w z^WbiU+xGIFjmz(Qy*M?-TCwRwJ^JI7L0w2V#J9aYMJ;-uc5_qX&ie6@-*;>~cIckf z#Z6t;zFd5E!p(R7nm_8L@%!!3gHN{ZJ9mC+=GepX&U5h_zwGH=GpKm|%0C}FSUuX$-N~vHk9mXDp?fRbMFOtNqeSg$=X3nOP>7V&hup>pS|O|dShDA zThG6_s=I5Be6FjqYyI6{jW}9tA$`g(Wq>;p|_jM)6&)>U=KFMqU7eRagH z)ZyvgB>Qto24dMjT`S=fy+aIo2tcZtMAOx2Mn{5@ObSXU;1`;?sA>WLF@AC}Vj1mb zl2}0VFsPz`7%^UPVF6*0Y650NO{0yNw$22jCPs`Uunq0SDS23fJR<=tMVGy>^tb4Gw;{kX=rSq<7uR6w8BZV$ooxi7X(gdhFWIPhmmrbqoX@~7 z8V3`$+{hUk+Mt>?T5}3$ysm<3prfrD*&1MLSb1WqJeYus=k~Bs@u~h|j8u#%Dwt&{hEP6;wH50*Ey! zG^-KKEvuRyt*=-B9S^*iTRsJ43{6kz&3?6OU(OL$&9_1zTwVy07q7<%5(+}{ zx;lff3YhE+yIWYvOOY2+*h=0UVh}bm*~I*JQvHUw}Kvnv6!S$9Udrdn??oe5`$~QnifDk`Pk>I0AkP6tK z0$?Jeb|5OjW%MkQ@2|JRBq<*>L(vC>W*7D&S&~EvrI5Ra1qDuyUMSnQCo6C?1NKBe zbh7??d=MJ=z&*d>*~5weM>YT}iyj-i81%?Nhl9~E01FLm;2sXh-Z+9@ES3Tu8$9pI z;k>*AWsiT*5({7&EVN{2FE6J87Cin5H~`%2ADbX}^CF0FVtsP9sp@HB(vRo5 z8kDFEBt^yF(vlQ-RZ|5~B9e@gIG|~Y+M=k04^@hSq>8*2aO%PAT2 zsu*hK>ar;VvB}EC3@C4BXvXzL1j+|0wUqVk3OXtB8oST3Rnul=CitPuN=s`aVc$Pt CQj?Pa literal 0 HcmV?d00001 diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_layers_in_resources_xobject.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_layers_in_resources_xobject.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ab05935ce7fc89fb3b155c89b23c24bf553d5cef GIT binary patch literal 51032 zcmeFa1z6SF(mzg@NJxsrrW6) zi8YRp5Vwq#tt;G_TgKMd6)pugbufc-E5Yq8TrB}G5fK+xXSlH)j^_tdB*Y6D2X1fn zy$jL0V8Hv2FyiC?iIZJt+0_6bE?WJR<_Q{8yrjuNL`D=c0bI=ZJP0?SxRy)iX)C#1LBt z3lWhE#a-;*1>JW;5O;I6bZ};2<(5S3xvQ0fy%fSCSfm8NKrj>t13?h)yg(>BkcSxp zWd2TtpchB*fdk}iZQT%-VeIPQ3;^?RK>!9UH?0s;c(@p_vT`fJy*wP8&0G-7l^x9f zK3~K#%-l@j&Ma11a8K7_fTV*n90212asd%M#bwo$0Pb*S7le2QtbhVA5D4T%2nGR& zJ34#0zyY$(4sMPB6&+S?O*a#3xTz~*N-Zl_TR4lFoT`?px`w22zUz98DFSZ0MWka=B5K&lDOIk(|yysuCm%U4e@Y+xPYA2ra&Ns zP6P)45OOv%7toNB`OXRPlQ5m7tE-~`H@Am}2bTwg%fZ=#8^q7g&kY1~gTb5#3QiX< zdskymPJ0*nOCI03NWoo9ovj=%OagGh)z}1~Fkw16#4O)l--Wev{A~(*7p_aexlA4G zxIK*>xe>x~|JMO#roYiUx;fik3S?%=4Y!5c!R=jL5Mw}p8iUY}vhr`k{&E^SyFZO` z{Au-n7=!o)Vj9SA0}$*55Eg1>?sZ|L!gLq5&k2Naf*@L8pa7Ux;L-sEAiy7MxfpQi zFYte2zz=izZ3yUM4lqy)#3KNN2tYyXKz;$>pXYFJ)^c#L{jv3ba=9NK$NED!GgAR` z2WLBD#AX>gI@(&9Ub;0mV$nY+F4ZX@laCVLCTAD>DI@IS)S&1O;=#%)$Je zP$-m-)5O>u&I#p%n8Uy(aAOmo*&kWR+q<|L+nd6FW5I_QY6^tHIeGa^O*x^a#-^Oc zV16*C3D6v9#sh`%f%$oVus{eT@Wbte=`Q`g@ehvw2ft?pH2!VB|C!~FQwL$H7ano( z5<;lN_2M}A(ed5H#cf@G@A=+mYjxo}0=CBX7Q%F%oMv!yV>eq@I+1_yAQ9re@D*FD z-}!#)`DrR4?*A&zg?wheyW3A=FPQx_^l~xZCiqJ!xqljZDacP_FQ@&}EZ?O2$=5G9 zeCtFUBZ$L^`?u5Scixvu{Wtyh+wt;m;(y8Z*ChXxc7Lhsm%9Eb4g6EYzpU$*y8bB* z{8Plgtm|*83+MOy3gTWUOy_ZN8~M{+9|ZmBMl9~^D&gR%4}x-m_#i+C9{|L|1qDN( z5D)+ahVgUp@k03cp#T^R%mo8Od0;$-+!7Z9fQSnx2r+<*AHoac2f+AwxcHzjAe0aB zds{f%?1F}eihZ0MEsxS_9Du0r0`TH=zJN{_jm50PI5I z3j$sM><29$0QT)tEn^1*z%K9FB8b;-7eg7ux9)E@!|&7nb}{t)a6ds1uMvnVc92F# zR)2V-RnWv@4k@B6%lSMIcn_>gazBNoY)W&=p!B(b>rJzQMvVUZkNh3(95{2vOOUjx z2?Hfvm@A%}W||Di3c5qGRx4jf(_*ZXzqYiC(&ilvp`B$crEYpB4ceR`I#U2F{z z^Ga>j^kid7-Cvo(h4y{~4gO#TJkWo=8Sp^<&&+`T@0bDXcRK(9f9x~D+JF~9q>DXA ztOs~u^cQU&MEeIlFQWa!S`elW{9!IIMCj#`&p&PsiGsx_AY3G$%nNfMYITZVDS#n8 zL3_D%hMpZ@h`k=!W@yqam5km<)s$P1qDNJ8^BN~xSbyO|d)4z|$1p+1Y>+`CZcDc( zN337mNI^>HRnjpU2Ln8OSnRE=>+^4lhOc3UpUhicV`*SZ<2Q3#;67{5&dpyS7Is-# z%UYEZB(&W;d43#Q%4~HxB6LJUoZ1n4)e+bnY`5z><_PBSP3{ud~%p0x)qo* z`)7mrvpGO`xnMj%5I+O}fpH-;%L@bnAP^`Q6mc9NoCwOp$Hfcgmsh5enl2a`8YwKWmepAMqO=etupAF+Ud&0_ErXS(^yO@c=IW zgCI8J;$(;NU63J;>A%q4x2W1TBUdtZalH(YSi#M{MF%fJX)wetgFrlpzy&k#B0Bgz zT*iZNr9Ws~(!tijSrZW(gCkC2NlnCO!8f;))V%oI__hJu(w?reny!cd41z}Xg63N^ zQ}%-9TeMSF6O1@%E*(==6XCGmd{kBw;jiCZR8|w=u9qH)IF@A)F}{oGfOinQE?0r* zLvF$%7APASbUJio+Rv@_&hWq*fMrxdLXVu*JcYgk0g|%o<5{(^ zGu}N-)BjkV$1UlUA{kOzG`lyXHGI@_DzWyl?j6n4>EgK*IX8G@fmbZYWJ&&^Rjhy*BO{wr9_tA zUk#w1nVrTIK0!Y_=a#H7UBmY1ao|Yn5fKm_f}5igM~^j z#pwfD%kjqW{EyqKpLaGhX-d*4M+_p4z*|$O!PEW$r{VZp=U<-4Hu;`6Er*Mqoiv@_ zy?Z|26o2Qg=gG-#3XMp1`N_tURbFp`ZDid-Vd}Ve8tx7$Pro;2?lX|}mnSDkh4%(` zSCo@l7VS<|D(L)Q-MTL$S`~PH%Cc3ImvWNr%oM1$vQyAkUV0FOS& z8qXDBoA3pwm;>KZ=e76TBL7H%4`~IBHG2j;oz1+CPdFA6{`SSRmCiy5Z{fX!huBFp zt`r|BANR8JTN4q#l6v~dnVO{g2{e-BoqShnGi6peP=thms{0}Cc3(Hf)>Ians4BbK z7olBme9SNTDAq{BfgyLF?#8V`cE^+DA~WQMUDC>xuUIx2)ho6Tn4T76x%lH5H0Qq2 z#m@8ekn42GD3_1^Lbx#0TETn^XdO5r8vDG&{WhN0s+^7EE*d$Wf)Ac0V^5(zANz!z zvG8rlS_PSi+viG{r(fwYuVvm1q6*6tIVG7=Iggz1J2^9vRrQ%?*9R1K!@ROS&Cf5= zJZpdBe{z`8=|RKode>#Ty=1fIEeq!nBbnheo}Hyn%~r?8;az7zh^p-CdB$GfnjvEg zf5z8?!tPAeCasDY>+W}7Vl=-F?{V8+#jAD1z4I{RG^jS^?Un$V&snu8+Q(gb%bQs4 z9QNFn@q{mXBeQONLGz)gSaNddl;=utD2O*6x+5N$N8NgGHTjmZt#eMO*y3YtTMo__ zD2hIX@ z+5Xt5l2PJu7vk|%b7P}LcFa{5;*a^kSJ0b3+yav}LDW{b>SrG2Mb20j$d~VP7|(Ms zk9*DME@26Zu0}A(dV2VV_*0Om$6+z&W2xrQwGpnOk*r#2dGecxh_{?T*(hm@_dq0X zm-gw4D2?L^W_VuH7%yOX1mPS&*`!coMa4t?*|@8T8;r zmZcCyx+htwnZ|gsAKaQ`Ws^|6FtxYHZ|(aCP^fv7sY>>g(;sEajcpw|g)ADnv4CBY zQ_@f7Rd{{mb5vyQXTQ{E*IL36|)Jka%T zuD&0(aSsC&+QNqNIuTD5_#7y3Bk^rNeZ$O+&*({ZSgafQPCmMnmh)(6bhG9|rFA1Y z@(>4>)FusxKt%d-f0K7D-n9AEFnW$X+v``T=n|-B<)U>D3F{uuw6qv0(z!jB!r3l!BHigi>&vPn8g0$`m?ev8+kkK3iAlC69soIHB?xDf z_eA#=Csq2Wfp-{!8>xu`t@p(Bq^<46i6%w~1eN6mAI_j|-+G?~s7FnQ3IR)xy8C}d)&4+pvT66$mq zcSU0((Y3&Y(#WAUX`rwcDjLLyM+B)qlw?9q%^lyHB;DRFG(VK3og*k7oI0FijoHrE zS$(aaOd3fPuqNJv^hNq!xVAj8O*TcwK`{Oysc$67Dd`sqg6)JgB$WD?kfMWmA$9!5 zSQUM5BM(BP+s$TJXkW9@18o_{l`C~^-<7{BT1twg(idS2Q|o$SBFHL3M3zj#m>lJ- zLf5k7Zp=Oot9ZIpIHRlsvwSHX5u7J-MOO-`SsMB%?5kP8kP^PchbCk+z67TR!RsGq z#ilfUS$)9!*5l#kWF2^Fli4O)EX5jSTg0+a7zFnpzT>DdaH>x~FuU1(SAUXeqf#o6 z5MU7~L$tw4@JZNP1$9L1H9jvvyR68MLHN>@Qk}De)b^9-SZyqg2w?-s2>gaS1M4eQ-Nj@3lZ>l(xX)ry=+3!FTUH zMvRQv^dJKdQ%JC0IPnpEuKVD8^*w$|zV0z6)?|hg*~IM^8(=B*^u*0B57SYf=xKrA z&>YKjx#inB&HCGE!=3YqmTS?Pvc}2#RpVFA#9RZYcAuq?F;5FjY8wo^vD%wq^4Sjc z*-95h$525DmLBR0B{qCn7Sy0S8gf_nDgBlElENV$pk*_pI2bku#)5mviZ4N9U^*+h z*CNmLn|%$%##*8}<;tTWH3Y8c!Ij1G=rhf^%S=1rm_)@aRSl$gMyX6tPW^&_HZ!7! z+OP%b->DVOh0%wx1cLyWU*dZb5_BLCGmjn72!p{Vl=w}|5rOr zpXInl9E&tiQZ%L#y;WZz4VNbnM=AJfMaif7#te430;dD!7(xh4EkYDBjTzD+c}f(N zn`GZV$leGdG>+H~)@4jH;d2ME&R}-es6=r9dPV~^ z47%`S7P7KhEZ&JDJP*jX3#nRztf%DPq=}3$iMs6^C`*!_ZnGresLqr;Y&by4i~cr~ z&0Ru+p_0~GHE+Ch!g{Pf?id@-xas|aDODrhJkQ3``E42I@fU{7rizvBgbyNONr}2g z1ec%Nj@(~Cg}*fU>{6}R6YyGt$8k>T7D|TSggqJ>l~YinpMd8oVTQV4Mp|v=w!~~m zawe@e3X~1wMZ85`r-%oFQ$=woHk)Pyy#;egR7x)+UL3OZY49BC2erj9+EEpTPvZxB zln1o+56|QtE;ElkMDfE$bDH5=b)?&*zZx`n186le)Ggplw`(?KrQ0l2bay|yz90C zSKm3iN8rzK;UwYG5$xIu0Gzp4NTWAK!D{V`wvj>31*uo*C*-vU#S4;MD7B(iu3>Xt zHRoznT8wccYYK;P^V$=uKj9pCGxYED{gml*Qy7^*!2ry2ULsaux*~2-{fc2e%G-cOgBYp zgWid^lz}en?>+TGcf#d1=Yq+&(K>^|hW2i5W$N&4g(~gNr(^mX_2PCnheSArGhP;H z);)FRa(zAg=^CKmS>?nHjN#N+x6Bt6{MLDG6JJpZHf|1b51+45?nj;K*NSmCnm$f3 z_c0zOEC-`cCIY}!wL7xj z_H1|0k38#vdXSzJ;q!RV$csu|5%ro_fIZ<7frefXkJ&pc!^{2psilKv7XILVhfmc&4r*QIY@`EH?D zo+{-q-3zVGmzrO06|8|rwDF6T-G+P2#HsmbailVUtmxt3;3UOs;SlCgb^?CS)lQUX zM+C;&$zIyPQ~&~6JBFP zolbN1qNHDa!*X2KTcOHO3PCy22DCtG+$0{LxJ+;G0!a;;_$N?`&>V@1J}tS(Y{`&X zSfpiuh-3^a_9Chv|K$6b95pCO>s+czsPCsJp{`QW2=`M;LTMJ;o&%5j~Q~IT01Gy7Jyj8 z?XSObwo^GU((xePD})Oe5Qj2^x7G!Bq=x)nx>-pS&AGoV#FzKMg$`R+6ArZ*l`}lWL-(p8DXb* zJvS{b6AD0shV~2uqoHl5+?3cgA__c7?HW{!(VzXy^*HQ4vMrTaSEvi`OtTbx!#oKV z90{^YPCgK{3nkA&=TM*$i!6GfV7t22-TY)Rl^_6w)$hRe<+MkS7VVuAzF48#@Pmnf zCX`~mV`YNITBo|fKz1pk$F}op!e$(fTKk_yn7`^dou(oM2DUYZc^fm9qn~zAQSozecpmq*FweSah+~xZBf+kg9TR zG=@g9pOZ#$ zt@=jzi>_hgLf73QZ7V88Yufa=B>9Eg&h0#Ldaa`x(fX}Lb0^))MIB<|?NIpLEQSYO zjaj)FvZ&}cA>B72R`*+#6~2f+Q&gy;?ajOw)ZG83UFLL_1Xs`Qtju`(+DS@%$tgb* zfkAhujhXyeco&JEn1KF+hIk>GkaSz`#?BQ*@O8=a0_3myj}GNUKiw-Ke)fWW)pfC# z&@@DF+{Sc|vW#<7a_FTpNDg=0i2%#is6%wJ{XIShQMUuiV01T~cdyOLjY-O?&qca)4Er99pL^l_WAt9vB9 z82s@=PX-d1v?){zuPq!- zah&OEbH7(U$>A)VFj!>j5x{s?d%$D=c%5M?knLUI`3r6A^XK(zBF4coTcc#t+D|{V zZw5Uo?6mz1e)i=>NyQF@pGUTgL%qKyiJqZ_WU#ZCw??4JOwTIS^%n5B`vCO8=9U%e z^$om!k1PWRr7E)NP*mm{uW@R$rdL|Qty``6q+ywm7oj^97`zq7DyI`SBsUdKRlMpH zcH;1J&vn0?BXd4yd-Uh1HRval>R*JCzXygvJU{(O%;$LK^K}dW5)$bYs^6bEf-hpe z-vi-)n~AhmkGOODC%VT<0*6VA^gef(#U&xSt5Fsuhal!hWRG7I+RSY%vp zMtGBK2?dCHvDJPy<17CvG*?}y!01{a(^QddQt#NWzqnrTIA@2JvdQsz&Yfvbe$Mgo z0pjF)_wQZbD`vELH=Op^oKFcz5q!c-9(-o)IgbZa>TCnPbsERjN$xi51~FoiTZ)gb z%7YohGkRIUmVi4*|L+WaBilN5m!cha3XUIB4gwv5nfLB$xZRAiPQ&UEy9rsUwTnC#{RFEiS9M*Vif9YIL&_ltyMv->rX`4ug-7Zb3rbtLPa${CxO>FF^ZOkt*cnycq($& zC=~UC+Ab(c`bvUGw{_^Jojl%23zH)ygr%$_bc zIO^g?uNyn|7Vfs0Qpzx9<9DFDxy2Dr_SEW3t*OaJq25bgxe+Z=zYL z6y9EaCDbgTyx9LvcHe(dy@7<0?1f%Iivow%^DB>MwdolKJ6ws8RjugjaPO3p@Pv1& zuGjbN6~_nUx@z33<1(K*Q;0T-Hm-;tv6vodm;m@FGmBDA22$Jb5tG;v;7L+kIo4x+ z9{a>bxX2Q&!k-oQjEc?s^GwAu|Dkw!@tgbI751zzv2@>b&}eS^adI7A53eUD-wg;7 z#POBLd}6+nmfK=3j5ZIM5}d`3VIw^oJ}Ip^_@JakmkMK)5)JT>V%Ow;-?9D_i~276 zTxo&-wTX7|WN7MX&viv^59NV7Zi1ta#*imATRg1oCEmiKM)yJ(n7hdHqzCNK930PI zU4g!f><-M^BT?v1&v22Qpm_NXB$~~P2a?H(zU>0pKP>WT_f}RV+y5*c8JqsP)_$d! zBqMK>`^%`xBbxcV(aCYP)uT;QP(>d1En-uWMw@Hrx4cK6ex4lWI=$g!J;Op`Nrju# zWYk|zGwr`}h<)5J1l8B+SX+xp4sv-Lv`KZmH5Fi8$kNZK{G>enDEv@~>-kgXsraTB zr;B?@9@!zG?pyu*W-Z?k)qg81Sl>$EM-K%5lk#lQ~$C$nr4BDX#Z82pzRg5Sb>+>qEs!1d=CRCbccq^ zEqX!Xr}InZR#K3`RRYQCg>?AI(v@kl;dL>jqxKQ=+4mdtb@c?C&FD=WZ^6RmgRL%K z;`mQSP?=xf?rTUI#V`nZ)b@nESV@`lMJdi&*cxTxWWfGp;Js#AGIh53rm~OnS;BAY zVm2e2pst9mBsY0%>|*R4nO^z`cp1xMoim@eUu7;-vG12kmn1j=f4ARsJ&8xah9C^>omKvvzT_lQZj~yW?=*>Ge1E zsISXgiJR7D^y-LMP-9&Zjkr8m<;m3^fYt)=N&N<@Ajlytf<%pmPB{zP5M)=u5vqsQ>!E+Z?*`h zW~{O+sD>Ego-z%g;-uGdyxZLk(rK@_Kfv^X#ijVA)vUpugeXT#yp;{b6>K2U`#?c- zdi;;|5m!PRS*RQBP0^lmfzwpP$;mN@0K^gwjSLY4LlyZM#V^HLk-{D8X}2g>1DNc0 z&rn-l3G@Yeph}G{YIH>BG24DM3!RCWXWyqTh7}&a@?+f)Q!=A`K2Xt7xrlXs(@=#G zzp2-)0jOIX>Pv}?48vc}L2flce-MQ4Ew+&N)lg9M<4I^=;&966A-%yR5ueuF4v*&s zYFz#0k`A+GxblMO5#aP~P7=AMYoeum-FE7g38I*X+^+mvvFyLPuLu9~e=S*6Ug z8#;b*qV8d5-={2qmH13P3bQqGO-sq*Rcn!Y4Km8n6OwwhQk0Ioe`Xm@e04lcU>2W5 zg7wRihP!&0mCf8JSJy46VYBlFchGJ**~5>mE(u#{EhaUnn{3%~G3LXAPKS)oq~0>M z&f6=3n+b z$k_6orrM0YT0zOgFJoi7Jx?M>HLP}gq|_0W5O6|z(hxi+sb=zc+~B||)X^Pt*Y0L1 zV-~Zx?yC1DDq&}8LU)se>$9`&T@>s|@;5_%;zB)10==nSC7=4EV*_s+>d>g7=)2o? zbG_JB3BWlV2}ilPL0(wiE?e9{_DbuTv6i=K653*=G|k7VRF%q64QEza)eRMKRnX%l zHEfDRv`ySQ)V`Bh{IRr%5PlEWa<6c8*MtyuZFTpavqC%NM(J8dCO)(My)2CxjrEeo z4C68Alhrd$eH8x?p`_^)&tut|4Rr6OFmWJnRC$!0wUxl|vuGWS@`sM;gg0l*yCUY2 zx1*qABWomWs4)_vH2Zr}=L6^SJWIS9H!`0j8DDKE1}zG+&f1Lkl7*V;$X5pOhUk^7 z_3-go)H(=$^(u5^YxWcoUMU#3`CzL>jX7 z>J?3XrJnWk-dh3!J(=soZJ!8z1AAkRR+-)>llr~tz`CDVbZB$BoYOD-GRbdJy)cr4 z6=$79QBn8{4t9?s1H{vg;MgAHIITxv-` zi(2Dpkm#W6z>*fc7$3A}5}80O#-%;Op=%#}I=T>4)(UvtEbiVat};cZE|;R{1s(== zCTy=4)eN25XK=aS49c8*o)njjn(Y0R)oV@UUgQGxLasQ<4oajw$CUn?6d9GQ2bmOA zf;GT5aX4QGC+;Y<^IYY?qI4gL?;NLH^kp#4=wZrPA-xTswwK|t6y#3ZP9vm{d4%Ef z5fptlZ)5HamT4(dh)qC#IUd-c%GBhJFYGZZwn}T&IJ>`mwAq~B$eY!7Pg1Olxv_xv zB8^Gx{nbbbBOKhb>S~00lx%h}!YFYnRL;xl1L8D=vbC75-O?%%PA;0tOVk{)nMfWE zhk8!l?^>uCt8K2$W%}~mAHT#s?cOP;+R?BF7bz99HhttQ!_m%K%l(sc#07w|0+8|N z3&17j;rj*P5}gRSKqVnE&3?Rd{@2FefyaDL#QSl<;p0{|c6GKwKqL|TF0$7y{{7T8 z{23SXU$BF}T`W95K-924&$2DxETAA^$0GOt#TtIP#Qdw*!2hpUgIpx|w>4k^|JEvg zf|g2Hxw@#qoh2RY93AWtX-NPW_wTTwAAq56sjB)HN(J-qZ~^~vW8~mg7M89T$Qy2X zdshT2ZUkRbxTC9^vF#6t5){bKtzvA4$mZkLR5o_8K>(pHp)@~|{4Oy;e*klG-?TDA zq{kw-{KfdB%$iQpvNnc*FAM7XDo-^EhD3)n(65xf*Q{bX z2wqax4xW!cgTTXOPqWzJsfP=-Lr&J>H2nccpQ`~MF-}P+ni;8X2i3~*QIQqaG^&eQ z7Au!ci9XfqYc*;3+E`gPOcPh!Dz1EnYKu>a;n#}}&#IXm@9K5{5_J#V>5k1q?+{OL7Qk5Ect$*js>DCH-t;h$Fb z2ls=1hr0ebYyA7dc%eT$iys2}?kdpV6TsynV{`=LCV&L+`y=`r*anYCXi;T6@x88C zT?f=tH*=sI0ao*^OJ;-C8{eu-;ntNeGC;EAlOelHk{Y;y@+ZNKRKiL{UH1yi#EVLJ z&mlcHOV8;|vP;cB-<5bNva0+#yRui=HMPr&%Q%Xqq~^FX{xo_7!cxbus% z-6ty(>vB(GYU^ zATM&)xj~>=r1>nA@JW!ka7!FC*Z0HAVyux4SEBaix3e5A?Cm{f(zenS(Up(A%cGvr ze_m|H=_S0K*gf#PF~pFmnXAbAOixc8_)K@f+tbXe7QN)o4N6BXK%?PeZT?8v+=|8X zsF8_)wxAfSqbSmQH%(szyFL%EdwR+~jtKygrBHIQ3f8nI)yJ6&WhV5Vh)S!I2<&d^ zMUUwRdIctspwNcQq3kz4_@v3X6N$If)U|8C7W*>cU9U;M$y=fE{OM0?BLUD+K<)`d zR}){v>=~U}5Gg78N3~6lIhDHExYaKGwoGvFNl0~clC$F%~V-Vw296^4+<*f%9Q9+WAg|_JA>LddfSRJZ1 zx4ppK8=*Ya$XT;xTlR}wj-O-FuI`hnEn@_>`A8Tm@GCxQ@;XA<9|CwDw2=3`@uKjn zv_Kh+dg}9(&XX)>+gCK(KVJc+uEN$xxdq}=XCU4XPIL`MYA~BV_MB7WkzuT{&byan z`J6>*H-u7YH+c5Jdt(mMFR`|JhpWR`GJ81!l*I2*&F?znQ%6aN7C(9`YnhAjg{)!j zeg5Om{wyK0H>o2(_vz8qwfjfqisDMfiBWaZcipu0W3GE_kYTN6TS~y;G=O1HsgK-LqT2)UXzV8&k`tDVmz+n zp5K2_W8k*~wg-$*FCP%hH8?(E{z896pFS5~ zYUtmW5XUV<=P=49Ee#0OeM4GGa7DLXg2M>;%D9=3?YZ|e<=uymhI)*`Adj7DKHlnb z)~VQ}cMm|uq9C0Z2FR^gN>% z4=kQ6ZOg&Jb{-1XGB<7vGK5&`RGP}})l6w=)Jq6(7#4cN#scvktQ%ww=1En{$RI`~ zv$T%3U5yqRcw(5EiX)((V>r4_RjnsDN${LNn$3zYc}Fbp%7)C4)t*YHJjb^o8@o4r zclPCnm?4>3zP<-eBdvlo9=?0WEPMSkO#|WKy^J&*nO2!+?lm8`4qE3w`-+sTd%wHA zb#T0U|AiY0O0d{$9?|^fY0*-RF9zfhW@?) z;PZU@DsTb7=lzD_zX0I#enY%p0Pqp190=AIUxR)E;PYOt0@3+D9)QpHZBdu`V!t(k zfZw{ozc&%dV?PqY5XoadnmoS~@c!P*_j@nw_a^_3<^=$s@4Jrv=0QfvV0Yd&ZT>hT{;Pd|U*Wtg-@&5+^pZ6ky{NMgT3GwW&UjY1H0Q_G7{9ge4 zUjY1H0Q_G7{9ge4UjY1H0Q_G7{Qvy`{7WpwzY2iQ_YHaSp9bJx!ae>?YxvIr@Gl`J zKUVQ~0r=kmL;pSiKHoP)(k}r1|D6E*%kBL~Ebad#0RAPW>mN}HfbZM0ZvP?<^9z9g z3xNL%fd79p0H5!t+@-%b0KR|o`31oLPXh2St44f3xqkuh|CP_bzbwM>OSAuu$^t(n z^8B*df2GY{mWcUfv;Pj8y~wKgrP+UnW-qc$erfjKq1m6lul=t+XJMCFUw=^%SbxI|7ddFr9gkQ*&j)HKi>bd$$8&$;r^-IJYEn8^z%bNetOEqMYS44;TuGOv>&;7 z{FfPMh=j2J!NqaF-^9Hvj)SNJcOm*^aU4Vew{LAkaU96EaiBlOB8uaDD@%h|_aBSn@P8w}$j$q`3H-ec{=NCPisSq??hnOre(Qz(&Vm0& z^Z&Rwj<8kyWSiUc^384hgy+XdK}eNvLD%Z#DY4sRo5(FI*1J53Hki(iy>1$sC4U-G zcuXy|t@lxyv~Ws&>u$H#F1JG-pTB3}v+1+VjlC?UvuS^kw@0J+G&G_oqul4a%XzEw z@%RpR&sCJqKko+f+B@|q@g6d+0K3IGXCMxrZjXVQfii1HqlG#v^5V zAn4gNfJGCWC!}(Wj0OH=PD&J%k!1KbsJD8eh;-hG)Ypk=CS1Py20B$k__8WKRv*~jZKogIYjfx1_rg*vq+(#>wp0NHQI(nF%>o8iy%s5Y9|ntbM_9} z07n!lol&i>Tp}N6#%*5j+(tQ0R2cU}0}{3ewFbVD zQXwl>%JrEdyicq;^oYfxBOoiX!nCJFn5fJ0GOpK!o>+R`#oI!L;_YcMGsolgOrPBH%#PjZ&m6o~r1)T60F!rpfHc~9ZCID={H|}i_H(f(6UJegH zdJn$aScU>H{$wg6{mjUeoIhFsDqa1QD1-7Xc3Kmtje`qqZc1h(9PHg}cfW9+8AWm! z6eA=SPd_%;P=}USmM>$eG{FX=fklN-TaMV9DJN9HD(jg)Jd4wg`ABYwuhl|WN-k3` z5zr7&Iv+5GN^S--+s-EqaPpxHl4xyr9=vv#)ov#C27{<36VvdqO}-a zeN`;oH3?XITfIS+Xnt}SWz1{{a`ScSH5ou#=uMIvYHLP%W=g$}rEIbdpAl{ln-tMw zer4#Wh>Yy&eS$0VHuJ4?Uj2?O{#P^^3OtG!TdD44NS1^tvHo4&LA{=S6}oe`zcMsx5k zEoo}0zI8Os5m!FOAU^#7T28a!&oQ2g6Xp)LJW)z1TjjLLXA8m-M?SK6dFj0GPs~D> zu8~n32$sQ>HM4OnvZ4`S1n5876v(haccahi9q*f+7YosBp{4|CW6z71+h$V;v6((& z6JE20W8oC6$);0_cfZ_ki8(acFE7-wuQ65a+42g~VM5F36U=m9j~A%jYfTl_V4jeg zT6&L;L<8gXM@`x0-ahw#{FP)d0`|DSlW6x9(W?&wjeO04aKg5mZ@qVx)@9LCTl=LH zdyM&VmxaH+dK-dIXv=z7>2UV>sIkYmGgLRgYnPP#WS^=?^qtRf?}`(6`h25lFw_6& zv%l!u)790ycu^7m!^Qddc=AWkQN4(G6;8e8HQgNlkh@i-!3I8gi-Mjw^?>w-7Yg7i zya~w1m>o+#_QZphbCJ$A@2@O;78YU_$FBC#XQ+^3#hGu`*dBNALv zpmDP*gY9vAQc}*|MzgOVh*|3vYo-jtReKEyn^iEyVTJ&$e>$D@?wz>4ivOk5+9oxF3JGq=M8d_)(kDm-${*-;_Ww&Z?UdPcT3tUjwkk~sy zwcDFG_#`KEhRd!bDtbl|wx;@K@_37;UjON<&&fOq!vv+?^`B06o2FUelhbwQolPEF zG?wRhw-@=gi;vztkf3vfqYTY+t+XfGIkziE23_EazGOh9_VS-{P6i+ zFFF;*nWL}lKvkfxH{cG8`Hos9m6&M8+bs;7xD&T!V%N;fAa}`zz!u?v{j{Kc7mYr3 z(mwKvMJLRs*N|}TeNBt`dIhVDV)d}n6dWC?jQ8ZgQJvlW@dNX~XYaHbc@1^=1hg#d za&BnrX)3~VOuM!CsFQwVcDZydO7w}9E^MlC#f8JOXG33iyfMwR!Nq>M zv5j;Shhs9&nD(0G35o8Z>S7B49l7*kB+QGdY-l#Dyt*^(SwacKh*AE^SB*VlS?oIh zWf1%Wd+wDoKH;>S_xR}Q9}Ui$<`71#yQA!up`xYI+Up#;+;6%E;J$B6-8+MFa^UyS z{HFScD^2^sQh_=oFe87m2WcDzr86QX5>IdUDVi~v2;5R>_amf8L~iJ4C*t5Gm%-|+ z;&Q2aM6g3hpkS5VlOP;2b5ZlEfwBy1oJ!%1Bqp$l@Zh0;^V|FHxzIVOsGYLUkTxP? zAS1WvpS*U2h~pk2H}Jl>=iaY1pXQboN(6|AP(00uc72(}q4LTPv)1LdZ_ybB3QGyQ;SIqNhCIF?jSpP!;YPyvcv$2NIf(p z@gNIHM7=9Q$75`aS?~p(eu_mgZDbkLTwaaH#WofmSUfoF7EYje@F2ShMTYS8}VE*i%Wq3l>EE&JTp z$PYk?_marC8RXO$7Lu_uQttwlP9=UJ!fW$vKdVX-#lF zgax<1Ylg%cY69en8RPK88G$5=TcRXCl9MQVgv~NUY1NQxAp(Trc7>5=!sm_yGFX1;0YCwV;C!8H_@A2{jh>^@0%--E8e?k$WpH}JGK&^pvJefpG z*O!ukmCG*gQ2yRzLwgUdnNsvgcencqmNaKi5woa{+NzB-4tks!<9ux{ftU0>pN|J8iEUjl(XsP;<@$3PMLBQ+6eSt-JV;Yraw!=|Z zIhpQV&3f4M=ta)Mjz@NRLTV;>&gieUaMC;Bo9%{IMl43h_u*~ zuVH~AKivHgN`*7SHpVINA#$#&%agJNLicRB;80p9^n|vXj$>XRl!lcZYGElxr+%(v z`V?DHj8U84LWt)T&(tPukRj^=>Zei-*f^2L;!9l0M?SY-jKQ`&7tS4dziL9#@~#r1 zSZ}k9zSlUGAu>gc4t{Qpc#I#~8;3r=kI6RI)^ES(t?dA$1&R{KMUNv`x9HXnThb?i)|Kf|IC+Jn}|mW2bKf-_Kr&Z8N&f z+ooTd*4;7xAb+h;y|syH(Zyxq{+-@G04O)wn>S zR-;88T&|WGJKHv+qICn&u7Sibwqoj}53aQ{)Fk=Ae4lFWqDXB8zYtF9ak=rX`hG7T zEF`$(%{~LBsEeaGpIP6CTQ<_ChpwAK{;?a$#nD&ySCFc)Yh*nnk@=I;O>2|Fr_yPJ zPq{G2gMvYhx~(LJT_8028#wuW=wYQ%v4)iSJYR%gi#g%@8>P`7-vt5h!HS}LbUrv@ zt-nujAR&l~BM#-4N?I3l!IG>wbX7$8$c$rj-NK~vNIV`v{~&e}UEWc_C% zCNy1h*kZ#C5_ zxEWY`%kBtsq7Ds6B(Xd{Q&(o%*a1E7@&ryJ%_E+LZ0eF#Jjzav?PJnYXgv5i(W*0- zD(&~uS3UP*vpp4>O+VX&p1&C}=naE4`&utas*nb%fWU-u0gx5c@F)WYA&akmgH>V{ zwrHAanEZwML@Cojl{*Y6$6ORDl^S&w}qin_dzk$=M=7Aua~)Tz|`)C38MeZ9YG~ zQr>M?#b{EbZxS>>Y|=K~);rvhLX3Hg#Ca}$hRg{go6cTPY2M(k-1~Gu8wfffzI`) zrf`Ol=2QOtb0NYe5NK@i0LMQr;Ekf%3<Eo}W>8cbz z`!c*>eUf0aNyH<%EFtZy@I-4eGuGGh7KsCOI0_{$3JG24KIO63eepcaL3Y`2Pg-gX z@gQ*qsUCBHR3y5zY1x^cARA*k=WR84w+a(8VgA>SYC8*`FozkcJtb^HZzXNdU7?Z= zO`}0d>&H&A4H%8;8PjYhn?XR5RqOKL?Rs$Na#h12vP+?F9{?dp*KN5Kw3bM zD!oWmDM1t|(m{|Sh)73}UZr=Ca#2t5eBZfq=HBz2dp&3JM`o|A?8)re^CoN0Jnwpb zpE6u#1K1rL&a#(}>C_=kQk+C@?yL^sb=Rk%ObeFdbj=nHKQnjmXv_L&bu>rW^4O-4 zBvZs58{=^E`}cY!-(aQ5oE1u?@w;P80BQ>;{p=|mkjpaacXt`k!6RUP>0Y>99%ff# z_Kn>61mdu>T!4mILFdlm{YO@S#*1`XF#4m{7_XkM{+sREsRQZLlH$pm^Fj8c_BAKs zEd3<0Q91FEFAPW6Nb)$!^`{EGtEmiI4K9uxJuDFlB;DkWNc>7~ap_IuU1Bw1w{qI< z4m1tInr9rQT}h0xH5evCt=;X{bp#Fas=uTEYR8-ga!l2O&s3k2m;-UAp+nrX1HQ%5qKY4U}J&<>CtMNsoQU5HeXEZhb$ls^lhTheaja)auHtG!#Uk91gu) z)aBjrRgHr@iC2cZO`xCBRpvvlWkIV92iO>neiI_YV?MBvX9Ans96wFKvf~lH6T|6G zi6@rGj)Db-zmILAK@;C!jkaN4C#}LnohD#upg0>9P=1mDkzBYlR3^Z^e#&!V6nUA^ zD}bg$+aulMf^8#82##{Q7%LxMjk~NCnbO&=P#JYO<_~xgj|dACl+goys35STD!!_` zls27IFjKkfKCAmrV{~?OIq_6B*== z5x{dQY`T~tWMZZzJ*LD*id=86s;A?OT=aguw!Jl_%7NBra`wJ({0lgb!_`W?*Yk%l zQKYTTd5Fsl%vm7i3DO0aUlbzkm5d}QeRu{*j8;Dn>NEt4HT&9mLh$SNS^)&4nS_o-z%a(qi%zYj9jEdf+ibfSQ?W(Xi#sAp9U11T!!y%IcZ@ERYvSu>o?}($ z7rtD!Rhy)r>zzQ5LAI7==p}cru0~hM|JLiNcd3Ool_Qf9#aTAPJ_*DV`me{V-_U5` zjO7lf)^u%clq=k>w=!Gk%7bVL3?m_-BaKb?TW*bq%gxW$(o--| z#Ctw3;yKs$W)*uj@cQu`j6TB>2#T*a>IJOikgSvsJW$f4lDRbIW@D88)RZ$R10T;= z>ChWvS0T$fmV1?k7!R?Qo?@_;fMg_rO2b+K#PD@j z|81xczA>*ie<)5KjdY*t{a(^8A^cqZJyIsj&7zhVgbM10DR@mMR%W}=ps3{uo!zVa z{i=3D3@|$V}vHUL^~qFUxlf z_cI5$M?a&WDuI2Gj~>9>m{dB8#pA#IzsYPA4&-}?bpl*PgpHEII&~LjO$!Xg<3Gr8 zH$!4LOQ#uaQsW{7f@sUj44M!7z80e7WgmibeKUySbKhhHr(2N|(p~A)>#>FotD$ye zD^*aHnOl#01Dg!0+SHFHXuyWcwYKzy1a%~bAHO7ykE7I$uc);9<`m5nwb`Zx`__ek z(xyo@ve7w0!Nmp zOL<}wcA*vu!`B#W=>Ez#j?U8I9WnvB;CEaSSYHh*%B<~ZSJ@j4qQ7_LP>&{)TvXzM z4)aGILgJGPh1oX`M>(5^hwkK}3j4+iXxNWwrcH8IuT|87W=w7Agj1KU(VTlx+Z{m@ zF&3$XA*)j_)$Mo=Ee;xWM~&5jri+?ZxxWl(N*CYXYRNyLC}P-Z_`RF+J4^Tzr23=P z?EfRZ{BK-~-(}MBtODPU$|k?D4F1IN{-Jyx1pH6wBmn`xL#JN|asIfXUx{)4Zv>?O z7;=JuKYW?r45T37pY5Q(#hky8g7p6obAo_B_CkE)oIj0EgCzd)f#6T$|4u{t*R%h| zL;BZg(VxCV>@VYgT1AS*omkw7#hqB(iN&2*+=<1VSlo%lomkw7#hqB(iN&2*+=<1V zSlo%lomkw7#hqB(iN&2*+=<1V|L(Z+2Tu5-c;`Q3^ZFO^&Y$GU|JFM?e?{K;QwBKp z-s1j|S^^O8KV>KKuUNM~>Cv%u`v=$Uw|%~U+XM0M8E^Xw)-4Fzy8|`GCsz3vh#x@U zpPm!`j;Tx0+S$dG6(T4q@Ml{oL_}0TomCj}Bkx$l9Hs4Q_fsdye4@~Rt>9c3|%Jc@5=<9l?xHN!T(H2DYfFVI`eBM zjNjO&(H!Nv>A-kh&Ra_iU57raHFq~HKTD1!gbOw2tQlk^PWY415+v`}!ZL|JplFHk zFi2@V(zNp|sT)o<4UmVSRf`2!Xx1!2R1Aqa#vD(y)j}?imFX&iST>%dGm`_>h(p#x$mNG& zd!~FFUUOdcfC#1>QD2QLzSM|!6|$Q&eH$*>5zlzQ&a7DEkNk)uuxtV?!|k1B{aK5Qmlg-sMtU1=zYcqWw%;6sKQ!7d16dGw!9dyqE@%(iztYeLGBO9DI#KAfwmF<*KR;-7ozEfE-F_o>)=V*ZKULRNDq zEr(&Mt<#2rT3GB(;js9%%8t*^L}E?O=9Q8`)%*M^80IG_+OvS@^#dYOry7crV%}-f&R6 zXkpexYtfElN>J-zU;c{&Y6Ia97*D4{lA+n0vOrn=xjmW1exy|YU@3FeP?2d*4*oW!yCu1iJA_(rm9zCj|4mrr}O zA6MZ!^xTUHH+G-PwsQ!(*lfK}yk-|q`~vZDs7o9AGzPB~(YJzvT5jH%+>p*lvZ;33g!i%N zIL%bwGTz)Ucw-YwNZl*hI+pYlDyTx2bAKvgCN)JRPYfVa@I@?aA`J(|FOZiH5Xfr} z+$%}Y>5EC&8Lu;ludaVN?D(wNRFaW+Fk~!5Wk6nc*Yil$!D}~d`LM^Q)+}+f6kjCi zsndOWfgAixEe6*sV$%0q*&Qsiy;BiX4mRNd$vR=Br)}jv&)MQ~5=4eW<*ig+p>K1= zjbb_*vI<9Bc_iBNf?0!7&>pHTrYh_y4-mRDgf^E_pXSQ~>a}S-(9sl3Im}uBs?f@p zYiOOf+93~OLVX}pda59{JI{8Nwyf0HJ@(4m-k#{$^!tg9#BWYI+ z@~x6-jU_YK`26Gu@kE;#CmE!2lG#4iUgrq(riMRb*W{B0lfF{ogkhu>aQyWsax(6$ z!^JMaDAN@w9HHSScCdBo`u1z zO;5s?UD1Vu(@FM{RWmVdpbW(_kmd^rm;CdDx3MvK!!MY&#EstIec|H9*Wt6%@7kHP zj^QtY2h8nS^mi3ZA)0PK)k|4mcZ-3<$w^%sI1J17KOd0q*f+m7&HCX3+~SGcZCj{Q za(I7<@*@2%5pjlu$C4CD00(?()J_kL!qgtDvajHd&LIWc7CDz``H7;~*4m!GjkV=b zn{%`^ZedA(Qt)&Fjou5WuF!}aunMeQ8G8gVOP*r9u@&Ym?PXdiG4B9_gc$jF>X_Xk z>O0^LFhzbSARD&S%;t}keOT*C1erxL^bQt~9dHXhBRQTTLp6##mNkQJyx3ufR_~iH z*(q6_;8=vrkNmy~83g|6@$~O3Mal}SMv_2ta}iNdFi=Dk0TcouzzCq2Ai`Y8LJSBO z1|rNwkw7p~3;_ZQgDt@b5epFn!W?L6i9DSFgMbzgVNnZnOQfZs2=cVu6C#X6f`uVs zaJad!s1VRX5P<-ShzTO$5G3T=ya-&-5-4P8X(?!74i^Inib2fb(*OTgU?QMwk3f1D zo%U9O#lF3OHdbgln5Lx2SK~&s^0EYwyhBB&NO%LQVOf`7p=^GsbB+kIREA z7k>F|1_*wo_{utE1)-KOy>Us92(T76#Z$e`H`(lc(t@ve5|?IdXYxgS+(Nc4JZO4{ z<3(UK`wZ8Gk)2Ry%2%y{1EjaO@)b&-QHm&Fo>6VHvD-f=zAk9|ijP>@4fyc1!mhP` zv146YI47E_@(|XRdvz^VrSn?YQ(D%AyuK`&+PW%QsVRzW`+qJjpD4OKEFN-%{=>}9@j|_QWs?uC>dy#{|XfyY~RgS5~;p`#$daVA+_n?VQ-q(e<5|2s1C~!7E9ECcUp7uMjp^_1j2C?YL4V zZn}~r?&{$1dRCJ0-kp^4-q0cT-f^YT-_arPUT@0YTMRvErqO7wch>;aQ)uK4tMSJf zm~u=_P;>bx-aON`>M}0V2uGUy{#OIA)^2s|mBl$%6f&4&H7gxBmtA{hvk`w|2 Oi2z9ffa@BHr2hrL*!^Ds literal 0 HcmV?d00001 diff --git a/itext/itext.kernel/itext/kernel/pdf/OcgPropertiesCopier.cs b/itext/itext.kernel/itext/kernel/pdf/OcgPropertiesCopier.cs index a323d1124b..7026084ca8 100644 --- a/itext/itext.kernel/itext/kernel/pdf/OcgPropertiesCopier.cs +++ b/itext/itext.kernel/itext/kernel/pdf/OcgPropertiesCopier.cs @@ -40,25 +40,30 @@ private OcgPropertiesCopier() { } // Empty constructor - public static void CopyOCGProperties(PdfDocument fromDocument, PdfDocument toDocument, IDictionary page2page) { + /// Copy unique page OCGs stored inside annotations/xobjects/resources from source pages to destination pages. + /// + /// document from which OCGs should be copied + /// document to which OCGs should be copied + /// page mapping, linking source pages to destination ones + public static void CopyOCGProperties(PdfDocument sourceDocument, PdfDocument destinationDocument, IDictionary + sourceToDestPageMapping) { try { // Configs are not copied - PdfDictionary toOcProperties = toDocument.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.OCProperties + PdfDictionary toOcProperties = destinationDocument.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.OCProperties ); - ICollection fromOcgsToCopy = iText.Kernel.Pdf.OcgPropertiesCopier.GetAllUsedNonFlushedOCGs - (page2page, toOcProperties); - if (fromOcgsToCopy.IsEmpty()) { + ICollection ocgsToCopy = iText.Kernel.Pdf.OcgPropertiesCopier.GetAllUsedNonFlushedOCGs + (sourceToDestPageMapping, toOcProperties); + if (ocgsToCopy.IsEmpty()) { return; } // Reset ocProperties field in order to create it a new at the // method end using the new (merged) OCProperties dictionary - toOcProperties = toDocument.GetCatalog().FillAndGetOcPropertiesDictionary(); - PdfDictionary fromOcProperties = fromDocument.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.OCProperties + toOcProperties = destinationDocument.GetCatalog().FillAndGetOcPropertiesDictionary(); + PdfDictionary fromOcProperties = sourceDocument.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.OCProperties ); - iText.Kernel.Pdf.OcgPropertiesCopier.CopyOCGs(fromOcgsToCopy, toOcProperties, toDocument); - iText.Kernel.Pdf.OcgPropertiesCopier.CopyDDictionary(fromOcgsToCopy, fromOcProperties.GetAsDictionary(PdfName - .D), toOcProperties, toDocument); + iText.Kernel.Pdf.OcgPropertiesCopier.CopyOCGs(ocgsToCopy, toOcProperties, destinationDocument); + iText.Kernel.Pdf.OcgPropertiesCopier.CopyDDictionary(ocgsToCopy, fromOcProperties.GetAsDictionary(PdfName. + D), toOcProperties, destinationDocument); } catch (Exception e) { LOGGER.LogError(MessageFormatUtil.Format(iText.IO.Logs.IoLogMessageConstant.OCG_COPYING_ERROR, e.ToString( @@ -66,6 +71,24 @@ public static void CopyOCGProperties(PdfDocument fromDocument, PdfDocument toDoc } } + /// Get all OCGs from a given page annotations/xobjects/resources, including ones already stored in catalog + /// + /// where to search for OCGs. + /// set of indirect references pointing to found OCGs. + public static ICollection GetOCGsFromPage(PdfPage page) { + //Using linked hash set for elements order consistency (e.g. in tests) + ICollection ocgs = new LinkedHashSet(); + IList annotations = page.GetAnnotations(); + foreach (PdfAnnotation annotation in annotations) { + //Pass null instead of catalog OCProperties value, to include ocg clashing with catalog + GetUsedNonFlushedOCGsFromAnnotation(null, ocgs, annotation, annotation); + } + PdfDictionary resources = page.GetPdfObject().GetAsDictionary(PdfName.Resources); + iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromResources(resources, resources, ocgs, null, + new HashSet()); + return ocgs; + } + private static ICollection GetAllUsedNonFlushedOCGs(IDictionary page2page , PdfDictionary toOcProperties) { // NOTE: the PDF is considered to be valid and therefore the presence of OСG in OCProperties.OCGs is not checked @@ -81,19 +104,10 @@ private static ICollection GetAllUsedNonFlushedOCGs(IDicti IList fromAnnotations = fromPage.GetAnnotations(); for (int j = 0; j < toAnnotations.Count; j++) { if (!toAnnotations[j].IsFlushed()) { - PdfDictionary toAnnotDict = toAnnotations[j].GetPdfObject(); - PdfDictionary fromAnnotDict = fromAnnotations[j].GetPdfObject(); PdfAnnotation toAnnot = toAnnotations[j]; PdfAnnotation fromAnnot = fromAnnotations[j]; - if (!toAnnotDict.IsFlushed()) { - iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromOcDict(toAnnotDict.GetAsDictionary(PdfName.OC - ), fromAnnotDict.GetAsDictionary(PdfName.OC), fromUsedOcgs, toOcProperties); - iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromXObject(toAnnot.GetNormalAppearanceObject(), - fromAnnot.GetNormalAppearanceObject(), fromUsedOcgs, toOcProperties, new HashSet()); - iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromXObject(toAnnot.GetRolloverAppearanceObject( - ), fromAnnot.GetRolloverAppearanceObject(), fromUsedOcgs, toOcProperties, new HashSet()); - iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromXObject(toAnnot.GetDownAppearanceObject(), fromAnnot - .GetDownAppearanceObject(), fromUsedOcgs, toOcProperties, new HashSet()); + if (!toAnnot.GetPdfObject().IsFlushed()) { + GetUsedNonFlushedOCGsFromAnnotation(toOcProperties, fromUsedOcgs, toAnnot, fromAnnot); } } } @@ -105,6 +119,18 @@ private static ICollection GetAllUsedNonFlushedOCGs(IDicti return fromUsedOcgs; } + private static void GetUsedNonFlushedOCGsFromAnnotation(PdfDictionary toOcProperties, ICollection fromUsedOcgs, PdfAnnotation toAnnot, PdfAnnotation fromAnnot) { + iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromOcDict(toAnnot.GetPdfObject().GetAsDictionary + (PdfName.OC), fromAnnot.GetPdfObject().GetAsDictionary(PdfName.OC), fromUsedOcgs, toOcProperties); + iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromXObject(toAnnot.GetNormalAppearanceObject(), + fromAnnot.GetNormalAppearanceObject(), fromUsedOcgs, toOcProperties, new HashSet()); + iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromXObject(toAnnot.GetRolloverAppearanceObject( + ), fromAnnot.GetRolloverAppearanceObject(), fromUsedOcgs, toOcProperties, new HashSet()); + iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromXObject(toAnnot.GetDownAppearanceObject(), fromAnnot + .GetDownAppearanceObject(), fromUsedOcgs, toOcProperties, new HashSet()); + } + private static void GetUsedNonFlushedOCGsFromResources(PdfDictionary toResources, PdfDictionary fromResources , ICollection fromUsedOcgs, PdfDictionary toOcProperties, ICollection visitedObjects) { diff --git a/itext/itext.kernel/itext/kernel/pdf/PdfPage.cs b/itext/itext.kernel/itext/kernel/pdf/PdfPage.cs index 31ddd33550..4293bd1ed9 100644 --- a/itext/itext.kernel/itext/kernel/pdf/PdfPage.cs +++ b/itext/itext.kernel/itext/kernel/pdf/PdfPage.cs @@ -31,6 +31,7 @@ You should have received a copy of the GNU Affero General Public License using iText.Kernel.Pdf.Action; using iText.Kernel.Pdf.Annot; using iText.Kernel.Pdf.Filespec; +using iText.Kernel.Pdf.Layer; using iText.Kernel.Pdf.Tagging; using iText.Kernel.Pdf.Tagutils; using iText.Kernel.Pdf.Xobject; @@ -543,6 +544,24 @@ public virtual iText.Kernel.Pdf.PdfPage CopyTo(PdfDocument toDocument, IPdfPageE return CopyTo(page, toDocument, copier); } + /// Get all pdf layers stored under this page's annotations/xobjects/resources. + /// + /// Get all pdf layers stored under this page's annotations/xobjects/resources. + /// Note that it will include all layers, even those already stored under /OCProperties entry in catalog. + /// To get only unique layers, you can simply exclude ocgs, which already present in catalog. + /// + /// set of pdf layers, associated with this page. + public virtual ICollection GetPdfLayers() { + ICollection ocgs = OcgPropertiesCopier.GetOCGsFromPage(this); + ICollection result = new LinkedHashSet(); + foreach (PdfIndirectReference ocg in ocgs) { + if (ocg.GetRefersTo() != null && ocg.GetRefersTo().IsDictionary()) { + result.Add(new PdfLayer((PdfDictionary)ocg.GetRefersTo())); + } + } + return result; + } + /// Copies page as FormXObject to the specified document. /// a document to copy to. /// diff --git a/port-hash b/port-hash index 238dce8913..66f9860105 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -20941942038f765b1cbc9df1c8ea7f895b03a730 +532081a7ad239eaf43570ac8df5d96e06c4b6281 From f1f0596305f49f47b8ff94703bcb6baf92595170 Mon Sep 17 00:00:00 2001 From: Dmitry Radchuk Date: Thu, 3 Oct 2024 18:27:41 +0000 Subject: [PATCH 2/4] Fix review issues DEVSIX-8576 Autoported commit. Original commit hash: [6595c3a7c] --- .../itext/kernel/pdf/layer/PdfLayerTest.cs | 13 ------------- .../input_layers_in_resources_xobject.pdf | Bin 51032 -> 0 bytes .../itext/kernel/pdf/OcgPropertiesCopier.cs | 8 ++++---- port-hash | 2 +- 4 files changed, 5 insertions(+), 18 deletions(-) delete mode 100644 itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_layers_in_resources_xobject.pdf diff --git a/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs b/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs index 3a519215a9..7f532ececb 100644 --- a/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs +++ b/itext.tests/itext.kernel.tests/itext/kernel/pdf/layer/PdfLayerTest.cs @@ -379,19 +379,6 @@ public virtual void TestReadAllLayersFromPage1() { , sourceFolder + "cmp_output_layered_2.pdf", destinationFolder, "diff")); } - [NUnit.Framework.Test] - public virtual void TestReadAllLayersFromPage2() { - PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFolder + "input_layers_in_resources_xobject.pdf") - ); - IList layersFromCatalog = pdfDoc.GetCatalog().GetOCProperties(true).GetLayers(); - NUnit.Framework.Assert.AreEqual(16, layersFromCatalog.Count); - PdfPage page = pdfDoc.GetPage(2); - ICollection layersFromPage = page.GetPdfLayers(); - //There is 8 ocgs nested under the resources xobject on 2nd page - NUnit.Framework.Assert.AreEqual(8, layersFromPage.Count); - pdfDoc.Close(); - } - [NUnit.Framework.Test] public virtual void TestReadAllLayersFromDocumentWithComplexOCG() { PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFolder + "input_complex_layers.pdf"), CompareTool diff --git a/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_layers_in_resources_xobject.pdf b/itext.tests/itext.kernel.tests/resources/itext/kernel/pdf/layer/PdfLayerTest/input_layers_in_resources_xobject.pdf deleted file mode 100644 index ab05935ce7fc89fb3b155c89b23c24bf553d5cef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51032 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 Oi2z9ffa@BHr2hrL*!^Ds diff --git a/itext/itext.kernel/itext/kernel/pdf/OcgPropertiesCopier.cs b/itext/itext.kernel/itext/kernel/pdf/OcgPropertiesCopier.cs index 7026084ca8..2138e88d47 100644 --- a/itext/itext.kernel/itext/kernel/pdf/OcgPropertiesCopier.cs +++ b/itext/itext.kernel/itext/kernel/pdf/OcgPropertiesCopier.cs @@ -81,7 +81,7 @@ public static ICollection GetOCGsFromPage(PdfPage page) { IList annotations = page.GetAnnotations(); foreach (PdfAnnotation annotation in annotations) { //Pass null instead of catalog OCProperties value, to include ocg clashing with catalog - GetUsedNonFlushedOCGsFromAnnotation(null, ocgs, annotation, annotation); + GetUsedNonFlushedOCGsFromAnnotation(annotation, annotation, ocgs, null); } PdfDictionary resources = page.GetPdfObject().GetAsDictionary(PdfName.Resources); iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromResources(resources, resources, ocgs, null, @@ -107,7 +107,7 @@ private static ICollection GetAllUsedNonFlushedOCGs(IDicti PdfAnnotation toAnnot = toAnnotations[j]; PdfAnnotation fromAnnot = fromAnnotations[j]; if (!toAnnot.GetPdfObject().IsFlushed()) { - GetUsedNonFlushedOCGsFromAnnotation(toOcProperties, fromUsedOcgs, toAnnot, fromAnnot); + GetUsedNonFlushedOCGsFromAnnotation(toAnnot, fromAnnot, fromUsedOcgs, toOcProperties); } } } @@ -119,8 +119,8 @@ private static ICollection GetAllUsedNonFlushedOCGs(IDicti return fromUsedOcgs; } - private static void GetUsedNonFlushedOCGsFromAnnotation(PdfDictionary toOcProperties, ICollection fromUsedOcgs, PdfAnnotation toAnnot, PdfAnnotation fromAnnot) { + private static void GetUsedNonFlushedOCGsFromAnnotation(PdfAnnotation toAnnot, PdfAnnotation fromAnnot, ICollection + fromUsedOcgs, PdfDictionary toOcProperties) { iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromOcDict(toAnnot.GetPdfObject().GetAsDictionary (PdfName.OC), fromAnnot.GetPdfObject().GetAsDictionary(PdfName.OC), fromUsedOcgs, toOcProperties); iText.Kernel.Pdf.OcgPropertiesCopier.GetUsedNonFlushedOCGsFromXObject(toAnnot.GetNormalAppearanceObject(), diff --git a/port-hash b/port-hash index 66f9860105..2c23d56565 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -532081a7ad239eaf43570ac8df5d96e06c4b6281 +6595c3a7c8890a521c1a217c82960c9fdc052420 From efc2d565a1ea0285961a3c630d8eec57771b0c12 Mon Sep 17 00:00:00 2001 From: Eugene Bochilo Date: Wed, 2 Oct 2024 22:33:24 +0000 Subject: [PATCH 3/4] Support linearized PDF files in DocumentsRevisionsValidator DEVSIX-8415 Autoported commit. Original commit hash: [5a9f769c0] --- ...cumentRevisionsValidatorIntegrationTest.cs | 6 +-- .../itext/signatures/SignExtensions.cs | 4 ++ .../validation/DocumentRevisionsValidator.cs | 48 +++++++++++-------- port-hash | 2 +- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/DocumentRevisionsValidatorIntegrationTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/DocumentRevisionsValidatorIntegrationTest.cs index 778d715937..7fb6b362a1 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/DocumentRevisionsValidatorIntegrationTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/DocumentRevisionsValidatorIntegrationTest.cs @@ -79,10 +79,8 @@ public virtual void LinearizedDocTest(bool continueValidationAfterFail) { using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "linearizedDoc.pdf"))) { DocumentRevisionsValidator validator = builder.BuildDocumentRevisionsValidator(); ValidationReport report = validator.ValidateAllDocumentRevisions(validationContext, document); - AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE - ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator - .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.LINEARIZED_NOT_SUPPORTED).WithStatus(ReportItem.ReportItemStatus - .INDETERMINATE))); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(0)); NUnit.Framework.Assert.AreEqual(AccessPermissions.ANNOTATION_MODIFICATION, validator.GetAccessPermissions( )); } diff --git a/itext/itext.sign/itext/signatures/SignExtensions.cs b/itext/itext.sign/itext/signatures/SignExtensions.cs index ecded872e0..e63a98b1a6 100644 --- a/itext/itext.sign/itext/signatures/SignExtensions.cs +++ b/itext/itext.sign/itext/signatures/SignExtensions.cs @@ -88,6 +88,10 @@ public static void AddAll(this IDictionary c, IDicti c[pair.Key] = pair.Value; } } + + public static void Add(this IList list, int index, T elem) { + list.Insert(index, elem); + } public static T JRemoveAt(this IList list, int index) { T value = list[index]; diff --git a/itext/itext.sign/itext/signatures/validation/DocumentRevisionsValidator.cs b/itext/itext.sign/itext/signatures/validation/DocumentRevisionsValidator.cs index 9a051511ec..6b38310377 100644 --- a/itext/itext.sign/itext/signatures/validation/DocumentRevisionsValidator.cs +++ b/itext/itext.sign/itext/signatures/validation/DocumentRevisionsValidator.cs @@ -103,10 +103,6 @@ public class DocumentRevisionsValidator { internal const String FIELD_REMOVED = "Form field {0} was removed or unexpectedly modified."; //\endcond -//\cond DO_NOT_DOCUMENT - internal const String LINEARIZED_NOT_SUPPORTED = "Linearized PDF documents are not supported by DocumentRevisionsValidator."; -//\endcond - //\cond DO_NOT_DOCUMENT internal const String LOCKED_FIELD_KIDS_ADDED = "Kids were added to locked form field \"{0}\"."; //\endcond @@ -390,6 +386,7 @@ internal virtual ValidationReport ValidateAllDocumentRevisions(ValidationContext .INDETERMINATE)); return report; } + MergeRevisionsInLinearizedDocument(document, documentRevisions); SignatureUtil signatureUtil = new SignatureUtil(document); IList signatures = new List(signatureUtil.GetSignatureNames()); if (signatures.IsEmpty()) { @@ -473,6 +470,29 @@ internal virtual void ValidateRevision(DocumentRevision previousRevision, Docume } //\endcond + private void MergeRevisionsInLinearizedDocument(PdfDocument document, IList documentRevisions + ) { + if (documentRevisions.Count > 1) { + // We need to check if document is linearized in first revision + // We don't need to populate validation report in case of exceptions, it will happen later + CreateDocumentAndPerformOperation(documentRevisions[0], document, new ValidationReport(), (firstRevisionDocument + ) => { + if (IsLinearizedPdf(document)) { + ICollection mergedModifiedReferences = new HashSet(documentRevisions + [0].GetModifiedObjects()); + mergedModifiedReferences.AddAll(documentRevisions[1].GetModifiedObjects()); + DocumentRevision mergedRevision = new DocumentRevision(documentRevisions[0].GetEofOffset(), mergedModifiedReferences + ); + documentRevisions.Add(0, mergedRevision); + documentRevisions.JRemoveAt(1); + documentRevisions.JRemoveAt(1); + } + return true; + } + ); + } + } + private bool ValidateRevision(ValidationReport validationReport, ValidationContext context, PdfDocument documentWithoutRevision , PdfDocument documentWithRevision, DocumentRevision currentRevision) { usuallyModifiedObjects = new Pair, ICollection>(CreateUsuallyModifiedObjectsSet @@ -737,25 +757,13 @@ private bool CreateDocumentAndPerformOperation(DocumentRevision revision, PdfDoc } } catch (System.IO.IOException exception) { - if (IsLinearizedPdf(originalDocument)) { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, LINEARIZED_NOT_SUPPORTED, exception, ReportItem.ReportItemStatus - .INDETERMINATE)); - } - else { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, REVISIONS_READING_EXCEPTION, exception, ReportItem.ReportItemStatus - .INDETERMINATE)); - } + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, REVISIONS_READING_EXCEPTION, exception, ReportItem.ReportItemStatus + .INDETERMINATE)); return false; } catch (Exception exception) { - if (IsLinearizedPdf(originalDocument)) { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, LINEARIZED_NOT_SUPPORTED, exception, ReportItem.ReportItemStatus - .INDETERMINATE)); - } - else { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, REVISIONS_READING_EXCEPTION, exception, ReportItem.ReportItemStatus - .INDETERMINATE)); - } + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, REVISIONS_READING_EXCEPTION, exception, ReportItem.ReportItemStatus + .INDETERMINATE)); return false; } } diff --git a/port-hash b/port-hash index 2c23d56565..d583678927 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -6595c3a7c8890a521c1a217c82960c9fdc052420 +1156fe6f2e4a2ea0794e650746efc2cf312edf2f \ No newline at end of file From 0f8556a14fbbc67bb6db29b80e832396a3742ffc Mon Sep 17 00:00:00 2001 From: Glenn Volckaert Date: Fri, 4 Oct 2024 12:40:38 +0000 Subject: [PATCH 4/4] Correct certificate comparisons DEVSIX-8629 Autoported commit. Original commit hash: [a6e77ae17] Manual files: bouncy-castle-adapter/src/main/java/com/itextpdf/bouncycastle/BouncyCastleFactory.java bouncy-castle-adapter/src/main/java/com/itextpdf/bouncycastle/asn1/x500/X500NameBC.java bouncy-castle-adapter/src/main/java/com/itextpdf/bouncycastle/cert/ocsp/BasicOCSPRespBC.java bouncy-castle-adapter/src/main/java/com/itextpdf/bouncycastle/cert/ocsp/RespIDBC.java bouncy-castle-adapter/src/main/java/com/itextpdf/bouncycastle/cert/ocsp/ResponderIDBC.java bouncy-castle-connector/src/main/java/com/itextpdf/bouncycastleconnector/BouncyCastleDefaultFactory.java bouncy-castle-fips-adapter/src/main/java/com/itextpdf/bouncycastlefips/BouncyCastleFipsFactory.java bouncy-castle-fips-adapter/src/main/java/com/itextpdf/bouncycastlefips/asn1/x500/X500NameBCFips.java bouncy-castle-fips-adapter/src/main/java/com/itextpdf/bouncycastlefips/cert/ocsp/BasicOCSPRespBCFips.java bouncy-castle-fips-adapter/src/main/java/com/itextpdf/bouncycastlefips/cert/ocsp/RespIDBCFips.java bouncy-castle-fips-adapter/src/main/java/com/itextpdf/bouncycastlefips/cert/ocsp/ResponderIDBCFips.java commons/src/main/java/com/itextpdf/commons/bouncycastle/IBouncyCastleFactory.java commons/src/main/java/com/itextpdf/commons/bouncycastle/cert/ocsp/IBasicOCSPResp.java sharpenConfiguration.xml Prepare autoport for: Correct certificate comparisons DEVSIX-8629 --- .../TrustedCerrtificatesStoreTest.cs | 252 ++++++++++++++++ .../signatures/validation/CRLValidatorTest.cs | 42 ++- .../CertificateChainValidatorTest.cs | 1 + .../OCSPValidatorIntegrationTest.cs | 9 +- .../validation/OCSPValidatorTest.cs | 54 +++- .../validation/RevocationDataValidatorTest.cs | 2 +- .../SignatureValidatorIntegrationTest.cs | 36 ++- .../mocks/MockIssuingCertificateRetriever.cs | 36 ++- .../mocks/MockTrustedCertificatesStore.cs | 48 +-- .../CRLValidatorTest/createTestData.cmd | 1 + .../multipleCrlIssuerCandidates.yml | 113 +++++++ .../multipleCrlIssuerCandidates/ca.cert.pem | 20 ++ .../multipleCrlIssuerCandidates/chain.pem | 121 ++++++++ .../crl-issuer-candidate1.cert.pem | 20 ++ .../crl-issuer-candidate2.cert.pem | 20 ++ .../crl-issuer.cert.pem | 21 ++ .../intermediate.cert.pem | 20 ++ .../multipleCrlIssuerCandidates/sign.cert.pem | 20 ++ .../candidate1-ocsp-issuer.cert.pem | 20 ++ .../candidate2-ocsp-issuer.cert.pem | 20 ++ .../itext/bouncycastle/BouncyCastleFactory.cs | 5 + .../asn1/ocsp/BasicOcspResponseBC.cs | 6 + .../bouncycastle/asn1/x509/X509NameBC.cs | 5 + .../itext/bouncycastle/cert/ocsp/RespIDBC.cs | 5 + .../bouncycastle/cert/ocsp/ResponderIDBC.cs | 59 ++++ .../BouncyCastleDefaultFactory.cs | 5 + .../BouncyCastleFipsFactory.cs | 5 + .../asn1/ocsp/BasicOcspResponseBCFips.cs | 5 + .../asn1/x500/X500NameBCFips.cs | 5 + .../cert/ocsp/RespIDBCFips.cs | 5 + .../cert/ocsp/ResponderIDBCFips.cs | 59 ++++ .../bouncycastle/IBouncyCastleFactory.cs | 13 + .../asn1/ocsp/IBasicOcspResponse.cs | 2 + .../bouncycastle/asn1/x500/IX500Name.cs | 4 + .../commons/bouncycastle/cert/ocsp/IRespID.cs | 7 + .../bouncycastle/cert/ocsp/IResponderID.cs | 39 +++ .../utils/Collections/MapExtensions.cs | 9 + .../DefaultIssuingCertificateRetriever.cs | 4 + .../IIssuingCertificateRetriever.cs | 13 +- .../signatures/IssuingCertificateRetriever.cs | 247 ++++++++++++--- .../signatures/validation/CRLValidator.cs | 65 ++-- .../validation/CertificateChainValidator.cs | 49 +-- .../signatures/validation/OCSPValidator.cs | 281 ++++++++++-------- .../validation/RevocationDataValidator.cs | 96 +++--- .../validation/TrustedCertificatesStore.cs | 210 ++++++++----- port-hash | 2 +- 46 files changed, 1706 insertions(+), 375 deletions(-) create mode 100644 itext.tests/itext.sign.tests/itext/signatures/TrustedCerrtificatesStoreTest.cs create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates.yml create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/ca.cert.pem create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/chain.pem create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer-candidate1.cert.pem create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer-candidate2.cert.pem create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer.cert.pem create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/intermediate.cert.pem create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/sign.cert.pem create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/OCSPValidatorTest/candidate1-ocsp-issuer.cert.pem create mode 100644 itext.tests/itext.sign.tests/resources/itext/signatures/validation/OCSPValidatorTest/candidate2-ocsp-issuer.cert.pem create mode 100644 itext/itext.bouncy-castle-adapter/itext/bouncycastle/cert/ocsp/ResponderIDBC.cs create mode 100644 itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/cert/ocsp/ResponderIDBCFips.cs create mode 100644 itext/itext.commons/itext/commons/bouncycastle/cert/ocsp/IResponderID.cs diff --git a/itext.tests/itext.sign.tests/itext/signatures/TrustedCerrtificatesStoreTest.cs b/itext.tests/itext.sign.tests/itext/signatures/TrustedCerrtificatesStoreTest.cs new file mode 100644 index 0000000000..37a552d811 --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/TrustedCerrtificatesStoreTest.cs @@ -0,0 +1,252 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 Apryse Group NV +Authors: Apryse Software. + +This program is offered under a commercial and under the AGPL license. +For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below. + +AGPL licensing: +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +using System; +using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Utils; +using iText.Signatures.Testutils; +using iText.Signatures.Validation; +using iText.Test; + +namespace iText.Signatures { + [NUnit.Framework.Category("BouncyCastleUnitTest")] + public class TrustedCerrtificatesStoreTest : ExtendedITextTest { + private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext + .CurrentContext.TestDirectory) + "/resources/itext/signatures/certs/"; + + private static readonly char[] KEY_PASSWORD = "testpassphrase".ToCharArray(); + + private static IX509Certificate crlCert; + + private static IX509Certificate crlRootCert; + + private static IX509Certificate intermediateCert; + + private static IX509Certificate ocspCert; + + private static IX509Certificate rootCert; + + private static IX509Certificate signCert; + + private static IX509Certificate tsaCert; + + private static IX509Certificate tsaRootCert; + + [NUnit.Framework.OneTimeSetUp] + public static void SetUpOnce() { + crlCert = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "crlCert.pem")[0]; + crlRootCert = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "crlRoot.pem")[0]; + intermediateCert = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "intermediate.pem")[0]; + ocspCert = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "ocspCert.pem")[0]; + rootCert = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "root.pem")[0]; + signCert = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "sign.pem")[0]; + tsaCert = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "tsaCert.pem")[0]; + tsaRootCert = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "tsaRoot.pem")[0]; + } + + [NUnit.Framework.Test] + public virtual void TestIsCertificateGenerallyTrusted() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsFalse(sut.IsCertificateGenerallyTrusted(rootCert)); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsTrue(sut.IsCertificateGenerallyTrusted(rootCert)); + } + + [NUnit.Framework.Test] + public virtual void TestIsCertificateTrustedForCA() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsFalse(sut.IsCertificateTrustedForCA(rootCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsTrue(sut.IsCertificateTrustedForCA(rootCert)); + } + + [NUnit.Framework.Test] + public virtual void TestIsCertificateTrustedForTimestamp() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsFalse(sut.IsCertificateTrustedForTimestamp(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsTrue(sut.IsCertificateTrustedForTimestamp(rootCert)); + } + + [NUnit.Framework.Test] + public virtual void TestIsCertificateTrustedForOcsp() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsFalse(sut.IsCertificateTrustedForOcsp(rootCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsTrue(sut.IsCertificateTrustedForOcsp(rootCert)); + } + + [NUnit.Framework.Test] + public virtual void TestIsCertificateTrustedForCrl() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsFalse(sut.IsCertificateTrustedForCrl(rootCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + NUnit.Framework.Assert.IsTrue(sut.IsCertificateTrustedForCrl(rootCert)); + } + + [NUnit.Framework.Test] + public virtual void TestGetKnownCertificates() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(crlCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(ocspCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaCert)); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaRootCert)); + NUnit.Framework.Assert.AreEqual(1, sut.GetKnownCertificates(crlCert.GetSubjectDN().ToString()).Count); + NUnit.Framework.Assert.AreEqual(1, sut.GetKnownCertificates(rootCert.GetSubjectDN().ToString()).Count); + } + + [NUnit.Framework.Test] + public virtual void TestGetAllTrustedCertificates() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaRootCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaRootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaCert)); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaRootCert)); + //duplicates should be removed + NUnit.Framework.Assert.AreEqual(3, sut.GetAllTrustedCertificates().Count); + NUnit.Framework.Assert.IsTrue(sut.GetAllTrustedCertificates().Contains(tsaRootCert)); + NUnit.Framework.Assert.IsTrue(sut.GetAllTrustedCertificates().Contains(rootCert)); + NUnit.Framework.Assert.IsTrue(sut.GetAllTrustedCertificates().Contains(tsaCert)); + } + + [NUnit.Framework.Test] + public virtual void TestGetAllTrustedCertificatesByName() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaRootCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaRootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaCert)); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaRootCert)); + //duplicates should be removed + NUnit.Framework.Assert.AreEqual(1, sut.GetAllTrustedCertificates(tsaRootCert.GetSubjectDN().ToString()).Count + ); + NUnit.Framework.Assert.IsTrue(sut.GetAllTrustedCertificates(tsaRootCert.GetSubjectDN().ToString()).Contains + (tsaRootCert)); + NUnit.Framework.Assert.IsTrue(sut.GetAllTrustedCertificates(rootCert.GetSubjectDN().ToString()).Contains(rootCert + )); + NUnit.Framework.Assert.IsTrue(sut.GetAllTrustedCertificates(tsaCert.GetSubjectDN().ToString()).Contains(tsaCert + )); + } + + [NUnit.Framework.Test] + public virtual void TestGetGenerallyTrustedCertificates() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(signCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(ocspCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(crlCert)); + String name = signCert.GetSubjectDN().ToString(); + NUnit.Framework.Assert.AreEqual(1, sut.GetGenerallyTrustedCertificates(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForCA(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForCrl(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForOcsp(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForTimestamp(name).Count); + } + + [NUnit.Framework.Test] + public virtual void TestGetCertificatesTrustedForCA() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(signCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(ocspCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(crlCert)); + String name = rootCert.GetSubjectDN().ToString(); + NUnit.Framework.Assert.AreEqual(0, sut.GetGenerallyTrustedCertificates(name).Count); + NUnit.Framework.Assert.AreEqual(1, sut.GetCertificatesTrustedForCA(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForCrl(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForOcsp(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForTimestamp(name).Count); + } + + [NUnit.Framework.Test] + public virtual void TestGetCertificatesTrustedForTimeStamp() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(signCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(ocspCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(crlCert)); + String name = tsaCert.GetSubjectDN().ToString(); + NUnit.Framework.Assert.AreEqual(0, sut.GetGenerallyTrustedCertificates(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForCA(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForCrl(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForOcsp(name).Count); + NUnit.Framework.Assert.AreEqual(1, sut.GetCertificatesTrustedForTimestamp(name).Count); + } + + [NUnit.Framework.Test] + public virtual void TestGetCertificatesTrustedForOcsp() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(signCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(ocspCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(crlCert)); + String name = ocspCert.GetSubjectDN().ToString(); + NUnit.Framework.Assert.AreEqual(0, sut.GetGenerallyTrustedCertificates(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForCA(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForCrl(name).Count); + NUnit.Framework.Assert.AreEqual(1, sut.GetCertificatesTrustedForOcsp(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForTimestamp(name).Count); + } + + [NUnit.Framework.Test] + public virtual void TestGetCertificatesTrustedForCrl() { + TrustedCertificatesStore sut = new TrustedCertificatesStore(); + sut.AddGenerallyTrustedCertificates(JavaCollectionsUtil.SingletonList(signCert)); + sut.AddCATrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + sut.AddTimestampTrustedCertificates(JavaCollectionsUtil.SingletonList(tsaCert)); + sut.AddOcspTrustedCertificates(JavaCollectionsUtil.SingletonList(ocspCert)); + sut.AddCrlTrustedCertificates(JavaCollectionsUtil.SingletonList(crlCert)); + String name = crlCert.GetSubjectDN().ToString(); + NUnit.Framework.Assert.AreEqual(0, sut.GetGenerallyTrustedCertificates(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForCA(name).Count); + NUnit.Framework.Assert.AreEqual(1, sut.GetCertificatesTrustedForCrl(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForOcsp(name).Count); + NUnit.Framework.Assert.AreEqual(0, sut.GetCertificatesTrustedForTimestamp(name).Count); + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/CRLValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/CRLValidatorTest.cs index 2b70ff0899..26f4e4c8eb 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/CRLValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/CRLValidatorTest.cs @@ -83,6 +83,46 @@ public virtual void HappyPathTest() { AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID)); } + [NUnit.Framework.Test] + public virtual void MultipleIssuersWithOneMatch() { + RetrieveTestResources("multipleCrlIssuerCandidates"); + byte[] crl = CreateCrl(crlIssuerCert, crlIssuerKey, TimeTestUtil.TEST_DATE_TIME.AddDays(-5), TimeTestUtil. + TEST_DATE_TIME.AddDays(+5)); + IX509Certificate candidateCrlIssuerCert1 = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + + "multipleCrlIssuerCandidates/crl-issuer-candidate1.cert.pem")[0]; + IX509Certificate candidateCrlIssuerCert2 = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + + "multipleCrlIssuerCandidates/crl-issuer-candidate2.cert.pem")[0]; + certificateRetriever.AddTrustedCertificates(JavaUtil.ArraysAsList(candidateCrlIssuerCert1, crlIssuerCert, + candidateCrlIssuerCert2)); + ValidationReport report = PerformValidation("multipleCrlIssuerCandidates", TimeTestUtil.TEST_DATE_TIME, crl + ); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID)); + // expected the CRL validator to stop after correct issuer was found + NUnit.Framework.Assert.AreEqual(2, mockChainValidator.verificationCalls.Count); + } + + [NUnit.Framework.Test] + public virtual void MultipleIssuersWithNoMatch() { + RetrieveTestResources("multipleCrlIssuerCandidates"); + byte[] crl = CreateCrl(crlIssuerCert, crlIssuerKey, TimeTestUtil.TEST_DATE_TIME.AddDays(-5), TimeTestUtil. + TEST_DATE_TIME.AddDays(+5)); + IX509Certificate candidateCrlIssuerCert1 = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + + "multipleCrlIssuerCandidates/crl-issuer-candidate1.cert.pem")[0]; + IX509Certificate candidateCrlIssuerCert2 = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + + "multipleCrlIssuerCandidates/crl-issuer-candidate2.cert.pem")[0]; + certificateRetriever.AddTrustedCertificates(JavaUtil.ArraysAsList(candidateCrlIssuerCert1, candidateCrlIssuerCert2 + )); + IX509Certificate certificateUnderTest = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + "multipleCrlIssuerCandidates/sign.cert.pem" + )[0]; + ValidationReport result = new ValidationReport(); + ValidationContext context = new ValidationContext(ValidatorContext.REVOCATION_DATA_VALIDATOR, CertificateSource + .SIGNER_CERT, TimeBasedContext.PRESENT); + validatorChainBuilder.GetCRLValidator().Validate(result, context, certificateUnderTest, (IX509Crl)CertificateUtil + .ParseCrlFromStream(new MemoryStream(crl)), TimeTestUtil.TEST_DATE_TIME, TimeTestUtil.TEST_DATE_TIME); + AssertValidationReport.AssertThat(result, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + )); + } + [NUnit.Framework.Test] public virtual void NextUpdateBeforeValidationTest() { RetrieveTestResources("happyPath"); @@ -334,7 +374,7 @@ public virtual void CertificateRetrieverFailureTest() { byte[] crl = CreateCrl(crlIssuerCert, crlIssuerKey, TimeTestUtil.TEST_DATE_TIME.AddDays(-5), TimeTestUtil. TEST_DATE_TIME.AddDays(+5)); MockIssuingCertificateRetriever mockCertificateRetriever = new MockIssuingCertificateRetriever(); - mockCertificateRetriever.OngetCrlIssuerCertificatesDo((c) => { + mockCertificateRetriever.OngetCrlIssuerCertificatesByNameDo((c) => { throw new Exception("just testing"); } ); diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/CertificateChainValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/CertificateChainValidatorTest.cs index 9b2a27045e..c2b98495a9 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/CertificateChainValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/CertificateChainValidatorTest.cs @@ -601,6 +601,7 @@ public virtual void TestStopOnInvalidRevocationResultTest() { ); AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID)); NUnit.Framework.Assert.AreEqual(0, mockCertificateRetriever.getCrlIssuerCertificatesCalls.Count); + NUnit.Framework.Assert.AreEqual(0, mockCertificateRetriever.getCrlIssuerCertificatesByNameCalls.Count); NUnit.Framework.Assert.AreEqual(1, mockRevocationDataValidator.calls.Count); } } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/OCSPValidatorIntegrationTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/OCSPValidatorIntegrationTest.cs index d39fbc2e04..c258d771bb 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/OCSPValidatorIntegrationTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/OCSPValidatorIntegrationTest.cs @@ -99,10 +99,11 @@ public virtual void ValidateResponderOcspNoCheckTest() { [NUnit.Framework.Test] public virtual void ValidateAuthorizedOCSPResponderWithOcspTest() { ValidationReport report = VerifyResponderWithOcsp(false); - AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasNumberOfLogs(2).HasLogItems(2 - , (al) => al.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator - .CERTIFICATE_TRUSTED, (l) => ((CertificateReportItem)l).GetCertificate().GetSubjectDN())).HasStatus(ValidationReport.ValidationResult - .VALID)); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasNumberOfLogs(2).HasLogItem((al + ) => al.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (l) => ((CertificateReportItem)l).GetCertificate().GetSubjectDN())).HasLogItem(( + al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.OCSP_RESPONDER_IS_CA)).HasStatus + (ValidationReport.ValidationResult.VALID)); } [NUnit.Framework.Test] diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/OCSPValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/OCSPValidatorTest.cs index 0a28863bd9..def5ead493 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/OCSPValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/OCSPValidatorTest.cs @@ -21,6 +21,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ using System; +using System.Collections.Generic; using iText.Bouncycastleconnector; using iText.Commons.Bouncycastle; using iText.Commons.Bouncycastle.Asn1.Ocsp; @@ -96,6 +97,49 @@ public virtual void HappyPathTest() { AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID)); } + [NUnit.Framework.Test] + public virtual void MultipleIssuerCandidatesHappyPathTest() { + IX509Certificate candidateOcspIssuerCert1 = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + + "candidate1-ocsp-issuer.cert.pem")[0]; + IX509Certificate candidateOcspIssuerCert2 = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + + "candidate2-ocsp-issuer.cert.pem")[0]; + certificateRetriever.AddTrustedCertificates(JavaUtil.ArraysAsList(candidateOcspIssuerCert1, responderCert, + candidateOcspIssuerCert2)); + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetOcspCertsChain(new IX509Certificate[] { caCert }); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. + GetEncoded(checkCert, caCert, null))); + ValidationReport report = new ValidationReport(); + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil + .TEST_DATE_TIME, TimeTestUtil.TEST_DATE_TIME); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID)); + } + + [NUnit.Framework.Test] + public virtual void MultipleIssuerCandidatesFailingTest() { + IX509Certificate candidateOcspIssuerCert1 = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + + "candidate1-ocsp-issuer.cert.pem")[0]; + IX509Certificate candidateOcspIssuerCert2 = (IX509Certificate)PemFileHelper.ReadFirstChain(SOURCE_FOLDER + + "candidate2-ocsp-issuer.cert.pem")[0]; + certificateRetriever.AddTrustedCertificates(JavaUtil.ArraysAsList(candidateOcspIssuerCert1, candidateOcspIssuerCert2 + )); + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetOcspCertsChain(new IX509Certificate[] { caCert }); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. + GetEncoded(checkCert, caCert, null))); + ValidationReport report = new ValidationReport(); + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil + .TEST_DATE_TIME, TimeTestUtil.TEST_DATE_TIME); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + )); + } + [NUnit.Framework.Test] public virtual void OcpsIssuerChainValidationsUsesCorrectParametersTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; @@ -419,6 +463,12 @@ public virtual void CertificateRetrieverIsCertificateTrustedFailureTest() { throw new Exception("Test isCertificateTrusted failure"); } ); + MockTrustedCertificatesStore mockTrustStore = new MockTrustedCertificatesStore(); + mockTrustStore.OnIsCertificateTrustedForOcspDo((c) => { + throw new Exception("Test isCertificateTrusted failure"); + } + ); + mockCertificateRetriever.OnGetTrustedCertificatesStoreDo(() => mockTrustStore); ValidationReport report = ValidateTest(checkDate); AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE ).HasLogItem((l) => l.WithMessage(OCSPValidator.OCSP_RESPONDER_TRUST_NOT_RETRIEVED))); @@ -500,8 +550,8 @@ public TestIssuingCertificateRetriever(String issuerPath) this.issuerCertificate = PemFileHelper.ReadFirstChain(issuerPath)[0]; } - public override IX509Certificate RetrieveIssuerCertificate(IX509Certificate certificate) { - return issuerCertificate; + public override IList RetrieveIssuerCertificate(IX509Certificate certificate) { + return JavaCollectionsUtil.SingletonList((IX509Certificate)issuerCertificate); } } } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/RevocationDataValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/RevocationDataValidatorTest.cs index 5f76316bad..a497b715b9 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/RevocationDataValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/RevocationDataValidatorTest.cs @@ -733,7 +733,7 @@ public virtual void CertificateRetrieverRetrieveIssuerCertificateFailureTest() { mockOCSPValidator.OnCallDo((c) => c.report.AddReportItem(reportItem)); validator.Validate(report, baseContext, checkCert, checkDate); AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE - ).HasLogItem((l) => l.WithMessage(RevocationDataValidator.ISSUER_RETRIEVAL_FAILED))); + ).HasLogItem((l) => l.WithMessage(RevocationDataValidator.UNABLE_TO_RETRIEVE_REV_DATA_ONLINE))); } [NUnit.Framework.Test] diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/SignatureValidatorIntegrationTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/SignatureValidatorIntegrationTest.cs index b846e83843..dd3d1bec17 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/SignatureValidatorIntegrationTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/SignatureValidatorIntegrationTest.cs @@ -80,9 +80,11 @@ public virtual void ValidLatestSignatureTest() { SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); report = signatureValidator.ValidateSignatures(); } - AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItems - (3, (al) => al.WithCertificate(rootCert).WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage - (CertificateChainValidator.CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()))); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItem + ((al) => al.WithCertificate(rootCert).WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage + (CertificateChainValidator.CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN())).HasLogItem((al) => al + .WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.OCSP_RESPONDER_TRUSTED)).HasLogItem + ((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.OCSP_RESPONDER_IS_CA))); } [NUnit.Framework.Test] @@ -107,9 +109,9 @@ public virtual void ShortValidityCertsWithOcspTest() { .UNEXPECTED_ENTRY_IN_XREF, (i) => 30)).HasLogItem((al) => al.WithCheckName(SignatureValidator.SIGNATURE_VERIFICATION ).WithMessage(SignatureValidator.VALIDATING_SIGNATURE_NAME, (i) => "timestampSig1")).HasLogItem((al) => al.WithCheckName(SignatureValidator.SIGNATURE_VERIFICATION).WithMessage(SignatureValidator.VALIDATING_SIGNATURE_NAME - , (i) => "Signature1")).HasLogItems(2, (al) => al.WithCertificate(rootCert).WithCheckName(CertificateChainValidator + , (i) => "Signature1")).HasLogItem((al) => al.WithCertificate(rootCert).WithCheckName(CertificateChainValidator .CERTIFICATE_CHECK).WithMessage(CertificateChainValidator.CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN - ())).HasLogItems(4, (al) => al.WithCertificate(tsRootCert).WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK + ())).HasLogItems(2, (al) => al.WithCertificate(tsRootCert).WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK ).WithMessage(CertificateChainValidator.CERTIFICATE_TRUSTED, (i) => tsRootCert.GetSubjectDN()))); } @@ -195,16 +197,18 @@ public virtual void ValidateSingleSignatureTest1() { (4).HasNumberOfFailures(0).HasLogItem((al) => al.WithCheckName(DocumentRevisionsValidator.DOC_MDP_CHECK ).WithMessage(DocumentRevisionsValidator.UNEXPECTED_ENTRY_IN_XREF, (i) => 17).WithStatus(ReportItem.ReportItemStatus .INFO)).HasLogItem((al) => al.WithCheckName(SignatureValidator.SIGNATURE_VERIFICATION).WithMessage(SignatureValidator - .VALIDATING_SIGNATURE_NAME, (p) => "Signature1")).HasLogItems(2, (al) => al.WithCertificate(rootCert). - WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator.CERTIFICATE_TRUSTED - , (i) => rootCert.GetSubjectDN()))); + .VALIDATING_SIGNATURE_NAME, (p) => "Signature1")).HasLogItem((al) => al.WithCertificate(rootCert).WithCheckName + (CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator.CERTIFICATE_TRUSTED + , (i) => rootCert.GetSubjectDN())).HasLogItem((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage + (OCSPValidator.OCSP_RESPONDER_IS_CA))); AssertValidationReport.AssertThat(report2, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID).HasNumberOfLogs (4).HasNumberOfFailures(1).HasLogItem((al) => al.WithCheckName(DocumentRevisionsValidator.DOC_MDP_CHECK ).WithMessage(DocumentRevisionsValidator.PAGE_ANNOTATIONS_MODIFIED).WithStatus(ReportItem.ReportItemStatus .INVALID)).HasLogItem((al) => al.WithCheckName(SignatureValidator.SIGNATURE_VERIFICATION).WithMessage( - SignatureValidator.VALIDATING_SIGNATURE_NAME, (p) => "Signature2")).HasLogItems(2, (al) => al.WithCertificate + SignatureValidator.VALIDATING_SIGNATURE_NAME, (p) => "Signature2")).HasLogItem((al) => al.WithCertificate (rootCert).WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator - .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()))); + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN())).HasLogItem((al) => al.WithCheckName(OCSPValidator + .OCSP_CHECK).WithMessage(OCSPValidator.OCSP_RESPONDER_IS_CA))); } [NUnit.Framework.Test] @@ -314,8 +318,8 @@ public virtual void LatestSignatureIsTimestampTest() { SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); report = signatureValidator.ValidateLatestSignature(document); } - AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasNumberOfLogs(3).HasLogItems(2 - , (la) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasNumberOfLogs(3).HasLogItem((la + ) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator .CERTIFICATE_TRUSTED, (l) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); } @@ -356,9 +360,11 @@ public virtual void CertificatesNotInLatestSignatureButSetAsKnownTest() { SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); report = signatureValidator.ValidateLatestSignature(document); } - AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItems - (3, (al) => al.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator - .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItem + ((al) => al.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert)).HasLogItem((al) => al + .WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.OCSP_RESPONDER_TRUSTED)).HasLogItem + ((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.OCSP_RESPONDER_IS_CA))); } [NUnit.Framework.Test] diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/mocks/MockIssuingCertificateRetriever.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/mocks/MockIssuingCertificateRetriever.cs index dfa106d10f..aedc046b96 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/mocks/MockIssuingCertificateRetriever.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/mocks/MockIssuingCertificateRetriever.cs @@ -25,6 +25,7 @@ You should have received a copy of the GNU Affero General Public License using System.IO; using iText.Commons.Bouncycastle.Asn1.Ocsp; using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Utils; using iText.Signatures; using iText.Signatures.Validation; @@ -36,6 +37,8 @@ public class MockIssuingCertificateRetriever : IssuingCertificateRetriever { public IList getCrlIssuerCertificatesCalls = new List(); + public IList getCrlIssuerCertificatesByNameCalls = new List(); + public IList retrieveIssuerCertificateCalls = new List(); public IList retrieveOCSPResponderCertificateCalls = new List(); @@ -59,9 +62,11 @@ public class MockIssuingCertificateRetriever : IssuingCertificateRetriever { private Func getCrlIssuerCertificatesHandler; + private Func getCrlIssuerCertificatesByNameHandler; + private Func retrieveIssuerCertificateHandler; - private Func retrieveOCSPResponderCertificateHandler; + private Func> retrieveOCSPResponderCertificateHandler; private Action> setTrustedCertificatesHandler; @@ -105,10 +110,22 @@ public override IX509Certificate[] GetCrlIssuerCertificates(IX509Crl crl) { return new IX509Certificate[0]; } - public override IX509Certificate RetrieveIssuerCertificate(IX509Certificate certificate) { + public override IX509Certificate[][] GetCrlIssuerCertificatesByName(IX509Crl crl) { + getCrlIssuerCertificatesByNameCalls.Add(crl); + if (getCrlIssuerCertificatesByNameHandler != null) { + return getCrlIssuerCertificatesByNameHandler.Invoke(crl); + } + if (wrapped != null) { + return wrapped.GetCrlIssuerCertificatesByName(crl); + } + return new IX509Certificate[0][]; + } + + public override IList RetrieveIssuerCertificate(IX509Certificate certificate) { retrieveIssuerCertificateCalls.Add(certificate); if (retrieveIssuerCertificateHandler != null) { - return retrieveIssuerCertificateHandler.Invoke(certificate); + return JavaCollectionsUtil.SingletonList((IX509Certificate)retrieveIssuerCertificateHandler.Invoke(certificate + )); } if (wrapped != null) { return wrapped.RetrieveIssuerCertificate(certificate); @@ -116,13 +133,14 @@ public override IX509Certificate RetrieveIssuerCertificate(IX509Certificate cert return null; } - public override IX509Certificate RetrieveOCSPResponderCertificate(IBasicOcspResponse ocspResp) { + public override ICollection RetrieveOCSPResponderByNameCertificate(IBasicOcspResponse ocspResp + ) { retrieveOCSPResponderCertificateCalls.Add(ocspResp); if (retrieveOCSPResponderCertificateHandler != null) { return retrieveOCSPResponderCertificateHandler.Invoke(ocspResp); } if (wrapped != null) { - return wrapped.RetrieveOCSPResponderCertificate(ocspResp); + return wrapped.RetrieveOCSPResponderByNameCertificate(ocspResp); } return null; } @@ -202,6 +220,12 @@ public virtual iText.Signatures.Validation.Mocks.MockIssuingCertificateRetriever return this; } + public virtual iText.Signatures.Validation.Mocks.MockIssuingCertificateRetriever OngetCrlIssuerCertificatesByNameDo + (Func callback) { + getCrlIssuerCertificatesByNameHandler = callback; + return this; + } + public virtual iText.Signatures.Validation.Mocks.MockIssuingCertificateRetriever OnRetrieveIssuerCertificateDo (Func callback) { retrieveIssuerCertificateHandler = callback; @@ -209,7 +233,7 @@ public virtual iText.Signatures.Validation.Mocks.MockIssuingCertificateRetriever } public virtual iText.Signatures.Validation.Mocks.MockIssuingCertificateRetriever OnRetrieveOCSPResponderCertificateDo - (Func callback) { + (Func> callback) { retrieveOCSPResponderCertificateHandler = callback; return this; } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/mocks/MockTrustedCertificatesStore.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/mocks/MockTrustedCertificatesStore.cs index 226433363f..929b6f7e02 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/mocks/MockTrustedCertificatesStore.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/mocks/MockTrustedCertificatesStore.cs @@ -63,17 +63,17 @@ public class MockTrustedCertificatesStore : TrustedCertificatesStore { private Func isCertificateTrustedForCAHandler; - private Func getGenerallyTrustedCertificateHandler; + private Func> getGenerallyTrustedCertificateHandler; - private Func getCertificateTrustedForOcspHandler; + private Func> getCertificateTrustedForOcspHandler; - private Func getCertificateTrustedForCrlHandler; + private Func> getCertificateTrustedForCrlHandler; - private Func getCertificateTrustedForTimestampHandler; + private Func> getCertificateTrustedForTimestampHandler; - private Func getCertificateTrustedForCAHandler; + private Func> getCertificateTrustedForCAHandler; - private Func getKnownCertificateHandler; + private Func> getKnownCertificateHandler; private Func> getAllTrustedCertificatesHandler; @@ -140,68 +140,68 @@ public override bool IsCertificateTrustedForCA(IX509Certificate certificate) { return true; } - public override IX509Certificate GetGenerallyTrustedCertificate(String certificateName) { + public override ICollection GetGenerallyTrustedCertificates(String certificateName) { getGenerallyTrustedCertificateCalls.Add(certificateName); if (getGenerallyTrustedCertificateHandler != null) { return getGenerallyTrustedCertificateHandler.Invoke(certificateName); } if (wrapped != null) { - return wrapped.GetGenerallyTrustedCertificate(certificateName); + return wrapped.GetGenerallyTrustedCertificates(certificateName); } return null; } - public override IX509Certificate GetCertificateTrustedForOcsp(String certificateName) { + public override ICollection GetCertificatesTrustedForOcsp(String certificateName) { getCertificateTrustedForOcspCalls.Add(certificateName); if (getCertificateTrustedForOcspHandler != null) { return getCertificateTrustedForOcspHandler.Invoke(certificateName); } if (wrapped != null) { - return wrapped.GetCertificateTrustedForOcsp(certificateName); + return wrapped.GetCertificatesTrustedForOcsp(certificateName); } return null; } - public override IX509Certificate GetCertificateTrustedForCrl(String certificateName) { + public override ICollection GetCertificatesTrustedForCrl(String certificateName) { getCertificateTrustedForCrlCalls.Add(certificateName); if (getCertificateTrustedForCrlHandler != null) { return getCertificateTrustedForCrlHandler.Invoke(certificateName); } if (wrapped != null) { - return wrapped.GetCertificateTrustedForCrl(certificateName); + return wrapped.GetCertificatesTrustedForCrl(certificateName); } return null; } - public override IX509Certificate GetCertificateTrustedForTimestamp(String certificateName) { + public override ICollection GetCertificatesTrustedForTimestamp(String certificateName) { getCertificateTrustedForTimestampCalls.Add(certificateName); if (getCertificateTrustedForTimestampHandler != null) { return getCertificateTrustedForTimestampHandler.Invoke(certificateName); } if (wrapped != null) { - return wrapped.GetCertificateTrustedForTimestamp(certificateName); + return wrapped.GetCertificatesTrustedForTimestamp(certificateName); } return null; } - public override IX509Certificate GetCertificateTrustedForCA(String certificateName) { + public override ICollection GetCertificatesTrustedForCA(String certificateName) { getCertificateTrustedForCACalls.Add(certificateName); if (getCertificateTrustedForCAHandler != null) { return getCertificateTrustedForCAHandler.Invoke(certificateName); } if (wrapped != null) { - return wrapped.GetCertificateTrustedForCA(certificateName); + return wrapped.GetCertificatesTrustedForCA(certificateName); } return null; } - public override IX509Certificate GetKnownCertificate(String certificateName) { + public override ICollection GetKnownCertificates(String certificateName) { getKnownCertificateCalls.Add(certificateName); if (getKnownCertificateHandler != null) { return getKnownCertificateHandler.Invoke(certificateName); } if (wrapped != null) { - return wrapped.GetKnownCertificate(certificateName); + return wrapped.GetKnownCertificates(certificateName); } return null; } @@ -248,37 +248,37 @@ public virtual iText.Signatures.Validation.Mocks.MockTrustedCertificatesStore On } public virtual iText.Signatures.Validation.Mocks.MockTrustedCertificatesStore OnGetGenerallyTrustedCertificateDo - (Func callBack) { + (Func> callBack) { getGenerallyTrustedCertificateHandler = callBack; return this; } public virtual iText.Signatures.Validation.Mocks.MockTrustedCertificatesStore OnGetCertificateTrustedForOcspDo - (Func callBack) { + (Func> callBack) { getCertificateTrustedForOcspHandler = callBack; return this; } public virtual iText.Signatures.Validation.Mocks.MockTrustedCertificatesStore OnGetCertificateTrustedForCrlDo - (Func callBack) { + (Func> callBack) { getCertificateTrustedForCrlHandler = callBack; return this; } public virtual iText.Signatures.Validation.Mocks.MockTrustedCertificatesStore OnGetCertificateTrustedForTimestampDo - (Func callBack) { + (Func> callBack) { getCertificateTrustedForTimestampHandler = callBack; return this; } public virtual iText.Signatures.Validation.Mocks.MockTrustedCertificatesStore OnGetCertificateTrustedForCADo - (Func callBack) { + (Func> callBack) { getCertificateTrustedForCAHandler = callBack; return this; } public virtual iText.Signatures.Validation.Mocks.MockTrustedCertificatesStore OnGetKnownCertificateDo(Func - callBack) { + > callBack) { getKnownCertificateHandler = callBack; return this; } diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/createTestData.cmd b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/createTestData.cmd index 47638363fd..ba87e44b37 100644 --- a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/createTestData.cmd +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/createTestData.cmd @@ -12,6 +12,7 @@ call :runTestCase happyPath call :runTestCase crlIssuerRevokedBeforeSigningDate call :runTestCase crlIssuerAndSignCertHaveNoSharedRoot call :runTestCase crlSignerInValidatedChain +call :runTestCase multipleCrlIssuerCandidates EXIT :runTestCase diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates.yml b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates.yml new file mode 100644 index 0000000000..28a382457b --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates.yml @@ -0,0 +1,113 @@ +external-url-prefix: "http://localhost.test" +keysets: + testkeys: + keys: + ca: + path: keys/root_key.pem + password: testpassphrase + intermediate: + path: keys/im_key.pem + password: testpassphrase + sign: + path: keys/sign-key.pem + password: testpassphrase + crl-issuer: + path: keys/crl-key.pem + password: testpassphrase + crl-issuer-candidate1: + path: keys/im_key.pem + password: testpassphrase + crl-issuer-candidate2: + path: keys/sign-key.pem + password: testpassphrase + +pki-architectures: + default: + keyset: testkeys + entity-defaults: + country-name: BE + organization-name: iText + entities: + ca: + common-name: iTextTestRoot + intermediate: + common-name: iTextTestIntermediate + sign: + common-name: iTextTestSign + crl-issuer: + common-name: iTextTestCrlIssuer + crl-issuer-candidate1: + common-name: iTextTestCrlIssuer + crl-issuer-candidate2: + common-name: iTextTestCrlIssuer + certs: + ca: + subject: ca + issuer: ca + validity: + valid-from: "2000-01-01T00:00:00+0000" + valid-to: "2500-01-01T00:00:00+0000" + extensions: + - id: basic_constraints + critical: true + value: + ca: true + - id: key_usage + critical: true + smart-value: + schema: key-usage + params: [digital_signature, non_repudiation, key_encipherment, key_cert_sign] + intermediate: + issuer: ca + validity: + valid-from: "2000-01-01T00:00:00+0000" + valid-to: "2450-01-01T00:00:00+0000" + extensions: + - id: key_usage + critical: true + smart-value: + schema: key-usage + params: [digital_signature, non_repudiation, key_encipherment, key_cert_sign, crl_sign] + sign: + issuer: intermediate + validity: + valid-from: "2000-01-01T00:00:00+0000" + valid-to: "2400-01-01T00:00:00+0000" + extensions: + - id: key_usage + critical: true + smart-value: + schema: key-usage + params: [digital_signature, non_repudiation] + crl-issuer: + issuer: ca + validity: + valid-from: "2000-01-01T00:00:00+0000" + valid-to: "2450-01-01T00:00:00+0000" + extensions: + - id: key_usage + critical: true + smart-value: + schema: key-usage + params: [ digital_signature, non_repudiation, crl_sign] + - id: crl_distribution_points + smart-value: + schema: crl-dist-url + params: + crl-repo-names: [crl] + crl-issuer-candidate1: + issuer: ca + validity: + valid-from: "2000-01-01T00:00:00+0000" + valid-to: "2450-01-01T00:00:00+0000" + crl-issuer-candidate2: + issuer: ca + validity: + valid-from: "2000-01-01T00:00:00+0000" + valid-to: "2450-01-01T00:00:00+0000" + services: + crl-repo: + crl: + for-issuer: crl-issuer + signing-key: crl-issuer + simulated-update-schedule: "P90D" \ No newline at end of file diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/ca.cert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/ca.cert.pem new file mode 100644 index 0000000000..efb7717302 --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/ca.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI1MDAwMTAxMDAwMDAwWjA1MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxFjAUBgNVBAMMDWlUZXh0VGVzdFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDYxUs+SgZkRkOhOfddCnPeuE1QP2kAm6gAsg4qjNzr +rZpZvwziyWaz6bXXq8fATjXEAdypAiWL/BDZscdCM/M11jds9mMv7dMvCtLns6Oe +4GbChYxxloN2rVxElFPK2siKBaEeWyItr2Ms0P+hSR5uHjFt+krzl2zv828fyEnH +fPvln42SAcuCKsLmfjtutus5jFKBFF8oiqDFlI2eXYggKV7JELttgPLobv2ZyFa5 +nXo/xbtzlPb8AvV3/mxpX4prFhBXupJXuCwmqmzVRqGbwKSz/Bewb/4aJGSpg32x +yIWeXTld58f+Jntfes8xHwK0aJB/Tm2WrH+RWoV+TN/pAgMBAAGjYzBhMB0GA1Ud +DgQWBBR0MlTFwFzoq8mPVusDjIEnL5avqzAfBgNVHSMEGDAWgBR0MlTFwFzoq8mP +VusDjIEnL5avqzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIC5DANBgkq +hkiG9w0BAQsFAAOCAQEAK2dbxbQ7jEdtqvnMpKOZsB55PDO5OZ2hPYilT5ZVZNt1 +jdE94HdCBHED3upmOeBH+XtsK/MLk+eYICM5SZwWVCBY0aQQR0URyTsX4SA/12A5 +VhrnP6bqy4b+3mO3J9s0go59cZOtSkxQ7191teEQMsDeh8GxXGLW/7Tf4x6v7U34 +B7UghMhUD2bHJ7U8MOqto9fVar/9S93tc9vRtYOXZbfRwjFKOYD4IFm7cH3VnrX7 +od3KUEKGKhQ2ZaqrtnW19xNTbdRcMT3+8QSai9DLV0kGGSSY09AHDO3WvxkAs9ZI +cIsMM/n+mLK0Km9fh5uE3k6NmiN3GV+QMyjbmFnVNg== +-----END CERTIFICATE----- diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/chain.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/chain.pem new file mode 100644 index 0000000000..c5a2a1b14e --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/chain.pem @@ -0,0 +1,121 @@ +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI1MDAwMTAxMDAwMDAwWjA1MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxFjAUBgNVBAMMDWlUZXh0VGVzdFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDYxUs+SgZkRkOhOfddCnPeuE1QP2kAm6gAsg4qjNzr +rZpZvwziyWaz6bXXq8fATjXEAdypAiWL/BDZscdCM/M11jds9mMv7dMvCtLns6Oe +4GbChYxxloN2rVxElFPK2siKBaEeWyItr2Ms0P+hSR5uHjFt+krzl2zv828fyEnH +fPvln42SAcuCKsLmfjtutus5jFKBFF8oiqDFlI2eXYggKV7JELttgPLobv2ZyFa5 +nXo/xbtzlPb8AvV3/mxpX4prFhBXupJXuCwmqmzVRqGbwKSz/Bewb/4aJGSpg32x +yIWeXTld58f+Jntfes8xHwK0aJB/Tm2WrH+RWoV+TN/pAgMBAAGjYzBhMB0GA1Ud +DgQWBBR0MlTFwFzoq8mPVusDjIEnL5avqzAfBgNVHSMEGDAWgBR0MlTFwFzoq8mP +VusDjIEnL5avqzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIC5DANBgkq +hkiG9w0BAQsFAAOCAQEAK2dbxbQ7jEdtqvnMpKOZsB55PDO5OZ2hPYilT5ZVZNt1 +jdE94HdCBHED3upmOeBH+XtsK/MLk+eYICM5SZwWVCBY0aQQR0URyTsX4SA/12A5 +VhrnP6bqy4b+3mO3J9s0go59cZOtSkxQ7191teEQMsDeh8GxXGLW/7Tf4x6v7U34 +B7UghMhUD2bHJ7U8MOqto9fVar/9S93tc9vRtYOXZbfRwjFKOYD4IFm7cH3VnrX7 +od3KUEKGKhQ2ZaqrtnW19xNTbdRcMT3+8QSai9DLV0kGGSSY09AHDO3WvxkAs9ZI +cIsMM/n+mLK0Km9fh5uE3k6NmiN3GV+QMyjbmFnVNg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA6MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxGzAZBgNVBAMMEmlUZXh0VGVzdENybElzc3VlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJ6M9OSQgx20Fw14rYFeM6HQuQxBD276e6kX +MpmdsbbkvExq5mrAWPvFGYHCmRAtH7pGm1pL91Zlgh1CbUPzwSa/Lfp9FeAX83G7 +OJn0sY3kJRthtj5B0dGinXSIFCIb6gx9ICzoVpY9Ljvl4tyBr0dkGmED3MBxSFlt +OMPplDhYwFekL7woNXMFIAxMnDWuS7dZ7xEpLAwNfH9D92I4lzSrQ9TrxwDPdW2g +WGvi2eu77W4YqFQgydgbKmFHAzDUTA01LNmKZkASBcLUoiLnLBMmVYgDMH4fo4oj ++2/J/OZNuGv5DK9dRCryAhnY3f8/9b8JzoPeRCmgRO9IU7ObZ/0CAwEAAaNCMEAw +HQYDVR0OBBYEFIlAkiqUVGkQL00y55tlNrfwvGTVMB8GA1UdIwQYMBaAFHQyVMXA +XOiryY9W6wOMgScvlq+rMA0GCSqGSIb3DQEBCwUAA4IBAQAPKoa+iFhdqGoBXjuj ++m/xG2NAetvfpN4Sfy8RGOhEypsre2Xk/MzL6UBa59IYK3cd/x8ceAqPoBozL+NZ +Xu3WzvD65zoLDJxBtHpOjaz3Mi/Yjxlfw7mb+loUj8tusouGhqhvJuKTzpXafFYu +Ue1gwk+Lb3KsunkMhPc5s2h/yTkgbnrBbHuP5g8HRW0pbk3xZC4qHmrMsWHFGLba +CAZ+AcyRaIGHqFQwnflWuMBNXr2KNL6J9IBs33mPNEaJZEqyIr5N7urXcTUS0Cog +ZA3eugW6tacN8hR+6RA4dBIpPVth/tV2V3OtyeRUFKPrFYmaalgfSXYQPzoKDZpr +SP8t +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA6MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxGzAZBgNVBAMMEmlUZXh0VGVzdENybElzc3VlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBALMHHzTZo1BOQkZzEZl2Q3leNfBH20YfO4R0 +l0ZBWbnpg/099ON4I8ItO0f24Wq0gs//+1adlJqeUMeUVxQ0fpu0cHQOc9h7CWJL +nPAJjEvItZ5WXW4l8/ppo1bo4WLB1hToF42NfHgh7Laoj2gqVLWXS6Ngd8chVxIT +axfKA9PG56dje+PlJnTNi+ayudB/O57dhoaAu+sbxBhog/7zRAV87OsH9zxcNuw+ +3ZzHhss5Lpn6F+/WDcHPyXsT7QkX6rvbRvxUOpcysXtCH3v5+ZRKyUqRPlI63/hy +MMZBDqYtFDHiawk587hbMp2swHF6z56d10Za22Vq9lxiX4MdftsCAwEAAaNCMEAw +HQYDVR0OBBYEFFVqj/1gRbt6YJdo1/zMvcyOmY1cMB8GA1UdIwQYMBaAFHQyVMXA +XOiryY9W6wOMgScvlq+rMA0GCSqGSIb3DQEBCwUAA4IBAQC5ZQAycuP/G9srjdfF +nT33PWTfQn98+YybTBcb1Zj1zsD8ELBvN1esksMknNyYwL212CuY0pN32adV9eUQ +A3GrLWEeBICJB6TR6UJ/BSI/0kLeN5TaAb0aRzjENQDAe3tJPXD1vVmNWvYxrDCk +ncdKVOODsz4N+v0+EO8iUUNENDp69VyPBbw0oLjTHCwtqx5B3m35oHibXT3BuQPd +OVDxFfd3PB/CrDWibobVOWw6MaTt5Oiv9WKbo0l0qZ76vyKdjqbQwqHdSfr23k/Y +5PB9YYlqqPPxdSExzka/Oi7n21x1NhAr2T42p1Ff3ph3oQ2P3Qwn72ESu3riImmK +18vH +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA6MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxGzAZBgNVBAMMEmlUZXh0VGVzdENybElzc3VlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMJ7DjX87Zpw3SySm1nfLBP9rcaJse9hwZ6w +TNxtmy6Rn3LwBZvZ/xdFi39XTw2GPeCQBSrARhtBJ4eHgSe+d2R4LoP6sAyIheQ5 +0R+e3thLaCVsF43kpuSiyLGlnFLFBhyQ30vEn1g6jiAwqfqptpr9TPfKaCrCKOJU +R8jOFwy+RERlAGJJQNIeZ6Bz9VdSZwkNP+Gfo+HV9LX057MaeMAPgrvAJrHy/lD9 +y07u0qKN62xzLZ/Mn9do+3qITEU1+9XkX9X0dTXrNMAO9kT3ukR6XdZIl8lvD/2P +gTTaaro51v0ei3I7FZXNokjyuF6J8ARMVxqeOMlVUIxLeBv7j60CAwEAAaOBlzCB +lDAdBgNVHQ4EFgQUEm6hMPB11w1J72tXumBhewwWxnUwHwYDVR0jBBgwFoAUdDJU +xcBc6KvJj1brA4yBJy+Wr6swDgYDVR0PAQH/BAQDAgHCMEIGA1UdHwQ7MDkwN6A1 +oDOGMWh0dHA6Ly9sb2NhbGhvc3QudGVzdC9kZWZhdWx0L2NybHMvY3JsL2xhdGVz +dC5jcmwwDQYJKoZIhvcNAQELBQADggEBAEg69NBvQJkOO25o1UxRoJqKb7PdwF5K +xiidxXjJQTZHVR56rWoMfg/31eDwBdc6bmSAELd2wPSs3Ov3HJJP4f31+plXB3Qb +B5pQUEc5i4IZiE0jz7Be4GLqSVNaLh9ay4o8PnpvcIZiKNukJzPhGsOzW8xrTF7R +Cc6VSjmLHG2UnEt2CBbkaWnVXV8ibENSawv4Ar2AKpcU9LLoT1F92zx/7YpUQLr1 +k/RwWxkKByV5ko001DuwiWf/BjZ67pu5WTgdHQP9KaNMahnSqrjERCTLWVipdvJ2 +o3bHCe9I0OQMVlMeesKMWPRqLiAMpz7f7q09AXlqjvZT9+FPSi6nsss= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDQjCCAiqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA9MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxHjAcBgNVBAMMFWlUZXh0VGVzdEludGVybWVkaWF0ZTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ6M9OSQgx20Fw14rYFeM6HQuQxBD276 +e6kXMpmdsbbkvExq5mrAWPvFGYHCmRAtH7pGm1pL91Zlgh1CbUPzwSa/Lfp9FeAX +83G7OJn0sY3kJRthtj5B0dGinXSIFCIb6gx9ICzoVpY9Ljvl4tyBr0dkGmED3MBx +SFltOMPplDhYwFekL7woNXMFIAxMnDWuS7dZ7xEpLAwNfH9D92I4lzSrQ9TrxwDP +dW2gWGvi2eu77W4YqFQgydgbKmFHAzDUTA01LNmKZkASBcLUoiLnLBMmVYgDMH4f +o4oj+2/J/OZNuGv5DK9dRCryAhnY3f8/9b8JzoPeRCmgRO9IU7ObZ/0CAwEAAaNS +MFAwHQYDVR0OBBYEFIlAkiqUVGkQL00y55tlNrfwvGTVMB8GA1UdIwQYMBaAFHQy +VMXAXOiryY9W6wOMgScvlq+rMA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQsF +AAOCAQEAMoIJ86nzczzR41l5EFHlxGvUEQY/w7BJ42LICN3F12WPmJ+A3i/GAJSQ +14W+Jy8CXEhEFZF3R45vuOAGhOkF7igtbXeeVXunM3rmi8XIlpGjqbBZxC5w1Fd8 +IPOTArrzcF8agwthRzKOEpFSklmpUDT4MYZkdzR7TVM6TkdL+ModYFrhf9/+OFGb +qHzKUPVv+dvNaKn6JvKfQ311MKG3fLU7vyKEUNoolAnLMy3VETsnhMTk6Wa5rfzJ +eq2sdBiLxgwC3xhYk33Cla0X3hsr++Yqb8BX1ztKaRxu0PSi+KKJ9kvXAxRkFlwh +Xa7iIRXelUhftWRF/4VgDsDhWx+eDQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDQjCCAiqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MR4wHAYDVQQDDBVpVGV4dFRlc3RJbnRlcm1lZGlhdGUw +IBcNMDAwMTAxMDAwMDAwWhgPMjQwMDAxMDEwMDAwMDBaMDUxCzAJBgNVBAYTAkJF +MQ4wDAYDVQQKDAVpVGV4dDEWMBQGA1UEAwwNaVRleHRUZXN0U2lnbjCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALMHHzTZo1BOQkZzEZl2Q3leNfBH20Yf +O4R0l0ZBWbnpg/099ON4I8ItO0f24Wq0gs//+1adlJqeUMeUVxQ0fpu0cHQOc9h7 +CWJLnPAJjEvItZ5WXW4l8/ppo1bo4WLB1hToF42NfHgh7Laoj2gqVLWXS6Ngd8ch +VxITaxfKA9PG56dje+PlJnTNi+ayudB/O57dhoaAu+sbxBhog/7zRAV87OsH9zxc +Nuw+3ZzHhss5Lpn6F+/WDcHPyXsT7QkX6rvbRvxUOpcysXtCH3v5+ZRKyUqRPlI6 +3/hyMMZBDqYtFDHiawk587hbMp2swHF6z56d10Za22Vq9lxiX4MdftsCAwEAAaNS +MFAwHQYDVR0OBBYEFFVqj/1gRbt6YJdo1/zMvcyOmY1cMB8GA1UdIwQYMBaAFIlA +kiqUVGkQL00y55tlNrfwvGTVMA4GA1UdDwEB/wQEAwIGwDANBgkqhkiG9w0BAQsF +AAOCAQEAMsIeJp6AHJwChOEddcGu5dD/Q4R6l7dMIe3Q8g8vDVz94572jmvHIOa5 +Q/xJALRFn5rG1oC06q6Gy4ffbUm5S1dNBXdmwyfNzAa5yAwXraIWkXLPM5zFUueB +qu2ekfPhHq0NjCqhVCCYkhngaf+mqeuw8usML6mMf8o0aC6TlQu7GXm/6Z4SSKmY +wDyM4iSVR3fZHb3R/VBP7+GNKsamapZPThdxueWnm6o2vsMKSfgGHRV/41BrUqb7 +t5SXDihXCImTpUr89EL7kix42Q/BFQ60hO3LYsKn4w8gjUQffQdlB2zR6cQJx2eK +9jrkP1+J6xcuWHHrn2l9YXw6rI7r0g== +-----END CERTIFICATE----- diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer-candidate1.cert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer-candidate1.cert.pem new file mode 100644 index 0000000000..cb1f374156 --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer-candidate1.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA6MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxGzAZBgNVBAMMEmlUZXh0VGVzdENybElzc3VlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJ6M9OSQgx20Fw14rYFeM6HQuQxBD276e6kX +MpmdsbbkvExq5mrAWPvFGYHCmRAtH7pGm1pL91Zlgh1CbUPzwSa/Lfp9FeAX83G7 +OJn0sY3kJRthtj5B0dGinXSIFCIb6gx9ICzoVpY9Ljvl4tyBr0dkGmED3MBxSFlt +OMPplDhYwFekL7woNXMFIAxMnDWuS7dZ7xEpLAwNfH9D92I4lzSrQ9TrxwDPdW2g +WGvi2eu77W4YqFQgydgbKmFHAzDUTA01LNmKZkASBcLUoiLnLBMmVYgDMH4fo4oj ++2/J/OZNuGv5DK9dRCryAhnY3f8/9b8JzoPeRCmgRO9IU7ObZ/0CAwEAAaNCMEAw +HQYDVR0OBBYEFIlAkiqUVGkQL00y55tlNrfwvGTVMB8GA1UdIwQYMBaAFHQyVMXA +XOiryY9W6wOMgScvlq+rMA0GCSqGSIb3DQEBCwUAA4IBAQAPKoa+iFhdqGoBXjuj ++m/xG2NAetvfpN4Sfy8RGOhEypsre2Xk/MzL6UBa59IYK3cd/x8ceAqPoBozL+NZ +Xu3WzvD65zoLDJxBtHpOjaz3Mi/Yjxlfw7mb+loUj8tusouGhqhvJuKTzpXafFYu +Ue1gwk+Lb3KsunkMhPc5s2h/yTkgbnrBbHuP5g8HRW0pbk3xZC4qHmrMsWHFGLba +CAZ+AcyRaIGHqFQwnflWuMBNXr2KNL6J9IBs33mPNEaJZEqyIr5N7urXcTUS0Cog +ZA3eugW6tacN8hR+6RA4dBIpPVth/tV2V3OtyeRUFKPrFYmaalgfSXYQPzoKDZpr +SP8t +-----END CERTIFICATE----- diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer-candidate2.cert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer-candidate2.cert.pem new file mode 100644 index 0000000000..dba77cd210 --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer-candidate2.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA6MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxGzAZBgNVBAMMEmlUZXh0VGVzdENybElzc3VlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBALMHHzTZo1BOQkZzEZl2Q3leNfBH20YfO4R0 +l0ZBWbnpg/099ON4I8ItO0f24Wq0gs//+1adlJqeUMeUVxQ0fpu0cHQOc9h7CWJL +nPAJjEvItZ5WXW4l8/ppo1bo4WLB1hToF42NfHgh7Laoj2gqVLWXS6Ngd8chVxIT +axfKA9PG56dje+PlJnTNi+ayudB/O57dhoaAu+sbxBhog/7zRAV87OsH9zxcNuw+ +3ZzHhss5Lpn6F+/WDcHPyXsT7QkX6rvbRvxUOpcysXtCH3v5+ZRKyUqRPlI63/hy +MMZBDqYtFDHiawk587hbMp2swHF6z56d10Za22Vq9lxiX4MdftsCAwEAAaNCMEAw +HQYDVR0OBBYEFFVqj/1gRbt6YJdo1/zMvcyOmY1cMB8GA1UdIwQYMBaAFHQyVMXA +XOiryY9W6wOMgScvlq+rMA0GCSqGSIb3DQEBCwUAA4IBAQC5ZQAycuP/G9srjdfF +nT33PWTfQn98+YybTBcb1Zj1zsD8ELBvN1esksMknNyYwL212CuY0pN32adV9eUQ +A3GrLWEeBICJB6TR6UJ/BSI/0kLeN5TaAb0aRzjENQDAe3tJPXD1vVmNWvYxrDCk +ncdKVOODsz4N+v0+EO8iUUNENDp69VyPBbw0oLjTHCwtqx5B3m35oHibXT3BuQPd +OVDxFfd3PB/CrDWibobVOWw6MaTt5Oiv9WKbo0l0qZ76vyKdjqbQwqHdSfr23k/Y +5PB9YYlqqPPxdSExzka/Oi7n21x1NhAr2T42p1Ff3ph3oQ2P3Qwn72ESu3riImmK +18vH +-----END CERTIFICATE----- diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer.cert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer.cert.pem new file mode 100644 index 0000000000..3656f9e733 --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/crl-issuer.cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA6MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxGzAZBgNVBAMMEmlUZXh0VGVzdENybElzc3VlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMJ7DjX87Zpw3SySm1nfLBP9rcaJse9hwZ6w +TNxtmy6Rn3LwBZvZ/xdFi39XTw2GPeCQBSrARhtBJ4eHgSe+d2R4LoP6sAyIheQ5 +0R+e3thLaCVsF43kpuSiyLGlnFLFBhyQ30vEn1g6jiAwqfqptpr9TPfKaCrCKOJU +R8jOFwy+RERlAGJJQNIeZ6Bz9VdSZwkNP+Gfo+HV9LX057MaeMAPgrvAJrHy/lD9 +y07u0qKN62xzLZ/Mn9do+3qITEU1+9XkX9X0dTXrNMAO9kT3ukR6XdZIl8lvD/2P +gTTaaro51v0ei3I7FZXNokjyuF6J8ARMVxqeOMlVUIxLeBv7j60CAwEAAaOBlzCB +lDAdBgNVHQ4EFgQUEm6hMPB11w1J72tXumBhewwWxnUwHwYDVR0jBBgwFoAUdDJU +xcBc6KvJj1brA4yBJy+Wr6swDgYDVR0PAQH/BAQDAgHCMEIGA1UdHwQ7MDkwN6A1 +oDOGMWh0dHA6Ly9sb2NhbGhvc3QudGVzdC9kZWZhdWx0L2NybHMvY3JsL2xhdGVz +dC5jcmwwDQYJKoZIhvcNAQELBQADggEBAEg69NBvQJkOO25o1UxRoJqKb7PdwF5K +xiidxXjJQTZHVR56rWoMfg/31eDwBdc6bmSAELd2wPSs3Ov3HJJP4f31+plXB3Qb +B5pQUEc5i4IZiE0jz7Be4GLqSVNaLh9ay4o8PnpvcIZiKNukJzPhGsOzW8xrTF7R +Cc6VSjmLHG2UnEt2CBbkaWnVXV8ibENSawv4Ar2AKpcU9LLoT1F92zx/7YpUQLr1 +k/RwWxkKByV5ko001DuwiWf/BjZ67pu5WTgdHQP9KaNMahnSqrjERCTLWVipdvJ2 +o3bHCe9I0OQMVlMeesKMWPRqLiAMpz7f7q09AXlqjvZT9+FPSi6nsss= +-----END CERTIFICATE----- diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/intermediate.cert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/intermediate.cert.pem new file mode 100644 index 0000000000..ca4cf7ba9c --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/intermediate.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQjCCAiqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA9MQswCQYDVQQGEwJCRTEOMAwGA1UE +CgwFaVRleHQxHjAcBgNVBAMMFWlUZXh0VGVzdEludGVybWVkaWF0ZTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ6M9OSQgx20Fw14rYFeM6HQuQxBD276 +e6kXMpmdsbbkvExq5mrAWPvFGYHCmRAtH7pGm1pL91Zlgh1CbUPzwSa/Lfp9FeAX +83G7OJn0sY3kJRthtj5B0dGinXSIFCIb6gx9ICzoVpY9Ljvl4tyBr0dkGmED3MBx +SFltOMPplDhYwFekL7woNXMFIAxMnDWuS7dZ7xEpLAwNfH9D92I4lzSrQ9TrxwDP +dW2gWGvi2eu77W4YqFQgydgbKmFHAzDUTA01LNmKZkASBcLUoiLnLBMmVYgDMH4f +o4oj+2/J/OZNuGv5DK9dRCryAhnY3f8/9b8JzoPeRCmgRO9IU7ObZ/0CAwEAAaNS +MFAwHQYDVR0OBBYEFIlAkiqUVGkQL00y55tlNrfwvGTVMB8GA1UdIwQYMBaAFHQy +VMXAXOiryY9W6wOMgScvlq+rMA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQsF +AAOCAQEAMoIJ86nzczzR41l5EFHlxGvUEQY/w7BJ42LICN3F12WPmJ+A3i/GAJSQ +14W+Jy8CXEhEFZF3R45vuOAGhOkF7igtbXeeVXunM3rmi8XIlpGjqbBZxC5w1Fd8 +IPOTArrzcF8agwthRzKOEpFSklmpUDT4MYZkdzR7TVM6TkdL+ModYFrhf9/+OFGb +qHzKUPVv+dvNaKn6JvKfQ311MKG3fLU7vyKEUNoolAnLMy3VETsnhMTk6Wa5rfzJ +eq2sdBiLxgwC3xhYk33Cla0X3hsr++Yqb8BX1ztKaRxu0PSi+KKJ9kvXAxRkFlwh +Xa7iIRXelUhftWRF/4VgDsDhWx+eDQ== +-----END CERTIFICATE----- diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/sign.cert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/sign.cert.pem new file mode 100644 index 0000000000..bfcfbf0242 --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/CRLValidatorTest/multipleCrlIssuerCandidates/sign.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQjCCAiqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UEBhMCQkUx +DjAMBgNVBAoMBWlUZXh0MR4wHAYDVQQDDBVpVGV4dFRlc3RJbnRlcm1lZGlhdGUw +IBcNMDAwMTAxMDAwMDAwWhgPMjQwMDAxMDEwMDAwMDBaMDUxCzAJBgNVBAYTAkJF +MQ4wDAYDVQQKDAVpVGV4dDEWMBQGA1UEAwwNaVRleHRUZXN0U2lnbjCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALMHHzTZo1BOQkZzEZl2Q3leNfBH20Yf +O4R0l0ZBWbnpg/099ON4I8ItO0f24Wq0gs//+1adlJqeUMeUVxQ0fpu0cHQOc9h7 +CWJLnPAJjEvItZ5WXW4l8/ppo1bo4WLB1hToF42NfHgh7Laoj2gqVLWXS6Ngd8ch +VxITaxfKA9PG56dje+PlJnTNi+ayudB/O57dhoaAu+sbxBhog/7zRAV87OsH9zxc +Nuw+3ZzHhss5Lpn6F+/WDcHPyXsT7QkX6rvbRvxUOpcysXtCH3v5+ZRKyUqRPlI6 +3/hyMMZBDqYtFDHiawk587hbMp2swHF6z56d10Za22Vq9lxiX4MdftsCAwEAAaNS +MFAwHQYDVR0OBBYEFFVqj/1gRbt6YJdo1/zMvcyOmY1cMB8GA1UdIwQYMBaAFIlA +kiqUVGkQL00y55tlNrfwvGTVMA4GA1UdDwEB/wQEAwIGwDANBgkqhkiG9w0BAQsF +AAOCAQEAMsIeJp6AHJwChOEddcGu5dD/Q4R6l7dMIe3Q8g8vDVz94572jmvHIOa5 +Q/xJALRFn5rG1oC06q6Gy4ffbUm5S1dNBXdmwyfNzAa5yAwXraIWkXLPM5zFUueB +qu2ekfPhHq0NjCqhVCCYkhngaf+mqeuw8usML6mMf8o0aC6TlQu7GXm/6Z4SSKmY +wDyM4iSVR3fZHb3R/VBP7+GNKsamapZPThdxueWnm6o2vsMKSfgGHRV/41BrUqb7 +t5SXDihXCImTpUr89EL7kix42Q/BFQ60hO3LYsKn4w8gjUQffQdlB2zR6cQJx2eK +9jrkP1+J6xcuWHHrn2l9YXw6rI7r0g== +-----END CERTIFICATE----- diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/OCSPValidatorTest/candidate1-ocsp-issuer.cert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/OCSPValidatorTest/candidate1-ocsp-issuer.cert.pem new file mode 100644 index 0000000000..d65da4d99c --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/OCSPValidatorTest/candidate1-ocsp-issuer.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQlkx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA+MQswCQYDVQQGEwJCWTEOMAwGA1UE +CgwFaVRleHQxHzAdBgNVBAMMFmlUZXh0VGVzdE9jc3BSZXNwb25kZXIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCejPTkkIMdtBcNeK2BXjOh0LkMQQ9u ++nupFzKZnbG25LxMauZqwFj7xRmBwpkQLR+6RptaS/dWZYIdQm1D88Emvy36fRXg +F/NxuziZ9LGN5CUbYbY+QdHRop10iBQiG+oMfSAs6FaWPS475eLcga9HZBphA9zA +cUhZbTjD6ZQ4WMBXpC+8KDVzBSAMTJw1rku3We8RKSwMDXx/Q/diOJc0q0PU68cA +z3VtoFhr4tnru+1uGKhUIMnYGyphRwMw1EwNNSzZimZAEgXC1KIi5ywTJlWIAzB+ +H6OKI/tvyfzmTbhr+QyvXUQq8gIZ2N3/P/W/Cc6D3kQpoETvSFOzm2f9AgMBAAGj +QjBAMB0GA1UdDgQWBBSJQJIqlFRpEC9NMuebZTa38Lxk1TAfBgNVHSMEGDAWgBR0 +MlTFwFzoq8mPVusDjIEnL5avqzANBgkqhkiG9w0BAQsFAAOCAQEAlA1IOlUFvoOj +HM1VqwYmbTNUUSFpuZV9+njFCLNg6FaLDSB28uszcEzH4NhWfA8YydM+zPLwqIxg +0mOBjaeFaLgWAjPR3ejCexc4jcF6gc3hZTBx5m7i6REiDChWvodCwoa6fZ7dBDvH +3vd4Rlj/h3Ir67L7lTwI9AnBqmrQ49UhkGRHHK4RK9hHdhr1pqZnvRKjuAtApFWC +McTQYJxFIhZPdhF6fHjoozUbEtq6GrviuaYMBul306lXCd5/OzkW84CvOihgT1q7 +OtgCRuOawdMFZTqjk6OHqQ50GFhEfp3WqubYFkH7ADYNO5E8k3rWNVK08ldfKF1s +McB+a0FXzw== +-----END CERTIFICATE----- diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/OCSPValidatorTest/candidate2-ocsp-issuer.cert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/OCSPValidatorTest/candidate2-ocsp-issuer.cert.pem new file mode 100644 index 0000000000..25be340dad --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/OCSPValidatorTest/candidate2-ocsp-issuer.cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQlkx +DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw +MTAwMDAwMFoYDzI0NTAwMTAxMDAwMDAwWjA+MQswCQYDVQQGEwJCWTEOMAwGA1UE +CgwFaVRleHQxHzAdBgNVBAMMFmlUZXh0VGVzdE9jc3BSZXNwb25kZXIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzBx802aNQTkJGcxGZdkN5XjXwR9tG +HzuEdJdGQVm56YP9PfTjeCPCLTtH9uFqtILP//tWnZSanlDHlFcUNH6btHB0DnPY +ewliS5zwCYxLyLWeVl1uJfP6aaNW6OFiwdYU6BeNjXx4Iey2qI9oKlS1l0ujYHfH +IVcSE2sXygPTxuenY3vj5SZ0zYvmsrnQfzue3YaGgLvrG8QYaIP+80QFfOzrB/c8 +XDbsPt2cx4bLOS6Z+hfv1g3Bz8l7E+0JF+q720b8VDqXMrF7Qh97+fmUSslKkT5S +Ot/4cjDGQQ6mLRQx4msJOfO4WzKdrMBxes+enddGWttlavZcYl+DHX7bAgMBAAGj +QjBAMB0GA1UdDgQWBBRVao/9YEW7emCXaNf8zL3MjpmNXDAfBgNVHSMEGDAWgBR0 +MlTFwFzoq8mPVusDjIEnL5avqzANBgkqhkiG9w0BAQsFAAOCAQEAte8X2bL+SorE +EK4pU75vmhr1X8W8rSNXJfy6p6m6GaigNIK4rmAu6bR9B4ai3qZNX9LE/eeKhwZu +yinLPGHema9vbG23FpHnM31mU7P3XAKprjrnrpxWBZ/dNx4IEYKtvDOKFIrR31Pt +stCOm7WXsPc8ovGQj+OnBh+lKaAy7yLNg7o8/8RxOwH9T2VbXcUBcMTnvKVsGiWa +tAiqa15EqnAEN7wAi9OpRkDKOfHdnR/bIv8CC6F3WWIwWmWcyi9st7ISv4ywtBmm +RriPwkjsBmyUu5DKBzFSfLEfnPpD1PjW+XMx0aHFuCf+HYAG8TDRkrhASwFCVb2B +6fFX5LBgew== +-----END CERTIFICATE----- diff --git a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/BouncyCastleFactory.cs b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/BouncyCastleFactory.cs index 088e964085..ffccd18ef9 100644 --- a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/BouncyCastleFactory.cs +++ b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/BouncyCastleFactory.cs @@ -875,6 +875,11 @@ public virtual IX500Name CreateX500Name(String s) { return new X509NameBC(new X509Name(s)); } + public IX500Name CreateX500Name(IAsn1Sequence s) + { + return new X509NameBC(X509Name.GetInstance(((Asn1SequenceBC) s).GetAsn1Sequence())); + } + /// public virtual IRespID CreateRespID(IX500Name x500Name) { return new RespIDBC(x500Name); diff --git a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/ocsp/BasicOcspResponseBC.cs b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/ocsp/BasicOcspResponseBC.cs index 78f8c1ddf4..7edd145ca7 100644 --- a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/ocsp/BasicOcspResponseBC.cs +++ b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/ocsp/BasicOcspResponseBC.cs @@ -22,6 +22,7 @@ You should have received a copy of the GNU Affero General Public License */ using System; using System.Collections.Generic; +using iText.Bouncycastle.Asn1.X509; using Org.BouncyCastle.Asn1.Ocsp; using iText.Bouncycastle.Cert.Ocsp; using iText.Bouncycastle.Crypto; @@ -127,5 +128,10 @@ public IAsn1Encodable GetExtensionParsedValue(IDerObjectIdentifier objectIdentif return new Asn1EncodableBC(GetBasicOcspResponse().TbsResponseData.ResponseExtensions .GetExtensionParsedValue(((DerObjectIdentifierBC)objectIdentifier).GetDerObjectIdentifier())); } + + public IRespID GetResponderId() + { + return new RespIDBC(new X509NameBC(GetBasicOcspResponse().TbsResponseData.ResponderID.Name)); + } } } diff --git a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/x509/X509NameBC.cs b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/x509/X509NameBC.cs index 52bf9c4b58..14410a20e3 100644 --- a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/x509/X509NameBC.cs +++ b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/asn1/x509/X509NameBC.cs @@ -51,5 +51,10 @@ public X509NameBC(X509Name x500Name) public virtual X509Name GetX509Name() { return (X509Name)GetEncodable(); } + + public string GetName() + { + return GetX509Name().ToString(); + } } } diff --git a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/cert/ocsp/RespIDBC.cs b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/cert/ocsp/RespIDBC.cs index b21175562f..9cfc196fb2 100644 --- a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/cert/ocsp/RespIDBC.cs +++ b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/cert/ocsp/RespIDBC.cs @@ -69,6 +69,11 @@ public virtual RespID GetRespID() { return respID; } + /// + public virtual IResponderID ToASN1Primitive() { + return new ResponderIDBC(respID.ToAsn1Object()); + } + /// Indicates whether some other object is "equal to" this one. /// Indicates whether some other object is "equal to" this one. Compares wrapped objects. public override bool Equals(Object o) { diff --git a/itext/itext.bouncy-castle-adapter/itext/bouncycastle/cert/ocsp/ResponderIDBC.cs b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/cert/ocsp/ResponderIDBC.cs new file mode 100644 index 0000000000..4f458913fa --- /dev/null +++ b/itext/itext.bouncy-castle-adapter/itext/bouncycastle/cert/ocsp/ResponderIDBC.cs @@ -0,0 +1,59 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 Apryse Group NV +Authors: Apryse Software. + +This program is offered under a commercial and under the AGPL license. +For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below. + +AGPL licensing: +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +using Org.BouncyCastle.Asn1.Ocsp; +using iText.Bouncycastle.Asn1.X509; +using iText.Commons.Bouncycastle.Asn1.X500; +using iText.Commons.Bouncycastle.Cert.Ocsp; + +namespace iText.Bouncycastle.Cert.Ocsp { + public class ResponderIDBC : IResponderID { + private readonly ResponderID responderID; + + /// + /// Creates new wrapper instance for + /// . + /// + /// + /// + /// + /// to be wrapped + /// + public ResponderIDBC(ResponderID responderID) { + this.responderID = responderID; + } + + /// + public virtual IX500Name GetName() { + return new X509NameBC(responderID.Name); + } + + /// Gets actual org.bouncycastle object being wrapped. + /// + /// wrapped + /// . + /// + public virtual ResponderID GetResponderID() { + return responderID; + } + } +} diff --git a/itext/itext.bouncy-castle-connector/itext/bouncycastleconnector/BouncyCastleDefaultFactory.cs b/itext/itext.bouncy-castle-connector/itext/bouncycastleconnector/BouncyCastleDefaultFactory.cs index d80dc474b7..5951924310 100644 --- a/itext/itext.bouncy-castle-connector/itext/bouncycastleconnector/BouncyCastleDefaultFactory.cs +++ b/itext/itext.bouncy-castle-connector/itext/bouncycastleconnector/BouncyCastleDefaultFactory.cs @@ -522,6 +522,11 @@ public IX500Name CreateX500Name(string s) { throw new NotSupportedException(BouncyCastleLogMessageConstant.BOUNCY_CASTLE_DEPENDENCY_MUST_PRESENT); } + public IX500Name CreateX500Name(IAsn1Sequence s) + { + throw new NotSupportedException(BouncyCastleLogMessageConstant.BOUNCY_CASTLE_DEPENDENCY_MUST_PRESENT); + } + public IRespID CreateRespID(IX500Name x500Name) { throw new NotSupportedException(BouncyCastleLogMessageConstant.BOUNCY_CASTLE_DEPENDENCY_MUST_PRESENT); } diff --git a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/BouncyCastleFipsFactory.cs b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/BouncyCastleFipsFactory.cs index 8ad6eb745b..aa7018c4e2 100644 --- a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/BouncyCastleFipsFactory.cs +++ b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/BouncyCastleFipsFactory.cs @@ -877,6 +877,11 @@ public virtual IX500Name CreateX500Name(String s) { return new X500NameBCFips(new X500Name(s)); } + public IX500Name CreateX500Name(IAsn1Sequence s) + { + return new X500NameBCFips(X500Name.GetInstance(((Asn1SequenceBCFips) s).GetAsn1Sequence())); + } + /// public virtual IRespID CreateRespID(IX500Name x500Name) { return new RespIDBCFips(x500Name); diff --git a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/ocsp/BasicOcspResponseBCFips.cs b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/ocsp/BasicOcspResponseBCFips.cs index d66cb978fb..c462dbac3c 100644 --- a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/ocsp/BasicOcspResponseBCFips.cs +++ b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/ocsp/BasicOcspResponseBCFips.cs @@ -129,5 +129,10 @@ public IAsn1Encodable GetExtensionParsedValue(IDerObjectIdentifier objectIdentif return new Asn1EncodableBCFips(GetBasicOcspResponse().TbsResponseData.ResponseExtensions.GetExtension( ((DerObjectIdentifierBCFips)objectIdentifier).GetDerObjectIdentifier())?.GetParsedValue()); } + + public IRespID GetResponderId() + { + return new RespIDBCFips(GetBasicOcspResponse().TbsResponseData.ResponderID); + } } } diff --git a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/x500/X500NameBCFips.cs b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/x500/X500NameBCFips.cs index f7c1e96604..f68380a71a 100644 --- a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/x500/X500NameBCFips.cs +++ b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/asn1/x500/X500NameBCFips.cs @@ -51,5 +51,10 @@ public X500NameBCFips(X500Name x500Name) public virtual X500Name GetX500Name() { return (X500Name)GetEncodable(); } + + public string GetName() + { + return GetX500Name().ToString(); + } } } diff --git a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/cert/ocsp/RespIDBCFips.cs b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/cert/ocsp/RespIDBCFips.cs index 2a17c0e146..fcc64f6999 100644 --- a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/cert/ocsp/RespIDBCFips.cs +++ b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/cert/ocsp/RespIDBCFips.cs @@ -95,5 +95,10 @@ public override int GetHashCode() { public override String ToString() { return respID.ToString(); } + + public IResponderID ToASN1Primitive() + { + return new ResponderIDBCFips(respID); + } } } diff --git a/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/cert/ocsp/ResponderIDBCFips.cs b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/cert/ocsp/ResponderIDBCFips.cs new file mode 100644 index 0000000000..5fa28964cb --- /dev/null +++ b/itext/itext.bouncy-castle-fips-adapter/itext/bouncycastlefips/cert/ocsp/ResponderIDBCFips.cs @@ -0,0 +1,59 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 Apryse Group NV +Authors: Apryse Software. + +This program is offered under a commercial and under the AGPL license. +For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below. + +AGPL licensing: +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +using Org.BouncyCastle.Asn1.Ocsp; +using iText.Bouncycastlefips.Asn1.X500; +using iText.Commons.Bouncycastle.Asn1.X500; +using iText.Commons.Bouncycastle.Cert.Ocsp; + +namespace iText.Bouncycastlefips.Cert.Ocsp { + public class ResponderIDBCFips : IResponderID { + private readonly ResponderID responderID; + + /// + /// Creates new wrapper instance for + /// . + /// + /// + /// + /// + /// to be wrapped + /// + public ResponderIDBCFips(ResponderID responderID) { + this.responderID = responderID; + } + + /// + public virtual IX500Name GetName() { + return new X500NameBCFips(responderID.Name); + } + + /// Gets actual org.bouncycastle object being wrapped. + /// + /// wrapped + /// . + /// + public virtual ResponderID GetResponderID() { + return responderID; + } + } +} diff --git a/itext/itext.commons/itext/commons/bouncycastle/IBouncyCastleFactory.cs b/itext/itext.commons/itext/commons/bouncycastle/IBouncyCastleFactory.cs index 451b308e64..5eac94997c 100644 --- a/itext/itext.commons/itext/commons/bouncycastle/IBouncyCastleFactory.cs +++ b/itext/itext.commons/itext/commons/bouncycastle/IBouncyCastleFactory.cs @@ -1054,6 +1054,19 @@ ITimeStampTokenGenerator CreateTimeStampTokenGenerator(IPrivateKey pk, IX509Cert /// /// created X500 Name wrapper IX500Name CreateX500Name(String s); + + + /// + /// Create X500 Name wrapper from + /// . + /// + /// + /// + /// + /// to create X500 Name wrapper from + /// + /// created X500 Name wrapper + IX500Name CreateX500Name(IAsn1Sequence s); /// Create resp ID wrapper from X500 Name wrapper. /// X500 Name wrapper to create resp ID wrapper from diff --git a/itext/itext.commons/itext/commons/bouncycastle/asn1/ocsp/IBasicOcspResponse.cs b/itext/itext.commons/itext/commons/bouncycastle/asn1/ocsp/IBasicOcspResponse.cs index 6d6251afc5..e33fffa079 100644 --- a/itext/itext.commons/itext/commons/bouncycastle/asn1/ocsp/IBasicOcspResponse.cs +++ b/itext/itext.commons/itext/commons/bouncycastle/asn1/ocsp/IBasicOcspResponse.cs @@ -93,5 +93,7 @@ public interface IBasicOcspResponse : IAsn1Encodable { /// /// Parsed extension value. IAsn1Encodable GetExtensionParsedValue(IDerObjectIdentifier getIdPkixOcspArchiveCutoff); + + IRespID GetResponderId(); } } diff --git a/itext/itext.commons/itext/commons/bouncycastle/asn1/x500/IX500Name.cs b/itext/itext.commons/itext/commons/bouncycastle/asn1/x500/IX500Name.cs index 5268d45cbb..d57d4d7fec 100644 --- a/itext/itext.commons/itext/commons/bouncycastle/asn1/x500/IX500Name.cs +++ b/itext/itext.commons/itext/commons/bouncycastle/asn1/x500/IX500Name.cs @@ -20,6 +20,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +using System; using iText.Commons.Bouncycastle.Asn1; namespace iText.Commons.Bouncycastle.Asn1.X500 { @@ -28,5 +29,8 @@ namespace iText.Commons.Bouncycastle.Asn1.X500 { /// to switch between bouncy-castle and bouncy-castle FIPS implementations. /// public interface IX500Name : IAsn1Encodable { + /// Gets the RFC2253 name. + /// the RFC2253 name + String GetName(); } } diff --git a/itext/itext.commons/itext/commons/bouncycastle/cert/ocsp/IRespID.cs b/itext/itext.commons/itext/commons/bouncycastle/cert/ocsp/IRespID.cs index bab491d4ee..1b8062bb6a 100644 --- a/itext/itext.commons/itext/commons/bouncycastle/cert/ocsp/IRespID.cs +++ b/itext/itext.commons/itext/commons/bouncycastle/cert/ocsp/IRespID.cs @@ -26,5 +26,12 @@ namespace iText.Commons.Bouncycastle.Cert.Ocsp { /// to switch between bouncy-castle and bouncy-castle FIPS implementations. /// public interface IRespID { + /// + /// Calls actual + /// toASN1Primitive + /// method for the wrapped BasicOCSPResp object. + /// + /// Responder ID as a ASN1 primitive. + IResponderID ToASN1Primitive(); } } diff --git a/itext/itext.commons/itext/commons/bouncycastle/cert/ocsp/IResponderID.cs b/itext/itext.commons/itext/commons/bouncycastle/cert/ocsp/IResponderID.cs new file mode 100644 index 0000000000..7199184abc --- /dev/null +++ b/itext/itext.commons/itext/commons/bouncycastle/cert/ocsp/IResponderID.cs @@ -0,0 +1,39 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 Apryse Group NV +Authors: Apryse Software. + +This program is offered under a commercial and under the AGPL license. +For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below. + +AGPL licensing: +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +using iText.Commons.Bouncycastle.Asn1.X500; + +namespace iText.Commons.Bouncycastle.Cert.Ocsp { + /// + /// This interface represents the wrapper for ResponderID that provides the ability + /// to switch between bouncy-castle and bouncy-castle FIPS implementations. + /// + public interface IResponderID { + /// + /// Calls actual + /// getName + /// method for the wrapped BasicOCSPResp object. + /// + /// wrapped X500NAme. + IX500Name GetName(); + } +} diff --git a/itext/itext.commons/itext/commons/utils/Collections/MapExtensions.cs b/itext/itext.commons/itext/commons/utils/Collections/MapExtensions.cs index df1ad21f57..f44e899d6b 100644 --- a/itext/itext.commons/itext/commons/utils/Collections/MapExtensions.cs +++ b/itext/itext.commons/itext/commons/utils/Collections/MapExtensions.cs @@ -38,5 +38,14 @@ public static V ComputeIfAbsent(this IDictionary dict, K key, Func(this IDictionary dict, K key, V defaultValue) + { + if (!dict.ContainsKey(key)) + { + return defaultValue; + } + return dict[key]; + } } } \ No newline at end of file diff --git a/itext/itext.sign/itext/signatures/DefaultIssuingCertificateRetriever.cs b/itext/itext.sign/itext/signatures/DefaultIssuingCertificateRetriever.cs index 4afd4f6920..9e152d182c 100644 --- a/itext/itext.sign/itext/signatures/DefaultIssuingCertificateRetriever.cs +++ b/itext/itext.sign/itext/signatures/DefaultIssuingCertificateRetriever.cs @@ -66,6 +66,10 @@ public virtual IX509Certificate[] GetCrlIssuerCertificates(IX509Crl crl) { return new IX509Certificate[0]; } + public virtual IX509Certificate[][] GetCrlIssuerCertificatesByName(IX509Crl crl) { + return new IX509Certificate[0][]; + } + /// /// /// diff --git a/itext/itext.sign/itext/signatures/IIssuingCertificateRetriever.cs b/itext/itext.sign/itext/signatures/IIssuingCertificateRetriever.cs index 403414c66a..ba3005e74e 100644 --- a/itext/itext.sign/itext/signatures/IIssuingCertificateRetriever.cs +++ b/itext/itext.sign/itext/signatures/IIssuingCertificateRetriever.cs @@ -39,14 +39,23 @@ public interface IIssuingCertificateRetriever { IX509Certificate[] RetrieveMissingCertificates(IX509Certificate[] chain); /// - /// Retrieves certificates that can be used to verify the signature on the CRL response using CRL - /// Authority Information Access (AIA) Extension. + /// Retrieves the certificate chain for the certificate that should be used to verify the signature on the + /// CRL response using CRL Authority Information Access (AIA) Extension and known certificates. /// /// CRL response to retrieve issuer for. /// certificates retrieved from CRL AIA extension or an empty list in case certificates cannot be retrieved. /// IX509Certificate[] GetCrlIssuerCertificates(IX509Crl crl); + /// + /// Retrieves the certificate chaind for the certificates that could be used to verify the signature on the + /// CRL response using CRL Authority Information Access (AIA) Extension and known certificates. + /// + /// CRL response to retrieve issuer for. + /// certificates retrieved from CRL AIA extension or an empty list in case certificates cannot be retrieved. + /// + IX509Certificate[][] GetCrlIssuerCertificatesByName(IX509Crl crl); + /// Sets trusted certificate list to be used for the missing certificates retrieving by the issuer name. /// /// diff --git a/itext/itext.sign/itext/signatures/IssuingCertificateRetriever.cs b/itext/itext.sign/itext/signatures/IssuingCertificateRetriever.cs index efc476716a..cc21beb6de 100644 --- a/itext/itext.sign/itext/signatures/IssuingCertificateRetriever.cs +++ b/itext/itext.sign/itext/signatures/IssuingCertificateRetriever.cs @@ -24,9 +24,13 @@ You should have received a copy of the GNU Affero General Public License using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Logging; +using iText.Bouncycastleconnector; using iText.Commons; +using iText.Commons.Bouncycastle; using iText.Commons.Bouncycastle.Asn1.Ocsp; using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Utils; +using iText.Commons.Utils.Collections; using iText.Signatures.Logs; using iText.Signatures.Validation; @@ -36,13 +40,15 @@ namespace iText.Signatures { /// default implementation. /// public class IssuingCertificateRetriever : IIssuingCertificateRetriever { + private static readonly IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory(); + private static readonly ILogger LOGGER = ITextLogManager.GetLogger(typeof(iText.Signatures.IssuingCertificateRetriever )); private readonly TrustedCertificatesStore trustedCertificatesStore = new TrustedCertificatesStore(); - private readonly IDictionary knownCertificates = new Dictionary(); + private readonly IDictionary> knownCertificates = new Dictionary>(); /// /// Creates @@ -80,10 +86,11 @@ public virtual IX509Certificate[] RetrieveMissingCertificates(IX509Certificate[] ICollection certificatesFromAIA = ProcessCertificatesFromAIA(url); if (certificatesFromAIA == null || certificatesFromAIA.IsEmpty()) { // Retrieve Issuer from the certificate store - IX509Certificate issuer = trustedCertificatesStore.GetKnownCertificate(lastAddedCert.GetIssuerDN().ToString - ()); - if (issuer == null) { - issuer = knownCertificates.Get(lastAddedCert.GetIssuerDN().ToString()); + IX509Certificate issuer = GetIssuerFromCertificateSet(lastAddedCert, trustedCertificatesStore.GetKnownCertificates + (lastAddedCert.GetIssuerDN().ToString())); + if (issuer == null || !IsSignedBy(lastAddedCert, issuer)) { + issuer = GetIssuerFromCertificateSet(lastAddedCert, knownCertificates.Get(lastAddedCert.GetIssuerDN().ToString + ())); if (issuer == null) { // Unable to retrieve missing certificates while (i < chain.Length) { @@ -104,6 +111,97 @@ public virtual IX509Certificate[] RetrieveMissingCertificates(IX509Certificate[] return fullChain.ToArray(new IX509Certificate[0]); } + /// This method tries to rebuild certificate issuer chain. + /// + /// This method tries to rebuild certificate issuer chain. The result contains all possible chains + /// starting with the given certificate based on issuer names and public keys. + /// + /// + /// + /// + /// for which issuer chains shall be built + /// + /// all possible issuer chains + public virtual IList BuildCertificateChains(IX509Certificate certificate) { + return BuildCertificateChains(new IX509Certificate[] { certificate }); + } + + /// This method tries to rebuild certificate issuer chain. + /// + /// This method tries to rebuild certificate issuer chain. The result contains all possible chains + /// starting with the given certificate array based on issuer names and public keys. + /// + /// + /// + /// + /// array for which issuer chains shall be built + /// + /// all possible issuer chains + public virtual IList BuildCertificateChains(IX509Certificate[] certificate) { + IList> allCertificateChains = BuildCertificateChainsList(certificate); + IList result = new List(allCertificateChains.Count * 5); + foreach (IList chain in allCertificateChains) { + JavaCollectionsUtil.Reverse(chain); + result.Add(chain.ToArray(new IX509Certificate[0])); + } + return result; + } + + private IList> BuildCertificateChainsList(IX509Certificate[] certificates) { + IList> allChains = new List>(BuildCertificateChainsList(certificates + [certificates.Length - 1])); + foreach (IList issuerChain in allChains) { + for (int i = certificates.Length - 2; i >= 0; --i) { + issuerChain.Add(certificates[i]); + } + } + return allChains; + } + + private IList> BuildCertificateChainsList(IX509Certificate certificate) { + if (CertificateUtil.IsSelfSigned(certificate)) { + IList> singleChain = new List>(); + IList chain = new List(); + chain.Add(certificate); + singleChain.Add(chain); + return singleChain; + } + IList> allChains = new List>(); + // Get missing certificates using AIA Extensions + String url = CertificateUtil.GetIssuerCertURL(certificate); + ICollection certificatesFromAIA = ProcessCertificatesFromAIA(url); + if (certificatesFromAIA != null && !certificatesFromAIA.IsEmpty()) { + IList> issuerChains = BuildCertificateChainsList(certificatesFromAIA.ToArray(new IX509Certificate + [0])); + foreach (IList issuerChain in issuerChains) { + issuerChain.Add(certificate); + allChains.Add(issuerChain); + } + } + else { + ICollection possibleIssuers = trustedCertificatesStore.GetKnownCertificates(certificate. + GetIssuerDN().ToString()); + if (knownCertificates.Get(certificate.GetIssuerDN().ToString()) != null) { + possibleIssuers.AddAll(knownCertificates.Get(certificate.GetIssuerDN().ToString())); + } + if (possibleIssuers.IsEmpty()) { + IList> singleChain = new List>(); + IList chain = new List(); + chain.Add(certificate); + singleChain.Add(chain); + return singleChain; + } + foreach (IX509Certificate possibleIssuer in possibleIssuers) { + IList> issuerChains = BuildCertificateChainsList((IX509Certificate)possibleIssuer); + foreach (IList issuerChain in issuerChains) { + issuerChain.Add(certificate); + allChains.Add(issuerChain); + } + } + } + return allChains; + } + /// Retrieve issuer certificate for the provided certificate. /// /// @@ -115,51 +213,41 @@ public virtual IX509Certificate[] RetrieveMissingCertificates(IX509Certificate[] /// /// if there is no issuer certificate, or it cannot be retrieved. /// - public virtual IX509Certificate RetrieveIssuerCertificate(IX509Certificate certificate) { - IX509Certificate[] certificateChain = RetrieveMissingCertificates(new IX509Certificate[] { certificate }); - if (certificateChain.Length > 1) { - return certificateChain[1]; + public virtual IList RetrieveIssuerCertificate(IX509Certificate certificate) { + IList result = new List(); + foreach (IX509Certificate[] certificateChain in BuildCertificateChains((IX509Certificate)certificate)) { + if (certificateChain.Length > 1) { + result.Add(certificateChain[1]); + } } - return null; + return result; } /// - /// Retrieves OCSP responder certificate either from the response certs or + /// Retrieves OCSP responder certificate candidates either from the response certs or /// trusted store in case responder certificate isn't found in /Certs. /// /// basic OCSP response to get responder certificate for - /// retrieved OCSP responder certificate or null in case it wasn't found. - public virtual IX509Certificate RetrieveOCSPResponderCertificate(IBasicOcspResponse ocspResp) { + /// retrieved OCSP responder candidates or an empty set in case none were found. + public virtual ICollection RetrieveOCSPResponderByNameCertificate(IBasicOcspResponse ocspResp + ) { + String name = null; + name = FACTORY.CreateX500Name(FACTORY.CreateASN1Sequence(ocspResp.GetResponderId().ToASN1Primitive().GetName + ().ToASN1Primitive())).GetName(); // Look for the existence of an Authorized OCSP responder inside the cert chain in the ocsp response. IEnumerable certs = SignUtils.GetCertsFromOcspResponse(ocspResp); foreach (IX509Certificate cert in certs) { try { - if (CertificateUtil.IsSignatureValid(ocspResp, cert)) { - return cert; + if (name.Equals(cert.GetSubjectDN().ToString())) { + return JavaCollectionsUtil.Singleton(cert); } } catch (Exception) { } } // Ignore. - // Certificate chain is not present in the response. - // Try to verify using trusted store according to RFC 6960 2.2. Response: - // "The key used to sign the response MUST belong to one of the following: - // - ... - // - a Trusted Responder whose public key is trusted by the requester; - // - ..." - try { - foreach (IX509Certificate anchor in trustedCertificatesStore.GetAllTrustedCertificates()) { - if (CertificateUtil.IsSignatureValid(ocspResp, anchor)) { - // Certificate from the root store is considered trusted and valid by this method. - return anchor; - } - } - } - catch (Exception) { - } - // Ignore. - return null; + // Certificate chain is not present in the response, or is does not contain the responder. + return trustedCertificatesStore.GetKnownCertificates(name); } /// @@ -172,27 +260,59 @@ public virtual IX509Certificate RetrieveOCSPResponderCertificate(IBasicOcspRespo /// /// public virtual IX509Certificate[] GetCrlIssuerCertificates(IX509Crl crl) { + IX509Certificate[][] result = GetCrlIssuerCertificatesGeneric(crl, true); + if (result.Length == 0) { + return new IX509Certificate[0]; + } + return result[0]; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual IX509Certificate[][] GetCrlIssuerCertificatesByName(IX509Crl crl) { + return GetCrlIssuerCertificatesGeneric(crl, false); + } + + private IX509Certificate[][] GetCrlIssuerCertificatesGeneric(IX509Crl crl, bool verify) { // Usually CRLs are signed using CA certificate, so we don’t need to do anything extra and the revocation data // is already collected. However, it is possible to sign it with any other certificate. // IssuingDistributionPoint extension: https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5 // Nothing special for the indirect CRLs. // AIA Extension + List matches = new List(); String url = CertificateUtil.GetIssuerCertURL(crl); IList certificatesFromAIA = (IList)ProcessCertificatesFromAIA(url); if (certificatesFromAIA == null) { // Retrieve Issuer from the certificate store - IX509Certificate issuer = trustedCertificatesStore.GetKnownCertificate(((IX509Crl)crl).GetIssuerDN().ToString - ()); - if (issuer == null) { - issuer = knownCertificates.Get(((IX509Crl)crl).GetIssuerDN().ToString()); - if (issuer == null) { - // Unable to retrieve CRL issuer - return new IX509Certificate[0]; + ICollection issuers = trustedCertificatesStore.GetKnownCertificates(((IX509Crl)crl).GetIssuerDN + ().ToString()); + if (issuers == null) { + issuers = new HashSet(); + } + IList localIssuers = GetCrlIssuersFromKnownCertificates((IX509Crl)crl); + if (localIssuers != null) { + issuers.AddAll(localIssuers); + } + if (issuers.IsEmpty()) { + // Unable to retrieve CRL issuer + return new IX509Certificate[0][]; + } + foreach (IX509Certificate i in issuers) { + if (!verify || IsSignedBy((IX509Crl)crl, i)) { + matches.AddAll(BuildCertificateChains((IX509Certificate)i)); } } - return RetrieveMissingCertificates(new IX509Certificate[] { issuer }); + return matches.ToArray(new IX509Certificate[][] { }); } - return RetrieveMissingCertificates(certificatesFromAIA.ToArray(new IX509Certificate[0])); + return BuildCertificateChains(certificatesFromAIA.ToArray(new IX509Certificate[0])).ToArray(new IX509Certificate + [][] { }); } /// Sets trusted certificate list to be used as certificates trusted for any possible usage. @@ -226,7 +346,10 @@ public virtual void AddTrustedCertificates(ICollection certifi /// public virtual void AddKnownCertificates(ICollection certificates) { foreach (IX509Certificate certificate in certificates) { - knownCertificates.Put(((IX509Certificate)certificate).GetSubjectDN().ToString(), certificate); + String name = ((IX509Certificate)certificate).GetSubjectDN().ToString(); + IList certs = knownCertificates.ComputeIfAbsent(name, (k) => new List( + )); + certs.Add(certificate); } } @@ -301,5 +424,41 @@ private ICollection ProcessCertificatesFromAIA(String url) { return null; } } + + private static bool IsSignedBy(IX509Certificate certificate, IX509Certificate issuer) { + try { + certificate.Verify(issuer.GetPublicKey()); + return true; + } + catch (Exception) { + return false; + } + } + + private static bool IsSignedBy(IX509Crl crl, IX509Certificate issuer) { + try { + crl.Verify(issuer.GetPublicKey()); + return true; + } + catch (Exception) { + return false; + } + } + + private static IX509Certificate GetIssuerFromCertificateSet(IX509Certificate lastAddedCert, ICollection certs) { + if (certs != null) { + foreach (IX509Certificate cert in certs) { + if (IsSignedBy(lastAddedCert, cert)) { + return cert; + } + } + } + return null; + } + + private IList GetCrlIssuersFromKnownCertificates(IX509Crl crl) { + return knownCertificates.Get(crl.GetIssuerDN().ToString()); + } } } diff --git a/itext/itext.sign/itext/signatures/validation/CRLValidator.cs b/itext/itext.sign/itext/signatures/validation/CRLValidator.cs index 3bce29d1a3..681633ed90 100644 --- a/itext/itext.sign/itext/signatures/validation/CRLValidator.cs +++ b/itext/itext.sign/itext/signatures/validation/CRLValidator.cs @@ -299,47 +299,60 @@ private static int ComputeInterimReasonsMask(IIssuingDistributionPoint issuingDi private void VerifyCrlIntegrity(ValidationReport report, ValidationContext context, IX509Certificate certificate , IX509Crl crl, DateTime responseGenerationDate) { - IX509Certificate[] certs = null; + IX509Certificate[][] certificateSets = null; try { - certs = certificateRetriever.GetCrlIssuerCertificates(crl); + certificateSets = certificateRetriever.GetCrlIssuerCertificatesByName(crl); } catch (Exception e) { report.AddReportItem(new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_REQUEST_FAILED, e, ReportItem.ReportItemStatus .INDETERMINATE)); return; } - if (certs == null || certs.Length == 0) { + if (certificateSets == null || certificateSets.Length == 0) { report.AddReportItem(new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_NOT_FOUND, ReportItem.ReportItemStatus .INDETERMINATE)); return; } - if (JavaUtil.ArraysToEnumerable(certs).Any((c) => c.Equals(certificate))) { - report.AddReportItem(new CertificateReportItem(certificate, CRL_CHECK, CERTIFICATE_IN_ISSUER_CHAIN, ReportItem.ReportItemStatus - .INDETERMINATE)); - return; + ValidationReport[] candidateReports = new ValidationReport[certificateSets.Length]; + for (int i = 0; i < certificateSets.Length; i++) { + ValidationReport candidateReport = new ValidationReport(); + candidateReports[i] = candidateReport; + IX509Certificate[] certs = certificateSets[i]; + if (JavaUtil.ArraysAsList(certs).Contains(certificate)) { + candidateReport.AddReportItem(new CertificateReportItem(certificate, CRL_CHECK, CERTIFICATE_IN_ISSUER_CHAIN + , ReportItem.ReportItemStatus.INDETERMINATE)); + continue; + } + IX509Certificate crlIssuer = certs[0]; + IList crlIssuerRoots = GetRoots(crlIssuer); + IList subjectRoots = GetRoots(certificate); + if (!crlIssuerRoots.Any((cert) => subjectRoots.Contains(cert))) { + candidateReport.AddReportItem(new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_NO_COMMON_ROOT, + ReportItem.ReportItemStatus.INDETERMINATE)); + continue; + } + SafeCalling.OnExceptionLog(() => crl.Verify(crlIssuer.GetPublicKey()), candidateReport, (e) => new CertificateReportItem + (certificate, CRL_CHECK, CRL_INVALID, e, ReportItem.ReportItemStatus.INDETERMINATE)); + ValidationReport responderReport = new ValidationReport(); + SafeCalling.OnExceptionLog(() => builder.GetCertificateChainValidator().Validate(responderReport, context. + SetCertificateSource(CertificateSource.CRL_ISSUER), (IX509Certificate)crlIssuer, responseGenerationDate + ), candidateReport, (e) => new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_CHAIN_FAILED, + e, ReportItem.ReportItemStatus.INDETERMINATE)); + AddResponderValidationReport(candidateReport, responderReport); + if (candidateReport.GetValidationResult() == ValidationReport.ValidationResult.VALID) { + report.Merge(candidateReport); + return; + } } - IX509Certificate crlIssuer = certs[0]; - IX509Certificate crlIssuerRoot = GetRoot(crlIssuer); - IX509Certificate subjectRoot = GetRoot(certificate); - if (!crlIssuerRoot.Equals(subjectRoot)) { - report.AddReportItem(new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_NO_COMMON_ROOT, ReportItem.ReportItemStatus - .INDETERMINATE)); - return; + // if failed, add all logs + foreach (ValidationReport candidateReport in candidateReports) { + report.Merge(candidateReport); } - SafeCalling.OnExceptionLog(() => crl.Verify(crlIssuer.GetPublicKey()), report, (e) => new CertificateReportItem - (certificate, CRL_CHECK, CRL_INVALID, e, ReportItem.ReportItemStatus.INDETERMINATE)); - ValidationReport responderReport = new ValidationReport(); - SafeCalling.OnExceptionLog(() => builder.GetCertificateChainValidator().Validate(responderReport, context. - SetCertificateSource(CertificateSource.CRL_ISSUER), (IX509Certificate)crlIssuer, responseGenerationDate - ), report, (e) => new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_CHAIN_FAILED, e, ReportItem.ReportItemStatus - .INDETERMINATE)); - AddResponderValidationReport(report, responderReport); } - private IX509Certificate GetRoot(IX509Certificate cert) { - IX509Certificate[] chain = certificateRetriever.RetrieveMissingCertificates(new IX509Certificate[] { cert } - ); - return chain[chain.Length - 1]; + private IList GetRoots(IX509Certificate cert) { + IList chains = certificateRetriever.BuildCertificateChains((IX509Certificate)cert); + return chains.Select((certArray) => certArray[certArray.Length - 1]).ToList(); } private static void AddResponderValidationReport(ValidationReport report, ValidationReport responderReport diff --git a/itext/itext.sign/itext/signatures/validation/CertificateChainValidator.cs b/itext/itext.sign/itext/signatures/validation/CertificateChainValidator.cs index 0c43de5b6b..8c510611a5 100644 --- a/itext/itext.sign/itext/signatures/validation/CertificateChainValidator.cs +++ b/itext/itext.sign/itext/signatures/validation/CertificateChainValidator.cs @@ -305,37 +305,50 @@ private void ValidateRevocationData(ValidationReport report, ValidationContext c private void ValidateChain(ValidationReport result, ValidationContext context, IX509Certificate certificate , DateTime validationDate, int certificateChainSize) { - IX509Certificate issuerCertificate = null; + IList issuerCertificates; try { - issuerCertificate = (IX509Certificate)certificateRetriever.RetrieveIssuerCertificate(certificate); + issuerCertificates = certificateRetriever.RetrieveIssuerCertificate(certificate); } catch (Exception e) { result.AddReportItem(new CertificateReportItem(certificate, CERTIFICATE_CHECK, ISSUER_RETRIEVAL_FAILED, e, ReportItem.ReportItemStatus.INDETERMINATE)); return; } - if (issuerCertificate == null) { + if (issuerCertificates.IsEmpty()) { result.AddReportItem(new CertificateReportItem(certificate, CERTIFICATE_CHECK, MessageFormatUtil.Format(ISSUER_MISSING , certificate.GetSubjectDN()), ReportItem.ReportItemStatus.INDETERMINATE)); return; } - try { - certificate.Verify(issuerCertificate.GetPublicKey()); - } - catch (AbstractGeneralSecurityException e) { - result.AddReportItem(new CertificateReportItem(certificate, CERTIFICATE_CHECK, MessageFormatUtil.Format(ISSUER_CANNOT_BE_VERIFIED - , issuerCertificate.GetSubjectDN(), certificate.GetSubjectDN()), e, ReportItem.ReportItemStatus.INVALID - )); - return; + ValidationReport[] candidateReports = new ValidationReport[issuerCertificates.Count]; + for (int i = 0; i < issuerCertificates.Count; i++) { + candidateReports[i] = new ValidationReport(); + try { + certificate.Verify(issuerCertificates[i].GetPublicKey()); + } + catch (AbstractGeneralSecurityException e) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, CERTIFICATE_CHECK, MessageFormatUtil + .Format(ISSUER_CANNOT_BE_VERIFIED, issuerCertificates[i].GetSubjectDN(), certificate.GetSubjectDN()), + e, ReportItem.ReportItemStatus.INVALID)); + continue; + } + catch (Exception e) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, CERTIFICATE_CHECK, MessageFormatUtil + .Format(ISSUER_VERIFICATION_FAILED, issuerCertificates[i].GetSubjectDN(), certificate.GetSubjectDN()), + e, ReportItem.ReportItemStatus.INVALID)); + continue; + } + this.Validate(candidateReports[i], context.SetCertificateSource(CertificateSource.CERT_ISSUER), issuerCertificates + [i], validationDate, certificateChainSize + 1); + if (candidateReports[i].GetValidationResult() == ValidationReport.ValidationResult.VALID) { + // We found valid issuer, no need to try other ones. + result.Merge(candidateReports[i]); + return; + } } - catch (Exception e) { - result.AddReportItem(new CertificateReportItem(certificate, CERTIFICATE_CHECK, MessageFormatUtil.Format(ISSUER_VERIFICATION_FAILED - , issuerCertificate.GetSubjectDN(), certificate.GetSubjectDN()), e, ReportItem.ReportItemStatus.INVALID - )); - return; + // Valid issuer wasn't found, add all the reports + foreach (ValidationReport candidateReport in candidateReports) { + result.Merge(candidateReport); } - this.Validate(result, context.SetCertificateSource(CertificateSource.CERT_ISSUER), issuerCertificate, validationDate - , certificateChainSize + 1); } } } diff --git a/itext/itext.sign/itext/signatures/validation/OCSPValidator.cs b/itext/itext.sign/itext/signatures/validation/OCSPValidator.cs index 546b19dc1f..6a32859904 100644 --- a/itext/itext.sign/itext/signatures/validation/OCSPValidator.cs +++ b/itext/itext.sign/itext/signatures/validation/OCSPValidator.cs @@ -21,6 +21,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ using System; +using System.Collections.Generic; using iText.Bouncycastleconnector; using iText.Commons.Bouncycastle; using iText.Commons.Bouncycastle.Asn1; @@ -57,6 +58,10 @@ public class OCSPValidator { internal const String ISSUERS_DO_NOT_MATCH = "OCSP: Issuers don't match."; //\endcond +//\cond DO_NOT_DOCUMENT + internal const String ISSUER_MISSING = "Issuer certificate wasn't found."; +//\endcond + //\cond DO_NOT_DOCUMENT internal const String FRESHNESS_CHECK = "OCSP response is not fresh enough: " + "this update: {0}, validation date: {1}, freshness: {2}."; //\endcond @@ -74,11 +79,23 @@ public class OCSPValidator { internal const String OCSP_RESPONDER_NOT_VERIFIED = "OCSP response could not be verified: \" +\n" + " \" Unexpected exception occurred while validating responder certificate."; //\endcond +//\cond DO_NOT_DOCUMENT + internal const String OCSP_RESPONDER_DID_NOT_SIGN = "OCSP response could not be verified against this responder."; +//\endcond + //\cond DO_NOT_DOCUMENT internal const String OCSP_RESPONDER_TRUST_NOT_RETRIEVED = "OCSP response could not be verified: \" +\n" + " \"responder trust state could not be retrieved."; //\endcond +//\cond DO_NOT_DOCUMENT + internal const String OCSP_RESPONDER_TRUSTED = "Responder certificate is a trusted certificate."; +//\endcond + +//\cond DO_NOT_DOCUMENT + internal const String OCSP_RESPONDER_IS_CA = "Responder certificate is the CA certificate."; +//\endcond + //\cond DO_NOT_DOCUMENT internal const String OCSP_IS_NO_LONGER_VALID = "OCSP is no longer valid: {0} after {1}"; //\endcond @@ -88,11 +105,13 @@ public class OCSPValidator { //\endcond //\cond DO_NOT_DOCUMENT - internal const String UNABLE_TO_CHECK_IF_ISSUERS_MATCH = "OCSP response could not be verified: Unexpected exception occurred checking if issuers match."; + internal const String UNABLE_TO_CHECK_IF_ISSUERS_MATCH = "OCSP response could not be verified: Unexpected exception" + + " occurred checking if issuers match."; //\endcond //\cond DO_NOT_DOCUMENT - internal const String UNABLE_TO_RETRIEVE_ISSUER = "OCSP response could not be verified: Unexpected exception occurred while retrieving issuer"; + internal const String UNABLE_TO_RETRIEVE_ISSUER = "OCSP response could not be verified: Unexpected exception " + + "occurred while retrieving issuer"; //\endcond //\cond DO_NOT_DOCUMENT @@ -147,79 +166,100 @@ public virtual void Validate(ValidationReport report, ValidationContext context, .INDETERMINATE)); return; } - IX509Certificate issuerCert; + IList issuerCerts; try { - issuerCert = certificateRetriever.RetrieveIssuerCertificate(certificate); + issuerCerts = certificateRetriever.RetrieveIssuerCertificate(certificate); } catch (Exception e) { report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, UNABLE_TO_RETRIEVE_ISSUER, e, ReportItem.ReportItemStatus .INDETERMINATE)); return; } - // Check if the issuer of the certID and signCert matches, i.e. check that issuerNameHash and issuerKeyHash - // fields of the certID is the hash of the issuer's name and public key: - try { - if (!CertificateUtil.CheckIfIssuersMatch(singleResp.GetCertID(), (IX509Certificate)issuerCert)) { - report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, ISSUERS_DO_NOT_MATCH, ReportItem.ReportItemStatus - .INDETERMINATE)); - return; - } - } - catch (Exception e) { - report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, UNABLE_TO_CHECK_IF_ISSUERS_MATCH, - e, ReportItem.ReportItemStatus.INDETERMINATE)); - return; - } - // So, since the issuer name and serial number identify a unique certificate, we found the single response - // for the provided certificate. - TimeSpan freshness = properties.GetFreshness(localContext); - // Check that thisUpdate + freshness < validation. - if (DateTimeUtil.AddMillisToDate(singleResp.GetThisUpdate(), (long)freshness.TotalMilliseconds).Before(validationDate - )) { - report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format(FRESHNESS_CHECK - , singleResp.GetThisUpdate(), validationDate, freshness), ReportItem.ReportItemStatus.INDETERMINATE)); + if (issuerCerts.IsEmpty()) { + report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format(ISSUER_MISSING + , certificate.GetSubjectDN()), ReportItem.ReportItemStatus.INDETERMINATE)); return; } - // If nextUpdate is not set, the responder is indicating that newer revocation information - // is available all the time. - if (singleResp.GetNextUpdate() != TimestampConstants.UNDEFINED_TIMESTAMP_DATE && validationDate.After(singleResp - .GetNextUpdate())) { - report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format(OCSP_IS_NO_LONGER_VALID - , validationDate, singleResp.GetNextUpdate()), ReportItem.ReportItemStatus.INDETERMINATE)); - return; - } - // Check the status of the certificate: - ICertStatus status = singleResp.GetCertStatus(); - IRevokedCertStatus revokedStatus = BOUNCY_CASTLE_FACTORY.CreateRevokedStatus(status); - bool isStatusGood = BOUNCY_CASTLE_FACTORY.CreateCertificateStatus().GetGood().Equals(status); - // Check OCSP Archive Cutoff extension in case OCSP response was generated after the certificate is expired. - if (isStatusGood && certificate.GetNotAfter().Before(ocspResp.GetProducedAt())) { - DateTime startExpirationDate = GetArchiveCutoffExtension(ocspResp); - if (TimestampConstants.UNDEFINED_TIMESTAMP_DATE == startExpirationDate || certificate.GetNotAfter().Before - (startExpirationDate)) { - report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format(CERT_IS_EXPIRED - , certificate.GetNotAfter()), ReportItem.ReportItemStatus.INDETERMINATE)); - return; + ValidationReport[] candidateReports = new ValidationReport[issuerCerts.Count]; + for (int i = 0; i < issuerCerts.Count; i++) { + candidateReports[i] = new ValidationReport(); + // Check if the issuer of the certID and signCert matches, i.e. check that issuerNameHash and issuerKeyHash + // fields of the certID is the hash of the issuer's name and public key: + try { + if (!CertificateUtil.CheckIfIssuersMatch(singleResp.GetCertID(), issuerCerts[i])) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, ISSUERS_DO_NOT_MATCH, + ReportItem.ReportItemStatus.INDETERMINATE)); + continue; + } } - } - if (isStatusGood || (revokedStatus != null && validationDate.Before(revokedStatus.GetRevocationTime()))) { - // Check if the OCSP response is genuine. - VerifyOcspResponder(report, localContext, ocspResp, (IX509Certificate)issuerCert, responseGenerationDate); - if (!isStatusGood) { - report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format(SignLogMessageConstant - .VALID_CERTIFICATE_IS_REVOKED, revokedStatus.GetRevocationTime()), ReportItem.ReportItemStatus.INFO)); + catch (Exception e) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, UNABLE_TO_CHECK_IF_ISSUERS_MATCH + , e, ReportItem.ReportItemStatus.INDETERMINATE)); + continue; } - } - else { - if (revokedStatus != null) { - report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, CERT_IS_REVOKED, ReportItem.ReportItemStatus - .INVALID)); + // So, since the issuer name and serial number identify a unique certificate, we found the single response + // for the provided certificate. + TimeSpan freshness = properties.GetFreshness(localContext); + // Check that thisUpdate + freshness < validation. + if (DateTimeUtil.AddMillisToDate(singleResp.GetThisUpdate(), (long)freshness.TotalMilliseconds).Before(validationDate + )) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format + (FRESHNESS_CHECK, singleResp.GetThisUpdate(), validationDate, freshness), ReportItem.ReportItemStatus. + INDETERMINATE)); + continue; + } + // If nextUpdate is not set, the responder is indicating that newer revocation information + // is available all the time. + if (singleResp.GetNextUpdate() != TimestampConstants.UNDEFINED_TIMESTAMP_DATE && validationDate.After(singleResp + .GetNextUpdate())) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format + (OCSP_IS_NO_LONGER_VALID, validationDate, singleResp.GetNextUpdate()), ReportItem.ReportItemStatus.INDETERMINATE + )); + continue; + } + // Check the status of the certificate: + ICertStatus status = singleResp.GetCertStatus(); + IRevokedCertStatus revokedStatus = BOUNCY_CASTLE_FACTORY.CreateRevokedStatus(status); + bool isStatusGood = BOUNCY_CASTLE_FACTORY.CreateCertificateStatus().GetGood().Equals(status); + // Check OCSP Archive Cutoff extension in case OCSP response was generated after the certificate is expired. + if (isStatusGood && certificate.GetNotAfter().Before(ocspResp.GetProducedAt())) { + DateTime startExpirationDate = GetArchiveCutoffExtension(ocspResp); + if (TimestampConstants.UNDEFINED_TIMESTAMP_DATE == startExpirationDate || certificate.GetNotAfter().Before + (startExpirationDate)) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format + (CERT_IS_EXPIRED, certificate.GetNotAfter()), ReportItem.ReportItemStatus.INDETERMINATE)); + continue; + } + } + if (isStatusGood || (revokedStatus != null && validationDate.Before(revokedStatus.GetRevocationTime()))) { + // Check if the OCSP response is genuine. + VerifyOcspResponder(candidateReports[i], localContext, ocspResp, issuerCerts[i], responseGenerationDate); + if (!isStatusGood) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, MessageFormatUtil.Format + (SignLogMessageConstant.VALID_CERTIFICATE_IS_REVOKED, revokedStatus.GetRevocationTime()), ReportItem.ReportItemStatus + .INFO)); + } } else { - report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, CERT_STATUS_IS_UNKNOWN, ReportItem.ReportItemStatus - .INDETERMINATE)); + if (revokedStatus != null) { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, CERT_IS_REVOKED, ReportItem.ReportItemStatus + .INVALID)); + } + else { + candidateReports[i].AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, CERT_STATUS_IS_UNKNOWN + , ReportItem.ReportItemStatus.INDETERMINATE)); + } + } + if (candidateReports[i].GetValidationResult() == ValidationReport.ValidationResult.VALID) { + // We found valid issuer, no need to try other ones. + report.Merge(candidateReports[i]); + return; } } + // Valid issuer wasn't found, add all the reports + foreach (ValidationReport candidateReport in candidateReports) { + report.Merge(candidateReport); + } } /// Verifies if an OCSP response is genuine. @@ -239,90 +279,87 @@ public virtual void Validate(ValidationReport report, ValidationContext context, private void VerifyOcspResponder(ValidationReport report, ValidationContext context, IBasicOcspResponse ocspResp , IX509Certificate issuerCert, DateTime responseGenerationDate) { ValidationContext localContext = context.SetCertificateSource(CertificateSource.OCSP_ISSUER); - ValidationReport responderReport = new ValidationReport(); // OCSP response might be signed by the issuer certificate or // the Authorized OCSP responder certificate containing the id-kp-OCSPSigning extended key usage extension. - IX509Certificate responderCert = null; // First check if the issuer certificate signed the response since it is expected to be the most common case: + // the CA will already be validated by the chain validator if (CertificateUtil.IsSignatureValid(ocspResp, issuerCert)) { - responderCert = issuerCert; + report.AddReportItem(new CertificateReportItem(issuerCert, OCSP_CHECK, OCSP_RESPONDER_IS_CA, ReportItem.ReportItemStatus + .INFO)); + return; } // If the issuer certificate didn't sign the ocsp response, look for authorized ocsp responses // from the properties or from the certificate chain received with response. - if (responderCert == null) { - try { - responderCert = (IX509Certificate)certificateRetriever.RetrieveOCSPResponderCertificate(ocspResp); - } - catch (Exception e) { - report.AddReportItem(new CertificateReportItem(issuerCert, OCSP_CHECK, OCSP_RESPONDER_NOT_RETRIEVED, e, ReportItem.ReportItemStatus - .INDETERMINATE)); - return; - } - if (responderCert == null) { - report.AddReportItem(new CertificateReportItem(issuerCert, OCSP_CHECK, OCSP_COULD_NOT_BE_VERIFIED, ReportItem.ReportItemStatus - .INDETERMINATE)); - return; + ICollection candidates = SafeCalling.OnRuntimeExceptionLog(() => certificateRetriever.RetrieveOCSPResponderByNameCertificate + (ocspResp), JavaCollectionsUtil.EmptySet(), report, (e) => new CertificateReportItem + (issuerCert, OCSP_CHECK, OCSP_RESPONDER_NOT_RETRIEVED, e, ReportItem.ReportItemStatus.INDETERMINATE)); + if (candidates.IsEmpty()) { + report.AddReportItem(new CertificateReportItem(issuerCert, OCSP_CHECK, OCSP_COULD_NOT_BE_VERIFIED, ReportItem.ReportItemStatus + .INDETERMINATE)); + return; + } + ValidationReport[] candidateReports = new ValidationReport[candidates.Count]; + int reportIndex = 0; + foreach (IX509Certificate cert in candidates) { + IX509Certificate responderCert = (IX509Certificate)cert; + ValidationReport candidateReport = new ValidationReport(); + candidateReports[reportIndex++] = candidateReport; + // if the response was not signed by this candidate we can stop further processing + if (!CertificateUtil.IsSignatureValid(ocspResp, responderCert)) { + candidateReport.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, OCSP_RESPONDER_DID_NOT_SIGN + , ReportItem.ReportItemStatus.INDETERMINATE)); + continue; } - bool needsToBeSignedByIssuer = false; + // if the responder is trusted validation is successful try { - needsToBeSignedByIssuer = (!certificateRetriever.IsCertificateTrusted(responderCert) && !certificateRetriever - .GetTrustedCertificatesStore().IsCertificateTrustedForOcsp(responderCert)); + if (certificateRetriever.GetTrustedCertificatesStore().IsCertificateTrustedForOcsp(responderCert) || certificateRetriever + .GetTrustedCertificatesStore().IsCertificateGenerallyTrusted(responderCert)) { + candidateReport.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, OCSP_RESPONDER_TRUSTED, + ReportItem.ReportItemStatus.INFO)); + report.Merge(candidateReport); + return; + } } catch (Exception e) { report.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, OCSP_RESPONDER_TRUST_NOT_RETRIEVED , e, ReportItem.ReportItemStatus.INDETERMINATE)); - return; + continue; } - if (needsToBeSignedByIssuer) { - // RFC 6960 4.2.2.2. Authorized Responders: - // "Systems relying on OCSP responses MUST recognize a delegation certificate as being issued - // by the CA that issued the certificate in question only if the delegation certificate and the - // certificate being checked for revocation were signed by the same key." - // and "This certificate MUST be issued directly by the CA that is identified in the request". - try { - responderCert.Verify(issuerCert.GetPublicKey()); - } - catch (Exception e) { - report.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, INVALID_OCSP, e, ReportItem.ReportItemStatus - .INVALID)); - return; - } - // Validating of the ocsp signer's certificate (responderCert) described in the - // RFC6960 4.2.2.2.1. Revocation Checking of an Authorized Responder. - try { - builder.GetCertificateChainValidator().Validate(responderReport, localContext, responderCert, responseGenerationDate - ); - } - catch (Exception e) { - report.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, OCSP_RESPONDER_NOT_VERIFIED, e, - ReportItem.ReportItemStatus.INDETERMINATE)); - return; - } + // RFC 6960 4.2.2.2. Authorized Responders: + // "Systems relying on OCSP responses MUST recognize a delegation certificate as being issued + // by the CA that issued the certificate in question only if the delegation certificate and the + // certificate being checked for revocation were signed by the same key." + // and "This certificate MUST be issued directly by the CA that is identified in the request". + try { + responderCert.Verify(issuerCert.GetPublicKey()); } - else { - try { - builder.GetCertificateChainValidator().Validate(responderReport, localContext.SetCertificateSource(CertificateSource - .TRUSTED), responderCert, responseGenerationDate); - } - catch (Exception e) { - report.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, OCSP_RESPONDER_NOT_VERIFIED, e, - ReportItem.ReportItemStatus.INDETERMINATE)); - return; - } + catch (Exception e) { + candidateReport.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, INVALID_OCSP, e, ReportItem.ReportItemStatus + .INVALID)); + continue; } - } - else { + // Validating of the ocsp signer's certificate (responderCert) described in the + // RFC6960 4.2.2.2.1. Revocation Checking of an Authorized Responder. + ValidationReport responderReport = new ValidationReport(); try { - builder.GetCertificateChainValidator().Validate(responderReport, localContext.SetCertificateSource(CertificateSource - .CERT_ISSUER), responderCert, responseGenerationDate); + builder.GetCertificateChainValidator().Validate(responderReport, localContext, responderCert, responseGenerationDate + ); } catch (Exception e) { - report.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, OCSP_RESPONDER_NOT_VERIFIED, e, - ReportItem.ReportItemStatus.INDETERMINATE)); + candidateReport.AddReportItem(new CertificateReportItem(responderCert, OCSP_CHECK, OCSP_RESPONDER_NOT_VERIFIED + , e, ReportItem.ReportItemStatus.INDETERMINATE)); + continue; + } + AddResponderValidationReport(candidateReport, responderReport); + if (candidateReport.GetValidationResult() == ValidationReport.ValidationResult.VALID) { + AddResponderValidationReport(report, candidateReport); return; } } - AddResponderValidationReport(report, responderReport); + //if we get here, none of the candidates were successful + foreach (ValidationReport subReport in candidateReports) { + report.Merge(subReport); + } } private static void AddResponderValidationReport(ValidationReport report, ValidationReport responderReport diff --git a/itext/itext.sign/itext/signatures/validation/RevocationDataValidator.cs b/itext/itext.sign/itext/signatures/validation/RevocationDataValidator.cs index ce245731ea..11aed3377f 100644 --- a/itext/itext.sign/itext/signatures/validation/RevocationDataValidator.cs +++ b/itext/itext.sign/itext/signatures/validation/RevocationDataValidator.cs @@ -97,6 +97,10 @@ public class RevocationDataValidator { internal const String CRL_VALIDATOR_FAILURE = "Unexpected exception occurred in CRL validator."; //\endcond +//\cond DO_NOT_DOCUMENT + internal const String UNABLE_TO_RETRIEVE_REV_DATA_ONLINE = "Online revocation data wasn't generated: Unexpected exception occurred."; +//\endcond + private static readonly IBouncyCastleFactory BOUNCY_CASTLE_FACTORY = BouncyCastleFactoryCreator.GetFactory (); @@ -233,7 +237,13 @@ public virtual void Validate(ValidationReport report, ValidationContext context, >(); IList onlineOcspResponses = new List(); - TryToFetchRevInfoOnline(report, context, certificate, onlineCrlResponses, onlineOcspResponses); + try { + TryToFetchRevInfoOnline(report, context, certificate, onlineCrlResponses, onlineOcspResponses); + } + catch (Exception e) { + report.AddReportItem(new CertificateReportItem(certificate, REVOCATION_DATA_CHECK, UNABLE_TO_RETRIEVE_REV_DATA_ONLINE + , e, ReportItem.ReportItemStatus.INFO)); + } if (!onlineCrlResponses.IsEmpty() || !onlineOcspResponses.IsEmpty()) { // Merge the report excluding NO_REVOCATION_DATA message. foreach (ReportItem reportItem in revDataValidationReport.GetLogs()) { @@ -330,9 +340,9 @@ private void ValidateRevocationData(ValidationReport report, ValidationContext c report, ValidationContext context, IX509Certificate certificate) { IList ocspResponses = new List(); - IX509Certificate issuerCert; + IList issuerCerts; try { - issuerCert = (IX509Certificate)certificateRetriever.RetrieveIssuerCertificate(certificate); + issuerCerts = certificateRetriever.RetrieveIssuerCertificate(certificate); } catch (Exception e) { report.AddReportItem(new CertificateReportItem(certificate, REVOCATION_DATA_CHECK, ISSUER_RETRIEVAL_FAILED @@ -349,24 +359,26 @@ private void ValidateRevocationData(ValidationReport report, ValidationContext c } } else { - byte[] basicOcspRespBytes = null; - basicOcspRespBytes = SafeCalling.OnRuntimeExceptionLog(() => ocspClient.GetEncoded(certificate, issuerCert - , null), null, report, (e) => new CertificateReportItem(certificate, REVOCATION_DATA_CHECK, MessageFormatUtil - .Format(OCSP_CLIENT_FAILURE, ocspClient), e, ReportItem.ReportItemStatus.INFO)); - if (basicOcspRespBytes != null) { - try { - IBasicOcspResponse basicOCSPResp = BOUNCY_CASTLE_FACTORY.CreateBasicOCSPResponse(BOUNCY_CASTLE_FACTORY.CreateASN1Primitive - (basicOcspRespBytes)); - FillOcspResponses(ocspResponses, basicOCSPResp, DateTimeUtil.GetCurrentUtcTime(), TimeBasedContext.PRESENT - ); - } - catch (System.IO.IOException e) { - report.AddReportItem(new ReportItem(REVOCATION_DATA_CHECK, MessageFormatUtil.Format(CANNOT_PARSE_OCSP, ocspClient - ), e, ReportItem.ReportItemStatus.INFO)); - } - catch (Exception e) { - report.AddReportItem(new ReportItem(REVOCATION_DATA_CHECK, MessageFormatUtil.Format(CANNOT_PARSE_OCSP, ocspClient - ), e, ReportItem.ReportItemStatus.INFO)); + foreach (IX509Certificate issuerCert in issuerCerts) { + byte[] basicOcspRespBytes = null; + basicOcspRespBytes = SafeCalling.OnRuntimeExceptionLog(() => ocspClient.GetEncoded(certificate, issuerCert + , null), null, report, (e) => new CertificateReportItem(certificate, REVOCATION_DATA_CHECK, MessageFormatUtil + .Format(OCSP_CLIENT_FAILURE, ocspClient), e, ReportItem.ReportItemStatus.INFO)); + if (basicOcspRespBytes != null) { + try { + IBasicOcspResponse basicOCSPResp = BOUNCY_CASTLE_FACTORY.CreateBasicOCSPResponse(BOUNCY_CASTLE_FACTORY.CreateASN1Primitive + (basicOcspRespBytes)); + FillOcspResponses(ocspResponses, basicOCSPResp, DateTimeUtil.GetCurrentUtcTime(), TimeBasedContext.PRESENT + ); + } + catch (System.IO.IOException e) { + report.AddReportItem(new ReportItem(REVOCATION_DATA_CHECK, MessageFormatUtil.Format(CANNOT_PARSE_OCSP, ocspClient + ), e, ReportItem.ReportItemStatus.INFO)); + } + catch (Exception e) { + report.AddReportItem(new ReportItem(REVOCATION_DATA_CHECK, MessageFormatUtil.Format(CANNOT_PARSE_OCSP, ocspClient + ), e, ReportItem.ReportItemStatus.INFO)); + } } } } @@ -374,14 +386,16 @@ private void ValidateRevocationData(ValidationReport report, ValidationContext c SignatureValidationProperties.OnlineFetching onlineFetching = properties.GetRevocationOnlineFetching(context .SetValidatorContext(ValidatorContext.OCSP_VALIDATOR)); if (SignatureValidationProperties.OnlineFetching.ALWAYS_FETCH == onlineFetching) { - SafeCalling.OnRuntimeExceptionLog(() => { - IBasicOcspResponse basicOCSPResp = new OcspClientBouncyCastle().GetBasicOCSPResp(certificate, issuerCert, - null); - FillOcspResponses(ocspResponses, basicOCSPResp, DateTimeUtil.GetCurrentUtcTime(), TimeBasedContext.PRESENT - ); + foreach (IX509Certificate issuerCert in issuerCerts) { + SafeCalling.OnRuntimeExceptionLog(() => { + IBasicOcspResponse basicOCSPResp = new OcspClientBouncyCastle().GetBasicOCSPResp(certificate, issuerCert, + null); + FillOcspResponses(ocspResponses, basicOCSPResp, DateTimeUtil.GetCurrentUtcTime(), TimeBasedContext.PRESENT + ); + } + , report, (e) => new CertificateReportItem(certificate, REVOCATION_DATA_CHECK, MessageFormatUtil.Format(OCSP_CLIENT_FAILURE + , "OcspClientBouncyCastle"), e, ReportItem.ReportItemStatus.INDETERMINATE)); } - , report, (e) => new CertificateReportItem(certificate, REVOCATION_DATA_CHECK, MessageFormatUtil.Format(OCSP_CLIENT_FAILURE - , "OcspClientBouncyCastle"), e, ReportItem.ReportItemStatus.INDETERMINATE)); } return ocspResponses; } @@ -415,19 +429,21 @@ private void TryToFetchRevInfoOnline(ValidationReport report, ValidationContext SignatureValidationProperties.OnlineFetching ocspOnlineFetching = properties.GetRevocationOnlineFetching(context .SetValidatorContext(ValidatorContext.OCSP_VALIDATOR)); if (SignatureValidationProperties.OnlineFetching.FETCH_IF_NO_OTHER_DATA_AVAILABLE == ocspOnlineFetching) { - SafeCalling.OnRuntimeExceptionLog(() => { - IBasicOcspResponse basicOCSPResp = new OcspClientBouncyCastle().GetBasicOCSPResp(certificate, (IX509Certificate - )certificateRetriever.RetrieveIssuerCertificate(certificate), null); - IList ocspResponses = new List(); - FillOcspResponses(ocspResponses, basicOCSPResp, DateTimeUtil.GetCurrentUtcTime(), TimeBasedContext.PRESENT - ); - // Sort all the OCSP responses available based on the most recent revocation data. - onlineOcspResponses.AddAll(ocspResponses.Sorted((o1, o2) => o2.singleResp.GetThisUpdate().CompareTo(o1.singleResp - .GetThisUpdate())).ToList()); + foreach (IX509Certificate issuerCert in certificateRetriever.RetrieveIssuerCertificate(certificate)) { + SafeCalling.OnRuntimeExceptionLog(() => { + IBasicOcspResponse basicOCSPResp = new OcspClientBouncyCastle().GetBasicOCSPResp(certificate, issuerCert, + null); + IList ocspResponses = new List(); + FillOcspResponses(ocspResponses, basicOCSPResp, DateTimeUtil.GetCurrentUtcTime(), TimeBasedContext.PRESENT + ); + // Sort all the OCSP responses available based on the most recent revocation data. + onlineOcspResponses.AddAll(ocspResponses.Sorted((o1, o2) => o2.singleResp.GetThisUpdate().CompareTo(o1.singleResp + .GetThisUpdate())).ToList()); + } + , report, (e) => new CertificateReportItem(certificate, REVOCATION_DATA_CHECK, MessageFormatUtil.Format(OCSP_CLIENT_FAILURE + , "OcspClientBouncyCastle"), e, ReportItem.ReportItemStatus.INDETERMINATE)); } - , report, (e) => new CertificateReportItem(certificate, REVOCATION_DATA_CHECK, MessageFormatUtil.Format(OCSP_CLIENT_FAILURE - , "OcspClientBouncyCastle"), e, ReportItem.ReportItemStatus.INDETERMINATE)); } } diff --git a/itext/itext.sign/itext/signatures/validation/TrustedCertificatesStore.cs b/itext/itext.sign/itext/signatures/validation/TrustedCertificatesStore.cs index dffea35280..0117a37df5 100644 --- a/itext/itext.sign/itext/signatures/validation/TrustedCertificatesStore.cs +++ b/itext/itext.sign/itext/signatures/validation/TrustedCertificatesStore.cs @@ -23,25 +23,27 @@ You should have received a copy of the GNU Affero General Public License using System; using System.Collections.Generic; using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Utils; +using iText.Commons.Utils.Collections; namespace iText.Signatures.Validation { /// Trusted certificates storage class to be used to configure trusted certificates in a particular way. /// public class TrustedCertificatesStore { - private readonly IDictionary generallyTrustedCertificates = new Dictionary(); + private readonly IDictionary> generallyTrustedCertificates = new Dictionary + >(); - private readonly IDictionary ocspTrustedCertificates = new Dictionary(); + private readonly IDictionary> ocspTrustedCertificates = new Dictionary + >(); - private readonly IDictionary timestampTrustedCertificates = new Dictionary(); + private readonly IDictionary> timestampTrustedCertificates = new Dictionary + >(); - private readonly IDictionary crlTrustedCertificates = new Dictionary(); + private readonly IDictionary> crlTrustedCertificates = new Dictionary + >(); - private readonly IDictionary caTrustedCertificates = new Dictionary(); + private readonly IDictionary> caTrustedCertificates = new Dictionary + >(); /// Add collection of certificates to be trusted for any possible usage. /// @@ -53,7 +55,7 @@ public class TrustedCertificatesStore { /// public virtual void AddGenerallyTrustedCertificates(ICollection certificates) { foreach (IX509Certificate certificate in certificates) { - generallyTrustedCertificates.Put(((IX509Certificate)certificate).GetSubjectDN().ToString(), certificate); + AddCertificateToMap(certificate, generallyTrustedCertificates); } } @@ -72,7 +74,7 @@ public virtual void AddGenerallyTrustedCertificates(ICollection public virtual void AddOcspTrustedCertificates(ICollection certificates) { foreach (IX509Certificate certificate in certificates) { - ocspTrustedCertificates.Put(((IX509Certificate)certificate).GetSubjectDN().ToString(), certificate); + AddCertificateToMap(certificate, ocspTrustedCertificates); } } @@ -91,7 +93,7 @@ public virtual void AddOcspTrustedCertificates(ICollection cer /// public virtual void AddCrlTrustedCertificates(ICollection certificates) { foreach (IX509Certificate certificate in certificates) { - crlTrustedCertificates.Put(((IX509Certificate)certificate).GetSubjectDN().ToString(), certificate); + AddCertificateToMap(certificate, crlTrustedCertificates); } } @@ -110,7 +112,7 @@ public virtual void AddCrlTrustedCertificates(ICollection cert /// public virtual void AddTimestampTrustedCertificates(ICollection certificates) { foreach (IX509Certificate certificate in certificates) { - timestampTrustedCertificates.Put(((IX509Certificate)certificate).GetSubjectDN().ToString(), certificate); + AddCertificateToMap(certificate, timestampTrustedCertificates); } } @@ -128,7 +130,7 @@ public virtual void AddTimestampTrustedCertificates(ICollection public virtual void AddCATrustedCertificates(ICollection certificates) { foreach (IX509Certificate certificate in certificates) { - caTrustedCertificates.Put(((IX509Certificate)certificate).GetSubjectDN().ToString(), certificate); + AddCertificateToMap(certificate, caTrustedCertificates); } } @@ -146,7 +148,7 @@ public virtual void AddCATrustedCertificates(ICollection certi /// otherwise /// public virtual bool IsCertificateGenerallyTrusted(IX509Certificate certificate) { - return generallyTrustedCertificates.ContainsKey(((IX509Certificate)certificate).GetSubjectDN().ToString()); + return MapContainsCertificate(certificate, generallyTrustedCertificates); } /// Check if provided certificate is configured to be trusted for OCSP response generation. @@ -163,7 +165,7 @@ public virtual bool IsCertificateGenerallyTrusted(IX509Certificate certificate) /// otherwise /// public virtual bool IsCertificateTrustedForOcsp(IX509Certificate certificate) { - return ocspTrustedCertificates.ContainsKey(((IX509Certificate)certificate).GetSubjectDN().ToString()); + return MapContainsCertificate(certificate, ocspTrustedCertificates); } /// Check if provided certificate is configured to be trusted for CRL generation. @@ -180,7 +182,7 @@ public virtual bool IsCertificateTrustedForOcsp(IX509Certificate certificate) { /// otherwise /// public virtual bool IsCertificateTrustedForCrl(IX509Certificate certificate) { - return crlTrustedCertificates.ContainsKey(((IX509Certificate)certificate).GetSubjectDN().ToString()); + return MapContainsCertificate(certificate, crlTrustedCertificates); } /// Check if provided certificate is configured to be trusted for timestamp generation. @@ -197,7 +199,7 @@ public virtual bool IsCertificateTrustedForCrl(IX509Certificate certificate) { /// otherwise /// public virtual bool IsCertificateTrustedForTimestamp(IX509Certificate certificate) { - return timestampTrustedCertificates.ContainsKey(((IX509Certificate)certificate).GetSubjectDN().ToString()); + return MapContainsCertificate(certificate, timestampTrustedCertificates); } /// Check if provided certificate is configured to be trusted to be CA. @@ -214,10 +216,10 @@ public virtual bool IsCertificateTrustedForTimestamp(IX509Certificate certificat /// otherwise /// public virtual bool IsCertificateTrustedForCA(IX509Certificate certificate) { - return caTrustedCertificates.ContainsKey(((IX509Certificate)certificate).GetSubjectDN().ToString()); + return MapContainsCertificate(certificate, caTrustedCertificates); } - /// Get certificate, if any, which is trusted for any usage, which corresponds to the provided certificate name. + /// Get certificates, if any, which is trusted for any usage, which corresponds to the provided certificate name. /// /// /// @@ -225,16 +227,17 @@ public virtual bool IsCertificateTrustedForCA(IX509Certificate certificate) { /// certificate name /// /// - /// + /// set of /// - /// which corresponds to the provided certificate name + /// which correspond to the provided certificate name /// - public virtual IX509Certificate GetGenerallyTrustedCertificate(String certificateName) { - return generallyTrustedCertificates.Get(certificateName); + public virtual ICollection GetGenerallyTrustedCertificates(String certificateName) { + return generallyTrustedCertificates.GetOrDefault(certificateName, JavaCollectionsUtil.EmptySet()); } /// - /// Get certificate, if any, which is trusted for OCSP response generation, + /// Get certificates, if any, which is trusted for OCSP response generation, /// which corresponds to the provided certificate name. /// /// @@ -243,28 +246,32 @@ public virtual IX509Certificate GetGenerallyTrustedCertificate(String certificat /// certificate name /// /// - /// + /// set of /// - /// which corresponds to the provided certificate name + /// which correspond to the provided certificate name /// - public virtual IX509Certificate GetCertificateTrustedForOcsp(String certificateName) { - return ocspTrustedCertificates.Get(certificateName); + public virtual ICollection GetCertificatesTrustedForOcsp(String certificateName) { + return ocspTrustedCertificates.GetOrDefault(certificateName, JavaCollectionsUtil.EmptySet()); } - /// Get certificate, if any, which is trusted for CRL generation, which corresponds to the provided certificate name. - /// + /// + /// Get certificates, if any, which is trusted for CRL generation, + /// which corresponds to the provided certificate name. + /// /// /// /// /// certificate name /// /// - /// + /// set of /// - /// which corresponds to the provided certificate name + /// which correspond to the provided certificate name /// - public virtual IX509Certificate GetCertificateTrustedForCrl(String certificateName) { - return crlTrustedCertificates.Get(certificateName); + public virtual ICollection GetCertificatesTrustedForCrl(String certificateName) { + return crlTrustedCertificates.GetOrDefault(certificateName, JavaCollectionsUtil.EmptySet + ()); } /// @@ -277,55 +284,53 @@ public virtual IX509Certificate GetCertificateTrustedForCrl(String certificateNa /// certificate name /// /// - /// + /// set of /// - /// which corresponds to the provided certificate name + /// which correspond to the provided certificate name /// - public virtual IX509Certificate GetCertificateTrustedForTimestamp(String certificateName) { - return timestampTrustedCertificates.Get(certificateName); + public virtual ICollection GetCertificatesTrustedForTimestamp(String certificateName) { + return timestampTrustedCertificates.GetOrDefault(certificateName, JavaCollectionsUtil.EmptySet()); } - /// Get certificate, if any, which is trusted to be a CA, which corresponds to the provided certificate name. - /// + /// + /// Get certificates, if any, + /// which is trusted to be a CA, which corresponds to the provided certificate name. + /// /// /// /// /// certificate name /// /// - /// + /// set of /// - /// which corresponds to the provided certificate name + /// which correspond to the provided certificate name /// - public virtual IX509Certificate GetCertificateTrustedForCA(String certificateName) { - return caTrustedCertificates.Get(certificateName); + public virtual ICollection GetCertificatesTrustedForCA(String certificateName) { + return caTrustedCertificates.GetOrDefault(certificateName, JavaCollectionsUtil.EmptySet( + )); } - /// Get certificate, if any, which corresponds to the provided certificate name. + /// Get certificates, if any, which corresponds to the provided certificate name. /// /// /// /// certificate name /// /// - /// + /// set of /// - /// which corresponds to the provided certificate name + /// which correspond to the provided certificate name /// - public virtual IX509Certificate GetKnownCertificate(String certificateName) { - if (generallyTrustedCertificates.ContainsKey(certificateName)) { - return generallyTrustedCertificates.Get(certificateName); - } - if (ocspTrustedCertificates.ContainsKey(certificateName)) { - return ocspTrustedCertificates.Get(certificateName); - } - if (crlTrustedCertificates.ContainsKey(certificateName)) { - return crlTrustedCertificates.Get(certificateName); - } - if (timestampTrustedCertificates.ContainsKey(certificateName)) { - return timestampTrustedCertificates.Get(certificateName); - } - return caTrustedCertificates.Get(certificateName); + public virtual ICollection GetKnownCertificates(String certificateName) { + ICollection result = new HashSet(); + AddMatched(result, generallyTrustedCertificates, certificateName); + AddMatched(result, ocspTrustedCertificates, certificateName); + AddMatched(result, crlTrustedCertificates, certificateName); + AddMatched(result, timestampTrustedCertificates, certificateName); + AddMatched(result, caTrustedCertificates, certificateName); + return result; } /// Get all the certificates, which where provided to this storage as trusted certificate. @@ -337,13 +342,80 @@ public virtual IX509Certificate GetKnownCertificate(String certificateName) { /// instances /// public virtual ICollection GetAllTrustedCertificates() { - IList certificates = new List(); - certificates.AddAll(generallyTrustedCertificates.Values); - certificates.AddAll(ocspTrustedCertificates.Values); - certificates.AddAll(crlTrustedCertificates.Values); - certificates.AddAll(timestampTrustedCertificates.Values); - certificates.AddAll(caTrustedCertificates.Values); + ICollection certificates = new HashSet(); + foreach (ICollection set in generallyTrustedCertificates.Values) { + certificates.AddAll(set); + } + foreach (ICollection set in ocspTrustedCertificates.Values) { + certificates.AddAll(set); + } + foreach (ICollection set in crlTrustedCertificates.Values) { + certificates.AddAll(set); + } + foreach (ICollection set in timestampTrustedCertificates.Values) { + certificates.AddAll(set); + } + foreach (ICollection set in caTrustedCertificates.Values) { + certificates.AddAll(set); + } return certificates; } + + /// Get all the certificates having name as subject, which where provided to this storage as trusted certificate. + /// + /// the subject name value for which to retrieve all trusted certificate + /// + /// set of + /// + /// which correspond to the provided certificate name + /// + public virtual ICollection GetAllTrustedCertificates(String name) { + ICollection certificates = new HashSet(); + ICollection set = generallyTrustedCertificates.Get(name); + if (set != null) { + certificates.AddAll(set); + } + set = ocspTrustedCertificates.Get(name); + if (set != null) { + certificates.AddAll(set); + } + set = crlTrustedCertificates.Get(name); + if (set != null) { + certificates.AddAll(set); + } + set = timestampTrustedCertificates.Get(name); + if (set != null) { + certificates.AddAll(set); + } + set = caTrustedCertificates.Get(name); + if (set != null) { + certificates.AddAll(set); + } + return certificates; + } + + private static void AddCertificateToMap(IX509Certificate certificate, IDictionary> map) { + String name = ((IX509Certificate)certificate).GetSubjectDN().ToString(); + ICollection set = map.ComputeIfAbsent(name, (k) => new HashSet()); + set.Add(certificate); + } + + private static bool MapContainsCertificate(IX509Certificate certificate, IDictionary> map) { + ICollection set = map.Get(((IX509Certificate)certificate).GetSubjectDN().ToString()); + if (set == null) { + return false; + } + return set.Contains(certificate); + } + + private static void AddMatched(ICollection target, IDictionary> source, String certificateName) { + ICollection subset = source.Get(certificateName); + if (subset != null) { + target.AddAll(subset); + } + } } } diff --git a/port-hash b/port-hash index d583678927..dc88712c80 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -1156fe6f2e4a2ea0794e650746efc2cf312edf2f \ No newline at end of file +a6e77ae175f64fa2b5f619945eab96b1d23c371d