8000 Extends JsonWebSignature to support verification with X.509 certifica… · iamhwani/google-http-java-client@8a250ce · GitHub
[go: up one dir, main page]

Skip to content

Commit 8a250ce

Browse files
committed
Extends JsonWebSignature to support verification with X.509 certificates.
Extend JsonWebSignature to support verification with X.509 certificates; JsonWebSignature: add support for using the system default trust manager; Log content encoding at HttpRequest if there's one; Use BufferedOutputStream in GZipEncoding; Add error message to precondition check in setChunkSize; Expose the ability to customize the creation of HttpURLConnection instances for NetHttpTransport. https://codereview.appspot.com/187060043/#ps20001
1 parent 3e48f93 commit 8a250ce

File tree

14 files changed

+754
-24
lines changed

14 files changed

+754
-24
lines changed

google-http-client/src/main/java/com/google/api/client/http/GZipEncoding.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import com.google.api.client.util.StreamingContent;
1818

19-
import java.io.FilterOutputStream;
19+
import java.io.BufferedOutputStream;
2020
import java.io.IOException;
2121
import java.io.OutputStream;
2222
import java.util.zip.GZIPOutputStream;
@@ -35,7 +35,7 @@ public String getName() {
3535

3636
public void encode(StreamingContent content, OutputStream out) throws IOException {
3737
// must not close the underlying output stream
38-
OutputStream out2 = new FilterOutputStream(out) {
38+
OutputStream out2 = new BufferedOutputStream(out) {
3939
@Override
4040
public void close() throws IOException {
4141
// copy implementation of super.close(), except do not close the underlying output stream

google-http-client/src/main/java/com/google/api/client/http/HttpRequest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,13 @@ public HttpResponse execute() throws IOException {
926926
curlbuf.append(" -H '" + header + "'");
927927
}
928928
}
929+
if (contentEncoding != null) {
930+
String header = "Content-Encoding: " + contentEncoding;
931+
logbuf.append(header).append(StringUtils.LINE_SEPARATOR);
932+
if (curlbuf != null) {
933+
curlbuf.append(" -H '" + header + "'");
934+
}
935+
}
929936
if (contentLength >= 0) {
930937
String header = "Content-Length: " + contentLength;
931938
logbuf.append(header).append(StringUtils.LINE_SEPARATOR);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.google.api.client.http.javanet;
2+
3+
import java.io.IOException;
4+
import java.net.HttpURLConnection;
5+
import java.net.URL;
6+
7+
/**
8+
* Given a {@link URL} instance, produces an {@link HttpURLConnection}.
9+
*/
10+
public interface ConnectionFactory {
11+
12+
/**
13+
* Creates a new {@link HttpURLConnection} from the given {@code url}.
14+
*
15+
* @param url the URL to which the conneciton will be made
16+
* @return the created connection object, which will still be in the pre-connected state
17+
* @throws IOException if there was a problem producing the connection
18+
* @throws ClassCastException if the URL is not for an HTTP endpoint
19+
*/
20+
HttpURLConnection openConnection(URL url) throws IOException, ClassCastException;
21+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.google.api.client.http.javanet;
2+
3+
import java.io.IOException;
4+
import java.net.HttpURLConnection;
5+
import java.net.Proxy;
6+
import java.net.URL;
7+
8+
/**
9+
* Default implementation of {@link ConnectionFactory}, which simply attempts to open the connection
10+
* with an optional {@link Proxy}.
11+
*/
12+
public class DefaultConnectionFactory implements ConnectionFactory {
13+
14+
private final Proxy proxy;
15+
16+
public DefaultConnectionFactory() {
17+
this(null);
18+
}
19+
20+
/**
21+
* @param proxy HTTP proxy or {@code null} to use the proxy settings from <a
22+
* href="http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html">
23+
* system properties</a>
24+
*/
25+
public DefaultConnectionFactory(Proxy proxy) {
26+
this.proxy = proxy;
27+
}
28+
29+
@Override
30+
public HttpURLConnection openConnection(URL url) throws IOException {
31+
return (HttpURLConnection) (proxy == null ? url.openConnection() : url.openConnection(proxy));
32+
}
33+
}

google-http-client/src/main/java/com/google/api/client/http/javanet/NetHttpTransport.java

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import java.net.HttpURLConnection;
2727
import java.net.Proxy;
2828
import java.net.URL;
29-
import java.net.URLConnection;
3029
import java.security.GeneralSecurityException;
3130
import java.security.KeyStore;
3231
import java.security.cert.CertificateFactory;
@@ -77,12 +76,8 @@ public final class NetHttpTransport extends HttpTransport {
7776
Arrays.sort(SUPPORTED_METHODS);
7877
}
7978

80-
/**
81-
* HTTP proxy or {@code null} to use the proxy settings from <a
82-
* href="http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html">system
83-
* properties</a>.
84-
*/
85-
private final Proxy proxy;
79+
/** Factory to produce connections from {@link URL}s */
80+
private final ConnectionFactory connectionFactory;
8681

8782
/** SSL socket factory or {@code null} for the default. */
8883
private final SSLSocketFactory sslSocketFactory;
@@ -98,7 +93,7 @@ public final class NetHttpTransport extends HttpTransport {
9893
* </p>
9994
*/
10095
public NetHttpTransport() {
101-
this(null, null, null);
96+
this((ConnectionFactory) null, null, null);
10297
}
10398

10499
/**
@@ -110,7 +105,20 @@ public NetHttpTransport() {
110105
*/
111106
NetHttpTransport(
112107
Proxy proxy, SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier) {
113-
this.proxy = proxy;
108+
this(new DefaultConnectionFactory(proxy), sslSocketFactory, hostnameVerifier);
109+
}
110+
111+
/**
112+
* @param connectionFactory factory to produce connections from {@link URL}s; if {@code null} then
113+
* {@link DefaultConnectionFactory} is used
114+
* @param sslSocketFactory SSL socket factory or {@code null} for the default
115+
* @param hostnameVerifier host name verifier or {@code null} for the default
116+
* @since 1.20
117+
*/
118+
NetHttpTransport(ConnectionFactory connectionFactory,
119+
SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier) {
120+
this.connectionFactory =
121+
connectionFactory == null ? new DefaultConnectionFactory() : connectionFactory;
114122
this.sslSocketFactory = sslSocketFactory;
115123
this.hostnameVerifier = hostnameVerifier;
116124
}
@@ -125,8 +133,7 @@ protected NetHttpRequest buildRequest(String method, String url) throws IOExcept
125133
Preconditions.checkArgument(supportsMethod(method), "HTTP method %s not supported", method);
126134
// connection with proxy settings
127135
URL connUrl = new URL(url);
128-
URLConnection conn = proxy == null ? connUrl.openConnection() : connUrl.openConnection(proxy);
129-
HttpURLConnection connection = (HttpURLConnection) conn;
136+
HttpURLConnection connection = connectionFactory.openConnection(connUrl);
130137
connection.setRequestMethod(method);
131138
// SSL settings
132139
if (connection instanceof HttpsURLConnection) {
@@ -165,6 +172,12 @@ public static final class Builder {
165172
*/
166173
private Proxy proxy;
167174

175+
/**
176+
* {@link ConnectionFactory} or {@code null} to use a DefaultConnectionFactory. This value is
177+
* only used if proxy is unset.
178+
*/
179+
private ConnectionFactory connectionFactory;
180+
168181
/**
169182
* Sets the HTTP proxy or {@code null} to use the proxy settings from <a
170183
* href="http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html">system
@@ -183,6 +196,22 @@ public Builder setProxy(Proxy proxy) {
183196
return this;
184197
}
185198

199+
/**
200+
* Sets the {@link ConnectionFactory} or {@code null} to use a {@link DefaultConnectionFactory}.
201+
* <b>This value is ignored if the {@link #setProxy} has been called with a non-null value.</b>
202+
*
203+
* <p>
204+
* If you wish to use a {@link Proxy}, it should be included in your {@link ConnectionFactory}
205+
* implementation.
206+
* </p>
207+
*
208+
* @since 1.20
209+
*/
210+
public Builder setConnectionFactory(ConnectionFactory connectionFactory) {
211+
this.connectionFactory = connectionFactory;
212+
return this;
213+
}
214+
186215
/**
187216
* Sets the SSL socket factory based on root certificates in a Java KeyStore.
188217
*
@@ -285,7 +314,9 @@ public Builder setHostnameVerifier(HostnameVerifier hostnameVerifier) {
285314

286315
/** Returns a new instance of {@link NetHttpTransport} based on the options. */
287316
public NetHttpTransport build() {
288-
return new NetHttpTransport(proxy, sslSocketFactory, hostnameVerifier);
317+
return proxy == null
318+
? new NetHttpTransport(connectionFactory, sslSocketFactory, hostnameVerifier)
319+
: new NetHttpTransport(proxy, sslSocketFactory, hostnameVerifier);
289320
}
290321
}
291322
}

google-http-client/src/main/java/com/google/api/client/json/webtoken/JsonWebSignature.java

Lines changed: 141 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import com.google.api.client.json.JsonFactory;
1818
import com.google.api.client.util.Base64;
19+
import com.google.api.client.util.Beta;
1920
import com.google.api.client.util.Key;
2021
import com.google.api.client.util.Preconditions;
2122
import com.google.api.client.util.SecurityUtils;
@@ -24,11 +25,20 @@
2425
import java.io.ByteArrayInputStream;
2526
import java.io.IOException;
2627
import java.security.GeneralSecurityException;
28+
import java.security.KeyStore;
29+
import java.security.KeyStoreException;
30+
import java.security.NoSuchAlgorithmException;
2731
import java.security.PrivateKey;
2832
import java.security.PublicKey;
2933
import java.security.Signature;
34+
import java.security.cert.X509Certificate;
35+
import java.util.ArrayList;
3036
import java.util.List;
3137

38+
import javax.net.ssl.TrustManager;
39+
import javax.net.ssl.TrustManagerFactory;
40+
import javax.net.ssl.X509TrustManager;
41+
3242
/**
3343
* <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-11">JSON Web Signature
3444
* (JWS)</a>.
@@ -128,9 +138,11 @@ public static class Header extends JsonWebToken.Header {
128138
* X.509 certificate chain header parameter contains the X.509 public key certificate or
129139
* certificate chain corresponding to the key used to digitally sign the JWS or {@code null} for
130140
* none.
141+
*
142+
* @since 1.19.1.
131143
*/
132144
@Key("x5c")
133-
private String x509Certificate;
145+
private List<String> x509Certificates;
134146

135147
/**
136148
* Array listing the header parameter names that define extensions that are used in the JWS
@@ -283,13 +295,50 @@ public Header setX509Thumbprint(String x509Thumbprint) {
283295
return this;
284296
}
285297

298+
/**
299+
* Returns the X.509 certificate chain header parameter contains the X.509 public key
300+
* certificate or corresponding to the key used to digitally sign the JWS or {@code null} for
301+
* none.
302+
*
303+
* <p>@deprecated Since release 1.19.1, replaced by {@link #getX509Certificates()}.
304+
*/
305+
@Deprecated
306+
public final String getX509Certificate() {
307+
if (x509Certificates == null || x509Certificates.isEmpty()) {
308+
return null;
309+
}
310+
return x509Certificates.get(0);
311+
}
312+
286313
/**
287314
* Returns the X.509 certificate chain header parameter contains the X.509 public key
288315
* certificate or certificate chain corresponding to the key used to digitally sign the JWS or
289316
* {@code null} for none.
317+
*
318+
* @since 1.19.1.
290319
*/
291-
public final String getX509Certificate() {
292-
return x509Certificate;
320+
public final List<String> getX509Certificates() {
321+
return x509Certificates;
322+
}
323+
324+
/**
325+
* Sets the X.509 certificate chain header parameter contains the X.509 public key certificate
326+
* corresponding to the key used to digitally sign the JWS or {@code null} for none.
327+
*
328+
* <p>
329+
* Overriding is only supported for the purpose of calling the super implementation and changing
330+
* the return type, but nothing else.
331+
* </p>
332+
*
333+
* <p>@deprecated Since release 1.19.1, replaced by
334+
* {@link #setX509Certificates(List x509Certificates)}.
335+
*/
336+
@Deprecated
337+
public Header setX509Certificate(String x509Certificate) {
338+
ArrayList<String> x509Certificates = new ArrayList<String>();
339+
x509Certificates.add(x509Certificate);
340+
this.x509Certificates = x509Certificates;
341+
return this;
293342
}
294343

295344
/**
@@ -301,9 +350,11 @@ public final String getX509Certificate() {
301350
* Overriding is only supported for the purpose of calling the super implementation and changing
302351
* the return type, but nothing else.
303352
* </p>
353+
*
354+
* @since 1.19.1.
304355
*/
305-
public Header setX509Certificate(String x509Certificate) {
306-
this.x509Certificate = x509Certificate;
356+
public Header setX509Certificates(List<String> x509Certificates) {
357+
this.x509Certificates = x509Certificates;
307358
return this;
308359
}
309360

@@ -372,6 +423,91 @@ public final boolean verifySignature(PublicKey publicKey) throws GeneralSecurity
372423
return SecurityUtils.verify(signatureAlg, publicKey, signatureBytes, signedContentBytes);
373424
}
374425

426+
/**
427+
* {@link Beta} <br/>
428+
* Verifies the signature of the content using the certificate chain embedded in the signature.
429+
*
430+
* <p>
431+
* Currently only {@code "RS256"} algorithm is verified, but others may be added in the future.
432+
* For any other algorithm it returns {@code null}.
433+
* </p>
434+
*
435+
* <p>
436+
* The leaf certificate of the certificate chain must be an SSL server certificate.
437+
* </p>
438+
*
439+
* @param trustManager Trust manager used to verify the X509 certificate chain embedded in this
440+
* message.
441+
* @return The signature certificate if the signature could be verified, null otherwise.
442+
* @throws GeneralSecurityException
443+
* @since 1.19.1.
444+
*/
445+
@Beta
446+
public final X509Certificate verifySignature(X509TrustManager trustManager)
447+
throws GeneralSecurityException {
448+
List<String> x509Certificates = getHeader().getX509Certificates();
449+
if (x509Certificates == null || x509Certificates.isEmpty()) {
450+
return null;
451+
}
452+
String algorithm = getHeader().getAlgorithm();
453+
Signature signatureAlg = null;
454+
if ("RS256".equals(algorithm)) {
455+
signatureAlg = SecurityUtils.getSha256WithRsaSignatureAlgorithm();
456+
} else {
457+
return null;
458+
}
459+
return SecurityUtils.verify(signatureAlg, trustManager, x509Certificates, signatureBytes,
460+
signedContentBytes);
461+
}
462+
463+
/**
464+
* {@link Beta} <br/>
465+
* Verifies the signature of the content using the certificate chain embedded in the signature.
466+
*
467+
* <p>
468+
* Currently only {@code "RS256"} algorithm is verified, but others may be added in the future.
469+
* For any other algorithm it returns {@code null}.
470+
* </p>
471+
*
472+
* <p>
473+
* The certificate chain is verified using the system default trust manager.
474+
* </p>
475+
*
476+
* <p>
477+
* The leaf certificate of the certificate chain must be an SSL server certificate.
478+
* </p>
479+
*
480+
* @return The signature certificate if the signature could be verified, null otherwise.
481+
* @throws GeneralSecurityException
482+
* @since 1.19.1.
483+
*/
484+
@Beta
485+
public final X509Certificate verifySignature() throws GeneralSecurityException {
486+
X509TrustManager trustManager = getDefaultX509TrustManager();
487+
if (trustManager == null) {
488+
return null;
489+
}
490+
return verifySignature(trustManager);
491+
}
492+
493+
private static X509TrustManager getDefaultX509TrustManager() {
494+
try {
495+
TrustManagerFactory factory =
496+
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
497+
factory.init((KeyStore) null);
498+
for (TrustManager manager : factory.getTrustManagers()) {
499+
if (manager instanceof X509TrustManager) {
500+
return (X509TrustManager) manager;
501+
}
502+
}
503+
return null;
504+
} catch (NoSuchAlgorithmException e) {
505+
return null;
506+
} catch (KeyStoreException e) {
507+
return null;
508+
}
509+
}
510+
375511
/** Returns the modifiable array of bytes of the signature. */
376512
public final byte[] getSignatureBytes() {
377513
return signatureBytes;

0 commit comments

Comments
 (0)
0