2525import java .io .IOException ;
2626import java .io .InputStream ;
2727import java .io .Serializable ;
28+ import java .lang .reflect .InvocationTargetException ;
2829import java .lang .reflect .Method ;
30+ import java .security .InvalidKeyException ;
31+ import java .security .NoSuchAlgorithmException ;
2932import java .security .PrivateKey ;
33+ import java .security .Signature ;
34+ import java .security .SignatureException ;
3035import java .util .Collection ;
3136import java .util .Objects ;
3237
3540 */
3641public abstract class AuthCredentials implements Restorable <AuthCredentials > {
3742
38- private static class AppEngineAuthCredentials extends AuthCredentials {
43+ /**
44+ * Represents built-in credentials when running in Google App Engine.
45+ */
46+ public static class AppEngineAuthCredentials extends AuthCredentials
47+ implements ServiceAccountSigner {
3948
4049 private static final AuthCredentials INSTANCE = new AppEngineAuthCredentials ();
4150 private static final AppEngineAuthCredentialsState STATE = new AppEngineAuthCredentialsState ();
4251
43- private static class AppEngineCredentials extends GoogleCredentials {
52+ private AppEngineCredentials credentials ;
53+
54+ private static class AppEngineCredentials extends GoogleCredentials
55+ implements ServiceAccountSigner {
4456
4557 private final Object appIdentityService ;
58+ private final String account ;
4659 private final Method getAccessToken ;
4760 private final Method getAccessTokenResult ;
61+ private final Method signForApp ;
62+ private final Method getSignature ;
4863 private final Collection <String > scopes ;
4964
5065 AppEngineCredentials () {
@@ -59,6 +74,12 @@ private static class AppEngineCredentials extends GoogleCredentials {
5974 "com.google.appengine.api.appidentity.AppIdentityService$GetAccessTokenResult" );
6075 this .getAccessTokenResult = serviceClass .getMethod ("getAccessToken" , Iterable .class );
6176 this .getAccessToken = tokenResultClass .getMethod ("getAccessToken" );
77+ this .account = (String ) serviceClass .getMethod ("getServiceAccountName" )
78+ .invoke (appIdentityService );
79+ this .signForApp = serviceClass .getMethod ("signForApp" , byte [].class );
80+ Class <?> signingResultClass = Class .forName (
81+ "com.google.appengine.api.appidentity.AppIdentityService$SigningResult" );
82+ this .getSignature = signingResultClass .getMethod ("getSignature" );
6283 this .scopes = null ;
6384 } catch (Exception e ) {
6485 throw new RuntimeException ("Could not create AppEngineCredentials." , e );
@@ -69,11 +90,14 @@ private static class AppEngineCredentials extends GoogleCredentials {
6990 this .appIdentityService = unscoped .appIdentityService ;
7091 this .getAccessToken = unscoped .getAccessToken ;
7192 this .getAccessTokenResult = unscoped .getAccessTokenResult ;
93+ this .account = unscoped .account ;
94+ this .signForApp = unscoped .signForApp ;
95+ this .getSignature = unscoped .getSignature ;
7296 this .scopes = scopes ;
7397 }
7498
7599 /**
76- * Refresh the access token by getting it from the App Identity service
100+ * Refresh the access token by getting it from the App Identity service.
77101 */
78102 @ Override
79103 public AccessToken refreshAccessToken () throws IOException {
@@ -98,6 +122,21 @@ public boolean createScopedRequired() {
98122 public GoogleCredentials createScoped (Collection <String > scopes ) {
99123 return new AppEngineCredentials (scopes , this );
100124 }
125+
126+ @ Override
127+ public String account () {
128+ return account ;
129+ }
130+
131+ @ Override
132+ public byte [] sign (byte [] toSign ) {
133+ try {
134+ Object signingResult = signForApp .invoke (appIdentityService , (Object ) toSign );
135+ return (byte []) getSignature .invoke (signingResult );
136+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
137+ throw new SigningException ("Failed to sign the provided bytes" , ex );
138+ }
139+ }
101140 }
102141
103142 private static class AppEngineAuthCredentialsState
@@ -122,14 +161,27 @@ public boolean equals(Object obj) {
122161 }
123162
124163 @ Override
125- public GoogleCredentials credentials () {
126- return new AppEngineCredentials ();
164+ public AppEngineCredentials credentials () {
165+ if (credentials == null ) {
166+ credentials = new AppEngineCredentials ();
167+ }
168+ return credentials ;
127169 }
128170
129171 @ Override
130172 public RestorableState <AuthCredentials > capture () {
131173 return STATE ;
132174 }
175+
176+ @ Override
177+ public String account () {
178+ return credentials ().account ();
179+ }
180+
181+ @ Override
182+ public byte [] sign (byte [] toSign ) {
183+ return credentials ().sign (toSign );
184+ }
133185 }
134186
135187 /**
@@ -138,8 +190,10 @@ public RestorableState<AuthCredentials> capture() {
138190 * @see <a href="https://cloud.google.com/docs/authentication#user_accounts_and_service_accounts">
139191 * User accounts and service accounts</a>
140192 */
141- public static class ServiceAccountAuthCredentials extends AuthCredentials {
193+ public static class ServiceAccountAuthCredentials extends AuthCredentials
194+ implements ServiceAccountSigner {
142195
196+ private final ServiceAccountCredentials credentials ;
143197 private final String account ;
144198 private final PrivateKey privateKey ;
145199
@@ -178,23 +232,44 @@ public boolean equals(Object obj) {
178232 }
179233
180234 ServiceAccountAuthCredentials (String account , PrivateKey privateKey ) {
181- this .account = checkNotNull (account );
182- this .privateKey = checkNotNull (privateKey );
235+ this (new ServiceAccountCredentials (null , account , privateKey , null , null ));
236+ }
237+
238+ ServiceAccountAuthCredentials (ServiceAccountCredentials credentials ) {
239+ this .credentials = checkNotNull (credentials );
240+ this .account = checkNotNull (credentials .getClientEmail ());
241+ this .privateKey = checkNotNull (credentials .getPrivateKey ());
183242 }
184243
185244 @ Override
186245 public ServiceAccountCredentials credentials () {
187- return new ServiceAccountCredentials ( null , account , privateKey , null , null ) ;
246+ return credentials ;
188247 }
189248
249+ @ Override
190250 public String account () {
191251 return account ;
192252 }
193253
254+ /**
255+ * Returns the private key associated with the service account credentials.
256+ */
194257 public PrivateKey privateKey () {
195258 return privateKey ;
196259 }
197260
261+ @ Override
262+ public byte [] sign (byte [] toSign ) {
263+ try {
264+ Signature signer = Signature .getInstance ("SHA256withRSA" );
265+ signer .initSign (privateKey ());
266+ signer .update (toSign );
267+ return signer .sign ();
268+ } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException ex ) {
269+ throw new SigningException ("Failed to sign the provided bytes" , ex );
270+ }
271+ }
272+
198273 @ Override
199274 public RestorableState <AuthCredentials > capture () {
200275 return new ServiceAccountAuthCredentialsState (account , privateKey );
@@ -242,6 +317,10 @@ public boolean equals(Object obj) {
242317 }
243318 }
244319
320+ ApplicationDefaultAuthCredentials (GoogleCredentials credentials ) {
321+ googleCredentials = credentials ;
322+ }
323+
245324 ApplicationDefaultAuthCredentials () throws IOException {
246325 googleCredentials = GoogleCredentials .getApplicationDefault ();
247326 }
@@ -320,7 +399,12 @@ public static AuthCredentials createForAppEngine() {
320399 * @throws IOException if the credentials cannot be created in the current environment
321400 */
322401 public static AuthCredentials createApplicationDefaults () throws IOException {
323- return new ApplicationDefaultAuthCredentials ();
402+ GoogleCredentials credentials = GoogleCredentials .getApplicationDefault ();
403+ if (credentials instanceof ServiceAccountCredentials ) {
404+ ServiceAccountCredentials serviceAccountCredentials = (ServiceAccountCredentials ) credentials ;
405+ return new ServiceAccountAuthCredentials (serviceAccountCredentials );
406+ }
407+ return new ApplicationDefaultAuthCredentials (credentials );
324408 }
325409
326410 /**
0 commit comments