3737
3838import javax .net .ssl .X509ExtendedTrustManager ;
3939
40+ import java .nio .charset .StandardCharsets ;
4041import java .nio .file .Files ;
4142import java .nio .file .NoSuchFileException ;
4243import java .nio .file .Path ;
@@ -69,11 +70,29 @@ public void testBuildTrustConfigFromMultiplePemFiles() throws Exception {
6970 }
7071
7172 public void testBadFileFormatFails () throws Exception {
72- final Path ca = createTempFile ("ca" , ".crt" );
73- Files .write (ca , generateRandomByteArrayOfLength (128 ), StandardOpenOption .APPEND );
74- final PemTrustConfig trustConfig = new PemTrustConfig (Collections .singletonList (ca ));
75- assertThat (trustConfig .getDependentFiles (), Matchers .containsInAnyOrder (ca ));
76- assertFailedToParse (trustConfig , ca );
73+ {
74+ final Path ca = createTempFile ("ca" , ".crt" );
75+ Files .write (ca , "This is definitely not a PEM certificate" .getBytes (StandardCharsets .UTF_8 ), StandardOpenOption .CREATE );
76+ final PemTrustConfig trustConfig = new PemTrustConfig (Collections .singletonList (ca ));
77+ assertThat (trustConfig .getDependentFiles (), Matchers .containsInAnyOrder (ca ));
78+ assertFailedToParse (trustConfig , ca );
79+ }
80+
81+ {
82+ final Path ca = createTempFile ("ca" , ".crt" );
83+ Files .write (ca , generateInvalidPemBytes (), StandardOpenOption .CREATE );
84+ final PemTrustConfig trustConfig = new PemTrustConfig (Collections .singletonList (ca ));
85+ assertThat (trustConfig .getDependentFiles (), Matchers .containsInAnyOrder (ca ));
86+ assertCannotCreateTrust (trustConfig , ca );
87+ }
88+
89+ { // test DER-encoded sequence
90+ final Path ca = createTempFile ("ca" , ".crt" );
91+ Files .write (ca , generateInvalidDerEncodedPemBytes (), StandardOpenOption .CREATE );
92+ final PemTrustConfig trustConfig = new PemTrustConfig (Collections .singletonList (ca ));
93+ assertThat (trustConfig .getDependentFiles (), Matchers .containsInAnyOrder (ca ));
94+ assertCannotCreateTrust (trustConfig , ca );
95+ }
7796 }
7897
7998 public void testEmptyFileFails () throws Exception {
@@ -119,8 +138,8 @@ public void testTrustConfigReloadsFileContents() throws Exception {
119138 Files .delete (ca1 );
120139 assertFileNotFound (trustConfig , ca1 );
121140
122- Files .write (ca1 , generateRandomByteArrayOfLength ( 128 ), StandardOpenOption .CREATE );
123- assertFailedToParse (trustConfig , ca1 );
141+ Files .write (ca1 , generateInvalidPemBytes ( ), StandardOpenOption .CREATE );
142+ assertCannotCreateTrust (trustConfig , ca1 );
124143 }
125144
126145 private void assertCertificateChain (PemTrustConfig trustConfig , String ... caNames ) {
@@ -134,13 +153,23 @@ private void assertCertificateChain(PemTrustConfig trustConfig, String... caName
134153 assertThat (issuerNames , Matchers .containsInAnyOrder (caNames ));
135154 }
136155
156+ // The parser returns an empty collection when no valid sequence is found,
157+ // but our implementation requires an exception to be thrown in this case
137158 private void assertFailedToParse (PemTrustConfig trustConfig , Path file ) {
138159 final SslConfigException exception = expectThrows (SslConfigException .class , trustConfig ::createTrustManager );
139160 logger .info ("failure" , exception );
140161 assertThat (exception .getMessage (), Matchers .containsString (file .toAbsolutePath ().toString ()));
141162 assertThat (exception .getMessage (), Matchers .containsString ("Failed to parse any certificate from" ));
142163 }
143164
165+ // The parser encounters malformed PEM data
166+ private void assertCannotCreateTrust (PemTrustConfig trustConfig , Path file ) {
167+ final SslConfigException exception = expectThrows (SslConfigException .class , trustConfig ::createTrustManager );
168+ logger .info ("failure" , exception );
169+ assertThat (exception .getMessage (), Matchers .containsString (file .toAbsolutePath ().toString ()));
170+ assertThat (exception .getMessage (), Matchers .containsString ("cannot create trust using PEM certificates" ));
171+ }
172+
144173 private void assertFileNotFound (PemTrustConfig trustConfig , Path file ) {
145174 final SslConfigException exception = expectThrows (SslConfigException .class , trustConfig ::createTrustManager );
146175 assertThat (exception .getMessage (), Matchers .containsString ("files do not exist" ));
@@ -149,23 +178,15 @@ private void assertFileNotFound(PemTrustConfig trustConfig, Path file) {
149178 assertThat (exception .getCause (), Matchers .instanceOf (NoSuchFileException .class ));
150179 }
151180
152- private byte [] generateRandomByteArrayOfLength (int length ) {
153- byte [] bytes = randomByteArrayOfLength (length );
154- /*
155- * If the bytes represent DER encoded value indicating ASN.1 SEQUENCE followed by length byte if it is zero then while trying to
156- * parse PKCS7 block from the encoded stream, it failed parsing the content type. The DerInputStream.getSequence() method in this
157- * case returns an empty DerValue array but ContentType does not check the length of array before accessing the array resulting in a
158- * ArrayIndexOutOfBoundsException. This check ensures that when we create random stream of bytes we do not create ASN.1 SEQUENCE
159- * followed by zero length which fails the test intermittently.
160- */
161- while (checkRandomGeneratedBytesRepresentZeroLengthDerSequenceCausingArrayIndexOutOfBound (bytes )) {
162- bytes = randomByteArrayOfLength (length );
163- }
164- return bytes ;
181+ private byte [] generateInvalidPemBytes () {
182+ String invalidPem = "-----BEGIN CERTIFICATE-----\n INVALID_CONTENT\n -----END CERTIFICATE-----" ;
183+ return invalidPem .getBytes (StandardCharsets .UTF_8 );
165184 }
166185
167- private static boolean checkRandomGeneratedBytesRepresentZeroLengthDerSequenceCausingArrayIndexOutOfBound (byte [] bytes ) {
168- // Tag value indicating an ASN.1 "SEQUENCE". Reference: sun.security.util.DerValue.tag_Sequence = 0x30
169- return bytes [0 ] == 0x30 && bytes [1 ] == 0x00 ;
186+ private byte [] generateInvalidDerEncodedPemBytes () {
187+ byte [] shortFormZeroLength = { 0x30 , 0x00 };
188+ byte [] longFormZeroLength = { 0x30 , (byte ) 0x81 , 0x00 };
189+ byte [] indefiniteForm = { 0x30 , (byte ) 0x80 , 0x01 , 0x02 , 0x00 , 0x00 };
190+ return randomFrom (shortFormZeroLength , longFormZeroLength , indefiniteForm );
170191 }
171192}
0 commit comments