Repository: netty/netty
Issue: Fallback to PemReader fails when BouncyCastlePemReader encounters an unsupported type

While I'm not completely certain this is a common scenario, we observed this failure on upgrading to Netty `4.1.80` and when using a custom bundle for a private key.

From a quick glance, it looks like it should be resolved by ensuring that the input stream passed to the `BouncyCastlePemReader` is a copy, and processed independently of the original stream processed by `PemReader`

### Netty version
4.1.80 w/ tcnative 2.0.54

### JVM version (e.g. `java -version`)
openjdk version "17.0.4" 2022-07-19 LTS

### Expected behavior
The PR https://github.com/netty/netty/pull/12670 introduced a change that enforces eager parsing by `BouncyCastlePemReader` when the classpath includes the `BouncyCastleProvider`

The intended behavior should be the fallback to `PemReader` kicking in when the key type does not match one of the supported variants.

### Actual behavior

In the case where there is a `BouncyCastleProvider` on the classpath, but the key type is of a format that is not supported by `BouncyCastlePemReader`, the **input stream has already been read**.

This renders the fallback to trigger, but fails decoding. 

```
Caused by: java.lang.IllegalArgumentException: Input stream does not contain valid private key.
        at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:416)
        at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:341)
        at io.netty.handler.ssl.SslContextBuilder.forServer(SslContextBuilder.java:84)
....
Caused by: java.security.KeyException: could not find a PKCS #8 private key in input stream (see https://netty.io/wiki/sslcontextbuilder-and-private-key.html for more information)
        at io.netty.handler.ssl.PemReader.keyNotFoundException(PemReader.java:156)
        at io.netty.handler.ssl.PemReader.readPrivateKey(PemReader.java:137)
        at io.netty.handler.ssl.SslContext.toPrivateKey(SslContext.java:1152)
        at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:414)
```
### Steps to reproduce
Use a key with a format not supported  by the `BouncyCastlePemReader`, and trigger the fallback logic.

Issue comments:
Thanks for the report. This should be fixed by https://github.com/netty/netty/pull/12746
Thank you for the quick fix!


Pull request review comments:

Patch that resolved the issue:
diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java
index efb8e36b4b8..dc898915da7 100644
--- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java
+++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java
@@ -28,6 +28,7 @@
 import io.netty.util.DefaultAttributeMap;
 import io.netty.util.internal.EmptyArrays;
 
+import java.io.BufferedInputStream;
 import java.security.Provider;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
@@ -1143,10 +1144,17 @@ protected static PrivateKey toPrivateKey(InputStream keyInputStream, String keyP
 
         // try BC first, if this fail fallback to original key extraction process
         if (BouncyCastlePemReader.isAvailable()) {
+            if (!keyInputStream.markSupported()) {
+                // We need an input stream that supports resetting, in case BouncyCastle fails to read.
+                keyInputStream = new BufferedInputStream(keyInputStream);
+            }
+            keyInputStream.mark(1048576); // Be able to reset up to 1 MiB of data.
             PrivateKey pk = BouncyCastlePemReader.getPrivateKey(keyInputStream, keyPassword);
             if (pk != null) {
                 return pk;
             }
+            // BouncyCastle could not read the key. Reset the input stream in case the input position changed.
+            keyInputStream.reset();
         }
 
         return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyInputStream), keyPassword);


Test patch: