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,8 @@ 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 The JCA provider to use during request signing. May be null, in which case the default
129+ * provider will be used.
125130 */
126131 ServiceAccountCredentials (
127132 String clientId ,
@@ -133,7 +138,8 @@ public class ServiceAccountCredentials extends GoogleCredentials
133138 URI tokenServerUri ,
134139 String serviceAccountUser ,
135140 String projectId ,
136- String quotaProjectId ) {
141+ String quotaProjectId ,
142+ Provider signingProvider ) {
137143 this .clientId = clientId ;
138144 this .clientEmail = Preconditions .checkNotNull (clientEmail );
139145 this .privateKey = Preconditions .checkNotNull (privateKey );
@@ -143,6 +149,7 @@ public class ServiceAccountCredentials extends GoogleCredentials
143149 firstNonNull (
144150 transportFactory ,
145151 getFromServiceLoader (HttpTransportFactory .class , OAuth2Utils .HTTP_TRANSPORT_FACTORY ));
152+ this .signingProvider = signingProvider ;
146153 this .transportFactoryClassName = this .transportFactory .getClass ().getName ();
147154 this .tokenServerUri = (tokenServerUri == null ) ? OAuth2Utils .TOKEN_SERVER_URI : tokenServerUri ;
148155 this .serviceAccountUser = serviceAccountUser ;
@@ -324,7 +331,8 @@ static ServiceAccountCredentials fromPkcs8(
324331 tokenServerUri ,
325332 serviceAccountUser ,
326333 projectId ,
327- quotaProject );
334+ quotaProject ,
335+ null );
328336 }
329337
330338 /** Helper to convert from a PKCS#8 String to an RSA private key */
@@ -512,7 +520,8 @@ public GoogleCredentials createScoped(Collection<String> newScopes) {
512520 tokenServerUri ,
513521 serviceAccountUser ,
514522 projectId ,
515- quotaProjectId );
523+ quotaProjectId ,
524+ null );
516525 }
517526
518527 @ Override
@@ -527,7 +536,8 @@ public GoogleCredentials createDelegated(String user) {
527536 tokenServerUri ,
528537 user ,
529538 projectId ,
530- quotaProjectId );
539+ quotaProjectId ,
540+ null );
531541 }
532542
533543 public final String getClientId () {
@@ -570,7 +580,9 @@ public String getAccount() {
570580 @ Override
571581 public byte [] sign (byte [] toSign ) {
572582 try {
573- Signature signer = Signature .getInstance (OAuth2Utils .SIGNATURE_ALGORITHM );
583+ Signature signer = signingProvider == null
584+ ? Signature .getInstance (OAuth2Utils .SIGNATURE_ALGORITHM )
585+ : Signature .getInstance (OAuth2Utils .SIGNATURE_ALGORITHM , signingProvider );
574586 signer .initSign (getPrivateKey ());
575587 signer .update (toSign );
576588 return signer .sign ();
@@ -647,6 +659,19 @@ public boolean equals(Object obj) {
647659 && Objects .equals (this .quotaProjectId , other .quotaProjectId );
648660 }
649661
662+ private String signJsonWebSignature (JsonFactory jsonFactory , JsonWebSignature .Header header , JsonWebToken .Payload payload ) throws IOException {
663+ String signedContentString = Base64 .encodeBase64URLSafeString (jsonFactory .toByteArray (header )) + "." + Base64 .encodeBase64URLSafeString (jsonFactory .toByteArray (payload ));
664+ byte [] signedContentBytes = StringUtils .getBytesUtf8 (signedContentString );
665+ try {
666+ byte [] signature = this .sign (signedContentBytes );
667+ return signedContentString + "." + Base64 .encodeBase64URLSafeString (signature );
668+
669+ } catch (SigningException e ) {
670+ throw new IOException (
671+ "Error signing service account access token request with private key." , e );
672+ }
673+ }
674+
650675 String createAssertion (JsonFactory jsonFactory , long currentTime , String audience )
651676 throws IOException {
652677 JsonWebSignature .Header header = new JsonWebSignature .Header ();
@@ -667,14 +692,8 @@ String createAssertion(JsonFactory jsonFactory, long currentTime, String audienc
667692 payload .setAudience (audience );
668693 }
669694
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 ;
695+ String jsonWebSignature = signJsonWebSignature (jsonFactory , header , payload );
696+ return jsonWebSignature ;
678697 }
679698
680699 @ VisibleForTesting
@@ -698,16 +717,10 @@ String createAssertionForIdToken(
698717 payload .setAudience (audience );
699718 }
700719
701- try {
702- payload .set ("target_audience" , targetAudience );
720+ payload .set ("target_audience" , targetAudience );
703721
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- }
722+ String assertion = signJsonWebSignature (jsonFactory , header , payload );
723+ return assertion ;
711724 }
712725
713726 @ SuppressWarnings ("unused" )
@@ -742,6 +755,7 @@ public static class Builder extends GoogleCredentials.Builder {
742755 private Collection <String > scopes ;
743756 private HttpTransportFactory transportFactory ;
744757 private String quotaProjectId ;
758+ private Provider signatureProvider ;
745759
746760 protected Builder () {}
747761
@@ -756,6 +770,7 @@ protected Builder(ServiceAccountCredentials credentials) {
756770 this .serviceAccountUser = credentials .serviceAccountUser ;
757771 this .projectId = credentials .projectId ;
758772 this .quotaProjectId = credentials .quotaProjectId ;
773+ this .signatureProvider = credentials .signingProvider ;
759774 }
760775
761776 public Builder setClientId (String clientId ) {
@@ -808,6 +823,11 @@ public Builder setQuotaProjectId(String quotaProjectId) {
808823 return this ;
809824 }
810825
826+ public Builder setSignatureProvider (Provider signatureProvider ) {
827+ this .signatureProvider = signatureProvider ;
828+ return this ;
829+ }
830+
811831 public String getClientId () {
812832 return clientId ;
813833 }
@@ -848,6 +868,10 @@ public String getQuotaProjectId() {
848868 return quotaProjectId ;
849869 }
850870
871+ public Provider getSignatureProvider () {
872+ return signatureProvider ;
873+ }
874+
851875 public ServiceAccountCredentials build () {
852876 return new ServiceAccountCredentials (
853877 clientId ,
@@ -859,7 +883,8 @@ public ServiceAccountCredentials build() {
859883 tokenServerUri ,
860884 serviceAccountUser ,
861885 projectId ,
862- quotaProjectId );
886+ quotaProjectId ,
887+ signatureProvider );
863888 }
864889 }
865890}
0 commit comments