25
25
package com .cloudbees .jenkins .plugins .bitbucket .impl .credentials ;
26
26
27
27
import com .cloudbees .jenkins .plugins .bitbucket .api .BitbucketAuthenticator ;
28
- import com .cloudbees .jenkins .plugins .bitbucket .api .BitbucketException ;
29
- import com .cloudbees .jenkins .plugins .bitbucket .impl .client .BitbucketTlsSocketStrategy ;
30
28
import com .cloudbees .plugins .credentials .common .StandardCertificateCredentials ;
31
29
import hudson .util .Secret ;
32
- import java .security .KeyManagementException ;
30
+ import java .net .URI ;
31
+ import java .net .URISyntaxException ;
33
32
import java .security .KeyStore ;
34
- import java .security .KeyStoreException ;
35
- import java .security .NoSuchAlgorithmException ;
36
- import java .security .UnrecoverableKeyException ;
37
- import javax .net .ssl .SSLContext ;
38
- import org .apache .hc .client5 .http .protocol .HttpClientContext ;
33
+ import java .util .ArrayList ;
34
+ import java .util .List ;
35
+ import java .util .Map ;
36
+ import java .util .logging .Logger ;
37
+ import javax .net .ssl .X509ExtendedKeyManager ;
38
+ import nl .altindag .ssl .SSLFactory ;
39
+ import nl .altindag .ssl .keymanager .AggregatedX509ExtendedKeyManager ;
40
+ import nl .altindag .ssl .keymanager .DummyX509ExtendedKeyManager ;
41
+ import nl .altindag .ssl .keymanager .HotSwappableX509ExtendedKeyManager ;
42
+ import nl .altindag .ssl .keymanager .LoggingX509ExtendedKeyManager ;
43
+ import nl .altindag .ssl .keymanager .X509KeyManagerWrapper ;
44
+ import nl .altindag .ssl .util .KeyManagerUtils ;
45
+ import nl .altindag .ssl .util .KeyStoreUtils ;
39
46
import org .apache .hc .core5 .http .HttpHost ;
40
- import org .apache . hc . core5 . ssl . SSLContextBuilder ;
41
- import org .apache . hc . core5 . ssl . SSLContexts ;
47
+ import org .kohsuke . accmod . Restricted ;
48
+ import org .kohsuke . accmod . restrictions . NoExternalUse ;
42
49
43
50
/**
44
51
* Authenticates against Bitbucket using a TLS client certificate
45
52
*/
46
53
public class BitbucketClientCertificateAuthenticator implements BitbucketAuthenticator {
54
+ private static final Logger logger = Logger .getLogger (BitbucketClientCertificateAuthenticator .class .getName ());
55
+
47
56
private final String credentialsId ;
48
57
private final KeyStore keyStore ;
49
58
private final Secret password ;
@@ -55,23 +64,64 @@ public BitbucketClientCertificateAuthenticator(StandardCertificateCredentials cr
55
64
}
56
65
57
66
/**
58
- * Sets the SSLContext for the builder to one that will connect with the selected certificate.
59
- * @param context The client builder context
67
+ * Sets the SSLContext for the builder to one that will connect with the
68
+ * selected certificate.
69
+ *
70
+ * @param sslFactory The client SSL context configured in the connection
71
+ * pool
60
72
* @param host the target host name
61
73
*/
62
- @ Override
63
- public void configureContext (HttpClientContext context , HttpHost host ) {
64
- try {
65
- context .setAttribute (BitbucketTlsSocketStrategy .SOCKET_FACTORY_REGISTRY , buildSSLContext ()); // override SSL registry for this context
66
- } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException | KeyManagementException e ) {
67
- throw new BitbucketException ("Failed to set up SSL context from provided client certificate" , e );
68
- }
74
+ @ Restricted (NoExternalUse .class )
75
+ public synchronized /*required to avoid combine the same identity material twice */ void configureContext (SSLFactory sslFactory , HttpHost host ) {
76
+ sslFactory .getKeyManager ().ifPresent (baseKeyManager -> {
77
+ List <X509ExtendedKeyManager > keyManagers = new ArrayList <>();
78
+
79
+ AggregatedX509ExtendedKeyManager aggregate = unwrap (baseKeyManager );
80
+ Map <String , List <URI >> routes = aggregate .getIdentityRoute ();
81
+
82
+ for (String alias : KeyStoreUtils .getAliases (keyStore )) {
83
+ // check if given route has been already added to the SSL context
84
+ if (!routes .containsKey (alias )) {
85
+ try {
86
+ URI hostURI = new URI (host .toURI ());
87
+ routes .put (alias , new ArrayList <>(List .of (hostURI )));
88
+ } catch (URISyntaxException e ) {
89
+ logger .severe ("Invalid host " + host );
90
+ }
91
+ keyManagers .add (KeyManagerUtils .createKeyManager (keyStore , Secret .toString (password ).toCharArray ()));
92
+ logger .info (() -> "Add new identity material (keyStore) " + alias + " to the SSLContext." );
93
+ }
94
+ }
95
+
96
+ if (!keyManagers .isEmpty ()) {
97
+ // create an aggregate keyManager with new identity material plus existing contributed
98
+ // from other configured client certificate client
99
+ X509ExtendedKeyManager combined = KeyManagerUtils .keyManagerBuilder ()
100
+ .withKeyManagers (aggregate .getInnerKeyManagers ())
101
+ .withKeyManagers (keyManagers )
102
+ .withIdentityRoute (routes )
103
+ .build ();
104
+ // swap identity materials and reuse existing http client
105
+ KeyManagerUtils .swapKeyManager (baseKeyManager , combined );
106
+ }
107
+ });
69
108
}
70
109
71
- private SSLContext buildSSLContext () throws NoSuchAlgorithmException , KeyStoreException , UnrecoverableKeyException , KeyManagementException {
72
- SSLContextBuilder contextBuilder = SSLContexts .custom ();
73
- contextBuilder .loadKeyMaterial (keyStore , Secret .toString (password ).toCharArray ());
74
- return contextBuilder .build ();
110
+
111
+ private AggregatedX509ExtendedKeyManager unwrap (X509ExtendedKeyManager keyManager ) {
112
+ if (keyManager instanceof AggregatedX509ExtendedKeyManager aggregate ) {
113
+ return aggregate ;
114
+ } else if (keyManager instanceof HotSwappableX509ExtendedKeyManager swappable ) {
115
+ return unwrap (swappable .getInnerKeyManager ());
116
+ } else if (keyManager instanceof LoggingX509ExtendedKeyManager logger ) {
117
+ return unwrap (logger .getInnerKeyManager ());
118
+ } else if (keyManager instanceof X509KeyManagerWrapper wrapper ) {
119
+ return unwrap ((X509ExtendedKeyManager ) wrapper .getInnerKeyManager ());
120
+ } else if (keyManager instanceof DummyX509ExtendedKeyManager ) {
121
+ return new AggregatedX509ExtendedKeyManager (new ArrayList <>());
122
+ } else {
123
+ return new AggregatedX509ExtendedKeyManager (List .of (keyManager ));
124
+ }
75
125
}
76
126
77
127
@ Override
0 commit comments