11package io .opentdf .platform ;
22
33import com .google .gson .Gson ;
4+ import com .google .gson .GsonBuilder ;
45import com .google .gson .JsonDeserializationContext ;
56import com .google .gson .JsonDeserializer ;
67import com .google .gson .JsonElement ;
78import com .google .gson .JsonObject ;
89import com .google .gson .JsonParseException ;
10+ import com .google .gson .JsonSyntaxException ;
11+ import com .google .gson .reflect .TypeToken ;
912import com .nimbusds .jose .JWSAlgorithm ;
1013import com .nimbusds .jose .jwk .Curve ;
1114import com .nimbusds .jose .jwk .JWK ;
1215import com .nimbusds .jose .jwk .KeyUse ;
13- import com .nimbusds .jose .jwk .RSAKey ;
1416import com .nimbusds .jose .jwk .gen .ECKeyGenerator ;
1517import com .nimbusds .jose .jwk .gen .RSAKeyGenerator ;
16- import com .google .gson .GsonBuilder ;
17- import com .google .gson .reflect .TypeToken ;
18-
19- import java .security .cert .X509Certificate ;
20- import java .text .ParseException ;
21- import com .google .gson .JsonSyntaxException ;
22- import io .opentdf .platform .sdk .AssertionConfig ;
23- import io .opentdf .platform .sdk .AutoConfigureException ;
24- import io .opentdf .platform .sdk .Config ;
25- import io .opentdf .platform .sdk .KeyType ;
26- import io .opentdf .platform .sdk .SDK ;
27- import io .opentdf .platform .sdk .SDKBuilder ;
28- import picocli .CommandLine ;
29- import picocli .CommandLine .HelpCommand ;
30- import picocli .CommandLine .Option ;
31-
32- import javax .net .ssl .X509TrustManager ;
3318import java .io .BufferedInputStream ;
3419import java .io .BufferedOutputStream ;
3520import java .io .File ;
4732import java .security .spec .InvalidKeySpecException ;
4833import java .security .spec .PKCS8EncodedKeySpec ;
4934import java .security .spec .X509EncodedKeySpec ;
35+ import java .text .ParseException ;
5036import java .util .ArrayList ;
5137import java .util .Base64 ;
5238import java .util .List ;
5339import java .util .Map ;
5440import java .util .Optional ;
5541import java .util .UUID ;
42+ import java .util .concurrent .Callable ;
5643import java .util .function .Consumer ;
44+ import io .opentdf .platform .sdk .AssertionConfig ;
45+ import io .opentdf .platform .sdk .AutoConfigureException ;
46+ import io .opentdf .platform .sdk .Config ;
47+ import io .opentdf .platform .sdk .KeyType ;
48+ import io .opentdf .platform .sdk .SDK ;
49+ import io .opentdf .platform .sdk .SDKBuilder ;
50+ import picocli .CommandLine ;
51+ import picocli .CommandLine .HelpCommand ;
52+ import picocli .CommandLine .Option ;
5753
5854/**
5955 * Constants for the TDF command line tool.
@@ -67,36 +63,32 @@ class Versions {
6763 public static final String TDF_SPEC = "4.3.0" ;
6864}
6965
70- @ CommandLine .Command (name = "tdf" , subcommands = { HelpCommand .class , Command .Supports .class }, version = "{\" version\" :\" " + Versions .SDK
71- + "\" ,\" tdfSpecVersion\" :\" " + Versions .TDF_SPEC + "\" }" )
66+ @ CommandLine .Command (name = "tdf" , subcommands = { HelpCommand .class ,
67+ Command .Supports .class }, version = "{\" version\" :\" " + Versions .SDK
68+ + "\" ,\" tdfSpecVersion\" :\" " + Versions .TDF_SPEC + "\" }" )
7269class Command {
7370
7471 @ Option (names = { "-V" , "--version" }, versionHelp = true , description = "display version info" )
7572 boolean versionInfoRequested ;
7673
7774 @ CommandLine .Command (name = "supports" , description = "Check if a feature is supported" )
78- static class Supports implements Runnable {
75+ static class Supports implements Callable < Integer > {
7976 @ CommandLine .Parameters (index = "0" , description = "Feature to check (e.g., dpop)" )
8077 private String feature ;
8178
8279 @ Override
83- public void run () {
84- // Check if the requested feature is supported
85- if ("dpop" .equalsIgnoreCase (feature )) {
86- // DPoP (RFC 9449) is supported
87- System .exit (0 );
88- } else {
89- // Unknown or unsupported feature
90- System .exit (1 );
91- }
80+ public Integer call () {
81+ return "dpop" .equalsIgnoreCase (feature ) ? 0 : 1 ;
9282 }
9383 }
9484
9585 private static class AssertionKeyDeserializer implements JsonDeserializer <AssertionConfig .AssertionKey > {
9686 @ Override
97- public AssertionConfig .AssertionKey deserialize (JsonElement json , java .lang .reflect .Type typeOfT , JsonDeserializationContext context ) throws JsonParseException {
87+ public AssertionConfig .AssertionKey deserialize (JsonElement json , java .lang .reflect .Type typeOfT ,
88+ JsonDeserializationContext context ) throws JsonParseException {
9889 JsonObject jsonObject = json .getAsJsonObject ();
99- AssertionConfig .AssertionKey assertionKey = new AssertionConfig .AssertionKey (AssertionConfig .AssertionKeyAlg .NotDefined , null );
90+ AssertionConfig .AssertionKey assertionKey = new AssertionConfig .AssertionKey (
91+ AssertionConfig .AssertionKeyAlg .NotDefined , null );
10092
10193 if (jsonObject .has ("alg" )) {
10294 assertionKey .alg = context .deserialize (jsonObject .get ("alg" ), AssertionConfig .AssertionKeyAlg .class );
@@ -112,7 +104,9 @@ public AssertionConfig.AssertionKey deserialize(JsonElement json, java.lang.refl
112104 }
113105 }
114106 if (jsonObject .has ("x5c" )) {
115- assertionKey .x5c = context .deserialize (jsonObject .get ("x5c" ), new TypeToken <List <com .nimbusds .jose .util .Base64 >>() {}.getType ());
107+ assertionKey .x5c = context .deserialize (jsonObject .get ("x5c" ),
108+ new TypeToken <List <com .nimbusds .jose .util .Base64 >>() {
109+ }.getType ());
116110 }
117111
118112 return assertionKey ;
@@ -130,7 +124,7 @@ private Gson buildGson() {
130124 private static final String PEM_HEADER = "-----BEGIN (.*)-----" ;
131125 private static final String PEM_FOOTER = "-----END (.*)-----" ;
132126
133- @ Option (names = { "--client-secret" }, required = true )
127+ @ Option (names = { "--client-secret" })
134128 private String clientSecret ;
135129
136130 @ Option (names = { "-h" , "--plaintext" }, defaultValue = "false" )
@@ -139,20 +133,18 @@ private Gson buildGson() {
139133 @ Option (names = { "-i" , "--insecure" }, defaultValue = "false" )
140134 private boolean insecure ;
141135
142- @ Option (names = { "--client-id" }, required = true )
136+ @ Option (names = { "--client-id" })
143137 private String clientId ;
144138
145- @ Option (names = { "-p" , "--platform-endpoint" }, required = true )
139+ @ Option (names = { "-p" , "--platform-endpoint" })
146140 private String platformEndpoint ;
147141
148- @ Option (names = { "--dpop" }, arity = "0..1" , fallbackValue = "" ,
149- scope = CommandLine .ScopeType .INHERIT ,
150- description = "Enable DPoP (RFC 9449). Optional: specify algorithm (RS256, RS384, RS512, ES256, ES384, ES512). Default: RS256." )
142+ @ Option (names = {
143+ "--dpop" }, arity = "0..1" , fallbackValue = "" , scope = CommandLine .ScopeType .INHERIT , description = "Enable DPoP (RFC 9449). Optional: specify algorithm (RS256, RS384, RS512, ES256, ES384, ES512). Default: RS256." )
151144 private String dpopAlg ;
152145
153- @ Option (names = { "--dpop-key" },
154- scope = CommandLine .ScopeType .INHERIT ,
155- description = "Enable DPoP using a PEM-encoded private key at <path>. Algorithm inferred from key type. Combinable with --dpop=<alg>." )
146+ @ Option (names = {
147+ "--dpop-key" }, scope = CommandLine .ScopeType .INHERIT , description = "Enable DPoP using a PEM-encoded private key at <path>. Algorithm inferred from key type. Combinable with --dpop=<alg>." )
156148 private Path dpopKeyPath ;
157149
158150 private Object correctKeyType (AssertionConfig .AssertionKeyAlg alg , Object key , boolean publicKey )
@@ -310,8 +302,10 @@ private SDK buildSDK() {
310302
311303 /**
312304 * Apply --dpop and --dpop-key options to the SDK builder.
313- * --dpop-key loads a PEM private key; --dpop specifies the algorithm (default RS256).
314- * If neither flag is set, the SDK auto-generates an ephemeral RSA-2048 DPoP key.
305+ * --dpop-key loads a PEM private key; --dpop specifies the algorithm (default
306+ * RS256).
307+ * If neither flag is set, the SDK auto-generates an ephemeral RSA-2048 DPoP
308+ * key.
315309 */
316310 private void applyDPoPOptions (SDKBuilder builder ) {
317311 try {
@@ -334,14 +328,21 @@ private void applyDPoPOptions(SDKBuilder builder) {
334328
335329 private static JWSAlgorithm parseAlgorithm (String alg ) {
336330 switch (alg .toUpperCase ()) {
337- case "RS256" : return JWSAlgorithm .RS256 ;
338- case "RS384" : return JWSAlgorithm .RS384 ;
339- case "RS512" : return JWSAlgorithm .RS512 ;
340- case "ES256" : return JWSAlgorithm .ES256 ;
341- case "ES384" : return JWSAlgorithm .ES384 ;
342- case "ES512" : return JWSAlgorithm .ES512 ;
343- default : throw new RuntimeException ("Unsupported DPoP algorithm: " + alg
344- + ". Supported: RS256, RS384, RS512, ES256, ES384, ES512" );
331+ case "RS256" :
332+ return JWSAlgorithm .RS256 ;
333+ case "RS384" :
334+ return JWSAlgorithm .RS384 ;
335+ case "RS512" :
336+ return JWSAlgorithm .RS512 ;
337+ case "ES256" :
338+ return JWSAlgorithm .ES256 ;
339+ case "ES384" :
340+ return JWSAlgorithm .ES384 ;
341+ case "ES512" :
342+ return JWSAlgorithm .ES512 ;
343+ default :
344+ throw new RuntimeException ("Unsupported DPoP algorithm: " + alg
345+ + ". Supported: RS256, RS384, RS512, ES256, ES384, ES512" );
345346 }
346347 }
347348
@@ -399,7 +400,8 @@ void decrypt(
399400 // try it as a file path
400401 try {
401402 String fileJson = new String (Files .readAllBytes (Paths .get (assertionVerificationInput )));
402- assertionVerificationKeys = gson .fromJson (fileJson , Config .AssertionVerificationKeys .class );
403+ assertionVerificationKeys = gson .fromJson (fileJson ,
404+ Config .AssertionVerificationKeys .class );
403405 } catch (JsonSyntaxException e2 ) {
404406 throw new RuntimeException ("Failed to parse assertion verification keys from file" , e2 );
405407 } catch (Exception e3 ) {
0 commit comments