4646import com .google .api .client .json .JsonObjectParser ;
4747import com .google .api .client .json .webtoken .JsonWebSignature ;
4848import com .google .api .client .json .webtoken .JsonWebToken ;
49+ import com .google .api .client .util .Base64 ;
4950import com .google .api .client .util .ExponentialBackOff ;
5051import com .google .api .client .util .GenericData ;
5152import com .google .api .client .util .Joiner ;
5253import com .google .api .client .util .PemReader ;
5354import com .google .api .client .util .PemReader .Section ;
5455import com .google .api .client .util .Preconditions ;
5556import com .google .api .client .util .SecurityUtils ;
57+ import com .google .api .client .util .StringUtils ;
5658import com .google .auth .ServiceAccountSigner ;
5759import com .google .auth .http .HttpTransportFactory ;
5860import com .google .common .annotations .Beta ;
6668import java .io .StringReader ;
6769import java .net .URI ;
6870import java .net .URISyntaxException ;
69- import java .security .GeneralSecurityException ;
7071import java .security .InvalidKeyException ;
7172import java .security .KeyFactory ;
7273import java .security .NoSuchAlgorithmException ;
7374import java .security .PrivateKey ;
75+ import java .security .Provider ;
7476import java .security .Signature ;
7577import java .security .SignatureException ;
7678import java .security .spec .InvalidKeySpecException ;
@@ -103,6 +105,7 @@ public class ServiceAccountCredentials extends GoogleCredentials
103105 private final URI tokenServerUri ;
104106 private final Collection <String > scopes ;
105107 private final String quotaProjectId ;
108+ private final Provider signingProvider ;
106109
107110 private transient HttpTransportFactory transportFactory ;
108111
@@ -122,6 +125,7 @@ public class ServiceAccountCredentials extends GoogleCredentials
122125 * authority to the service account.
123126 * @param projectId the project used for billing
124127 * @param quotaProjectId The project used for quota and billing purposes. May be null.
128+ * @param signingProvider Explicitly set the JCA provider to use during request signing. May be null.
125129 */
126130 ServiceAccountCredentials (
127131 String clientId ,
@@ -133,7 +137,8 @@ public class ServiceAccountCredentials extends GoogleCredentials
133137 URI tokenServerUri ,
134138 String serviceAccountUser ,
135139 String projectId ,
136- String quotaProjectId ) {
140+ String quotaProjectId ,
141+ Provider signingProvider ) {
137142 this .clientId = clientId ;
138143 this .clientEmail = Preconditions .checkNotNull (clientEmail );
139144 this .privateKey = Preconditions .checkNotNull (privateKey );
@@ -143,6 +148,7 @@ public class ServiceAccountCredentials extends GoogleCredentials
143148 firstNonNull (
144149 transportFactory ,
145150 getFromServiceLoader (HttpTransportFactory .class , OAuth2Utils .HTTP_TRANSPORT_FACTORY ));
151+ this .signingProvider = signingProvider ;
146152 this .transportFactoryClassName = this .transportFactory .getClass ().getName ();
147153 this .tokenServerUri = (tokenServerUri == null ) ? OAuth2Utils .TOKEN_SERVER_URI : tokenServerUri ;
148154 this .serviceAccountUser = serviceAccountUser ;
@@ -324,7 +330,8 @@ static ServiceAccountCredentials fromPkcs8(
324330 tokenServerUri ,
325331 serviceAccountUser ,
326332 projectId ,
327- quotaProject );
333+ quotaProject ,
334+ null );
328335 }
329336
330337 /** Helper to convert from a PKCS#8 String to an RSA private key */
@@ -512,7 +519,8 @@ public GoogleCredentials createScoped(Collection<String> newScopes) {
512519 tokenServerUri ,
513520 serviceAccountUser ,
514521 projectId ,
515- quotaProjectId );
522+ quotaProjectId ,
523+ null );
516524 }
517525
518526 @ Override
@@ -527,7 +535,8 @@ public GoogleCredentials createDelegated(String user) {
527535 tokenServerUri ,
528536 user ,
529537 projectId ,
530- quotaProjectId );
538+ quotaProjectId ,
539+ null );
531540 }
532541
533542 public final String getClientId () {
@@ -570,7 +579,9 @@ public String getAccount() {
570579 @ Override
571580 public byte [] sign (byte [] toSign ) {
572581 try {
573- Signature signer = Signature .getInstance (OAuth2Utils .SIGNATURE_ALGORITHM );
582+ Signature signer = signingProvider == null
583+ ? Signature .getInstance (OAuth2Utils .SIGNATURE_ALGORITHM )
584+ : Signature .getInstance (OAuth2Utils .SIGNATURE_ALGORITHM , signingProvider );
574585 signer .initSign (getPrivateKey ());
575586 signer .update (toSign );
576587 return signer .sign ();
@@ -647,6 +658,19 @@ public boolean equals(Object obj) {
647658 && Objects .equals (this .quotaProjectId , other .quotaProjectId );
648659 }
649660
661+ private String signJsonWebSignature (JsonFactory jsonFactory , JsonWebSignature .Header header , JsonWebToken .Payload payload ) throws IOException {
662+ String signedContentString = Base64 .encodeBase64URLSafeString (jsonFactory .toByteArray (header )) + "." + Base64 .encodeBase64URLSafeString (jsonFactory .toByteArray (payload ));
663+ byte [] signedContentBytes = StringUtils .getBytesUtf8 (signedContentString );
664+ try {
665+ byte [] signature = this .sign (signedContentBytes );
666+ return signedContentString + "." + Base64 .encodeBase64URLSafeString (signature );
667+
668+ } catch (Exception e ) {
669+ throw new IOException (
670+ "Error signing service account access token request with private key." , e );
671+ }
672+ }
673+
650674 String createAssertion (JsonFactory jsonFactory , long currentTime , String audience )
651675 throws IOException {
652676 JsonWebSignature .Header header = new JsonWebSignature .Header ();
@@ -667,14 +691,8 @@ String createAssertion(JsonFactory jsonFactory, long currentTime, String audienc
667691 payload .setAudience (audience );
668692 }
669693
670- String assertion ;
671- try {
672- assertion = JsonWebSignature .signUsingRsaSha256 (privateKey , jsonFactory , header , payload );
673- } catch (GeneralSecurityException e ) {
674- throw new IOException (
675- "Error signing service account access token request with private key." , e );
676- }
677- return assertion ;
694+ String jsonWebSignature = signJsonWebSignature (jsonFactory , header , payload );
695+ return jsonWebSignature ;
678696 }
679697
680698 @ VisibleForTesting
@@ -698,16 +716,10 @@ String createAssertionForIdToken(
698716 payload .setAudience (audience );
699717 }
700718
701- try {
702- payload .set ("target_audience" , targetAudience );
719+ payload .set ("target_audience" , targetAudience );
703720
704- String assertion =
705- JsonWebSignature .signUsingRsaSha256 (privateKey , jsonFactory , header , payload );
706- return assertion ;
707- } catch (GeneralSecurityException e ) {
708- throw new IOException (
709- "Error signing service account access token request with private key." , e );
710- }
721+ String assertion = signJsonWebSignature (jsonFactory , header , payload );
722+ return assertion ;
711723 }
712724
713725 @ SuppressWarnings ("unused" )
@@ -742,6 +754,7 @@ public static class Builder extends GoogleCredentials.Builder {
742754 private Collection <String > scopes ;
743755 private HttpTransportFactory transportFactory ;
744756 private String quotaProjectId ;
757+ private Provider signatureProvider ;
745758
746759 protected Builder () {}
747760
@@ -756,6 +769,7 @@ protected Builder(ServiceAccountCredentials credentials) {
756769 this .serviceAccountUser = credentials .serviceAccountUser ;
757770 this .projectId = credentials .projectId ;
758771 this .quotaProjectId = credentials .quotaProjectId ;
772+ this .signatureProvider = credentials .signingProvider ;
759773 }
760774
761775 public Builder setClientId (String clientId ) {
@@ -808,6 +822,11 @@ public Builder setQuotaProjectId(String quotaProjectId) {
808822 return this ;
809823 }
810824
825+ public Builder setSignatureProvider (Provider signatureProvider ) {
826+ this .signatureProvider = signatureProvider ;
827+ return this ;
828+ }
829+
811830 public String getClientId () {
812831 return clientId ;
813832 }
@@ -848,6 +867,10 @@ public String getQuotaProjectId() {
848867 return quotaProjectId ;
849868 }
850869
870+ public Provider getSignatureProvider () {
871+ return signatureProvider ;
872+ }
873+
851874 public ServiceAccountCredentials build () {
852875 return new ServiceAccountCredentials (
853876 clientId ,
@@ -859,7 +882,8 @@ public ServiceAccountCredentials build() {
859882 tokenServerUri ,
860883 serviceAccountUser ,
861884 projectId ,
862- quotaProjectId );
885+ quotaProjectId ,
886+ signatureProvider );
863887 }
864888 }
865889}
0 commit comments