16
16
17
17
import com .google .api .client .json .JsonFactory ;
18
18
import com .google .api .client .util .Base64 ;
19
+ import com .google .api .client .util .Beta ;
19
20
import com .google .api .client .util .Key ;
20
21
import com .google .api .client .util .Preconditions ;
21
22
import com .google .api .client .util .SecurityUtils ;
24
25
import java .io .ByteArrayInputStream ;
25
26
import java .io .IOException ;
26
27
import java .security .GeneralSecurityException ;
28
+ import java .security .KeyStore ;
29
+ import java .security .KeyStoreException ;
30
+ import java .security .NoSuchAlgorithmException ;
27
31
import java .security .PrivateKey ;
28
32
import java .security .PublicKey ;
29
33
import java .security .Signature ;
34
+ import java .security .cert .X509Certificate ;
35
+ import java .util .ArrayList ;
30
36
import java .util .List ;
31
37
38
<
F438
span class="diff-text-marker">+import javax .net .ssl .TrustManager ;
39
+ import javax .net .ssl .TrustManagerFactory ;
40
+ import javax .net .ssl .X509TrustManager ;
41
+
32
42
/**
33
43
* <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-11">JSON Web Signature
34
44
* (JWS)</a>.
@@ -128,9 +138,11 @@ public static class Header extends JsonWebToken.Header {
128
138
* X.509 certificate chain header parameter contains the X.509 public key certificate or
129
139
* certificate chain corresponding to the key used to digitally sign the JWS or {@code null} for
130
140
* none.
141
+ *
142
+ * @since 1.19.1.
131
143
*/
132
144
@ Key ("x5c" )
133
- private String x509Certificate ;
145
+ private List < String > x509Certificates ;
134
146
135
147
/**
136
148
* Array listing the header parameter names that define extensions that are used in the JWS
@@ -283,13 +295,50 @@ public Header setX509Thumbprint(String x509Thumbprint) {
283
295
return this ;
284
296
}
285
297
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
+
286
313
/**
287
314
* Returns the X.509 certificate chain header parameter contains the X.509 public key
288
315
* certificate or certificate chain corresponding to the key used to digitally sign the JWS or
289
316
* {@code null} for none.
317
+ *
318
+ * @since 1.19.1.
290
319
*/
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 ;
293
342
}
294
343
295
344
/**
@@ -301,9 +350,11 @@ public final String getX509Certificate() {
301
350
* Overriding is only supported for the purpose of calling the super implementation and changing
302
351
* the return type, but nothing else.
303
352
* </p>
353
+ *
354
+ * @since 1.19.1.
304
355
*/
305
- public Header setX509Certificate ( String x509Certificate ) {
306
- this .x509Certificate = x509Certificate ;
356
+ public Header setX509Certificates ( List < String > x509Certificates ) {
357
+ this .x509Certificates = x509Certificates ;
307
358
return this ;
308
359
}
309
360
@@ -372,6 +423,91 @@ public final boolean verifySignature(PublicKey publicKey) throws GeneralSecurity
372
423
return SecurityUtils .verify (signatureAlg , publicKey , signatureBytes , signedContentBytes );
373
424
}
374
425
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
+
375
511
/** Returns the modifiable array of bytes of the signature. */
376
512
public final byte [] getSignatureBytes () {
377
513
return signatureBytes ;
0 commit comments