55import com .venafi .vcert .sdk .certificate .ChainOption ;
66import com .venafi .vcert .sdk .certificate .DataFormat ;
77import com .venafi .vcert .sdk .certificate .PEMCollection ;
8+ import com .venafi .vcert .sdk .connectors .ConnectorException .KeyStoreUnzipedFilesBytesSizeExceeded ;
9+ import com .venafi .vcert .sdk .connectors .ConnectorException .KeyStoreZipCompressionRatioExceeded ;
10+ import com .venafi .vcert .sdk .connectors .ConnectorException .KeyStoreZipEntriesExceeded ;
811import com .venafi .vcert .sdk .connectors .ConnectorException .PolicyMatchException ;
912import com .venafi .vcert .sdk .connectors .cloud .CloudConnector .CsrAttributes ;
1013import com .venafi .vcert .sdk .connectors .cloud .CloudConnector .SubjectAlternativeNamesByType ;
2225
2326import static org .apache .commons .lang3 .StringUtils .isNotBlank ;
2427
28+ import java .io .IOException ;
2529import java .io .InputStream ;
26- import java .io .InputStreamReader ;
30+ import java .io .StringReader ;
2731import java .security .PrivateKey ;
2832import java .util .*;
2933import java .util .regex .Pattern ;
@@ -390,7 +394,7 @@ public static String getVaaSChainOption(ChainOption chainOption) {
390394 }
391395 }
392396
393- public static PEMCollection getPEMCollectionFromKeyStoreAsStream (InputStream keyStoreAsInputStream , ChainOption chainOption , String keyPassword , DataFormat dataFormat ) throws VCertException {
397+ public static PEMCollection getPEMCollectionFromKeyStoreAsStream (InputStream keyStoreAsInputStream , String certId , ChainOption chainOption , String keyPassword , DataFormat dataFormat ) throws VCertException {
394398 String certificateAsPem = null ;
395399
396400 String pemFileSuffix = null ;
@@ -402,19 +406,44 @@ public static PEMCollection getPEMCollectionFromKeyStoreAsStream(InputStream key
402406 PrivateKey privateKey = null ;
403407
404408 try (ZipInputStream zis = new ZipInputStream (keyStoreAsInputStream )) {
405-
409+ //The next constants are in order to be on safe about of the zip bomb attacks
410+ final int MAX_ENTRIES = 6 ;//The expected number of files in the zip returned by the call to
411+ //the API "POST /outagedetection/v1/certificates/{id}/keystore"
412+ final int MAX_UNZIPED_FILES_SIZE = 1000000 ; //1 MB
413+
414+ int entriesCount = 0 ;
415+ int unzipedAcumulatedSize = 0 ;
416+
406417 ZipEntry zipEntry ;
407418 while ((zipEntry = zis .getNextEntry ())!= null ) {
419+
420+ entriesCount ++;
421+
422+ //ZIP Bomb Attack validation
423+ //If the number of entries is major that the expected max number of entries
424+ if (entriesCount > MAX_ENTRIES )
425+ throw new KeyStoreZipEntriesExceeded (certId , MAX_ENTRIES );
426+
427+ String zipEntryContent = readZipEntry (zipEntry , zis , certId );
428+
408429 String fileName = zipEntry .getName ();
409430 if (fileName .endsWith (".key" )) {
410431 //Getting the PrivateKey in PKCS8 and decrypting it
411- PEMParser pemParser = new PEMParser (new InputStreamReader ( zis ));
432+ PEMParser pemParser = new PEMParser (new StringReader ( zipEntryContent ));
412433 privateKey = PEMCollection .decryptPKCS8PrivateKey (pemParser , keyPassword );
413434 } else {
414435 if (fileName .endsWith (pemFileSuffix )) {
415- certificateAsPem = new String ( zis . readAllBytes ()) ;
436+ certificateAsPem = zipEntryContent ;
416437 }
417438 }
439+
440+ unzipedAcumulatedSize += zipEntryContent .getBytes ().length ;
441+
442+ //ZIP Bomb Attack validation
443+ //If the sum of the number of bytes of the unzipped files is major that the expected
444+ //maximum number of bytes.
445+ if (unzipedAcumulatedSize > MAX_UNZIPED_FILES_SIZE )
446+ throw new KeyStoreUnzipedFilesBytesSizeExceeded (certId , MAX_UNZIPED_FILES_SIZE );
418447 }
419448 } catch (Exception e ) {
420449 throw new VCertException (e );
@@ -427,6 +456,32 @@ public static PEMCollection getPEMCollectionFromKeyStoreAsStream(InputStream key
427456 keyPassword ,
428457 dataFormat );
429458 }
459+
460+ private static String readZipEntry (ZipEntry zipEntry , ZipInputStream zis , String certId ) throws VCertException , IOException {
461+
462+ int totalSizeEntry = 0 ;
463+
464+ final int MAX_RATIO = 3 ;//It's expected that the compression ratio should't be more than 3
465+
466+ StringBuilder s = new StringBuilder ();
467+ byte [] buffer = new byte [1024 ];
468+ int nBytes = 0 ;
469+ while ((nBytes = zis .read (buffer , 0 , 1024 )) >= 0 ) {
470+ s .append (new String (buffer , 0 , nBytes ));
471+
472+ //ZIP Bomb Attack validation
473+ //If the compression ratio of the current unzipped file is major that the expected
474+ // max ratio
475+ totalSizeEntry += nBytes ;
476+ double compressionRatio = totalSizeEntry / zipEntry .getCompressedSize ();
477+ if (compressionRatio > MAX_RATIO ) {
478+ // ratio between compressed and uncompressed data is highly suspicious, looks like a Zip Bomb Attack
479+ throw new KeyStoreZipCompressionRatioExceeded (certId , zipEntry .getName (), MAX_RATIO );
480+ }
481+ }
482+
483+ return s .toString ();
484+ }
430485
431486 @ Data
432487 @ AllArgsConstructor
0 commit comments