1919
2020package io .temporal .samples .encryptedpayloads ;
2121
22- import com .google .common .base .Defaults ;
2322import com .google .protobuf .ByteString ;
2423import io .temporal .api .common .v1 .Payload ;
25- import io .temporal .api .common .v1 .Payloads ;
26- import io .temporal .common .converter .DataConverter ;
2724import io .temporal .common .converter .DataConverterException ;
28- import java .lang .reflect .Type ;
25+ import io .temporal .common .converter .EncodingKeys ;
26+ import io .temporal .payload .codec .PayloadCodec ;
27+ import io .temporal .payload .codec .PayloadCodecException ;
2928import java .nio .ByteBuffer ;
3029import java .nio .charset .Charset ;
3130import java .nio .charset .StandardCharsets ;
3231import java .security .SecureRandom ;
33- import java .util .Optional ;
32+ import java .util .List ;
33+ import java .util .stream .Collectors ;
3434import javax .crypto .Cipher ;
3535import javax .crypto .SecretKey ;
3636import javax .crypto .spec .GCMParameterSpec ;
3737import javax .crypto .spec .SecretKeySpec ;
38+ import org .jetbrains .annotations .NotNull ;
3839
39- public class CryptDataConverter implements DataConverter {
40- static final String METADATA_ENCODING_KEY = "encoding" ;
40+ class CryptCodec implements PayloadCodec {
4141 static final ByteString METADATA_ENCODING =
4242 ByteString .copyFrom ("binary/encrypted" , StandardCharsets .UTF_8 );
4343
@@ -53,10 +53,61 @@ public class CryptDataConverter implements DataConverter {
5353 private static final int GCM_TAG_LENGTH_BIT = 128 ;
5454 private static final Charset UTF_8 = StandardCharsets .UTF_8 ;
5555
56- private final DataConverter converter ;
56+ @ NotNull
57+ @ Override
58+ public List <Payload > encode (@ NotNull List <Payload > payloads ) {
59+ return payloads .stream ().map (this ::encodePayload ).collect (Collectors .toList ());
60+ }
61+
62+ @ NotNull
63+ @ Override
64+ public List <Payload > decode (@ NotNull List <Payload > payloads ) {
65+ return payloads .stream ().map (this ::decodePayload ).collect (Collectors .toList ());
66+ }
67+
68+ private Payload encodePayload (Payload payload ) {
69+ String keyId = getKeyId ();
70+ SecretKey key = getKey (keyId );
71+
72+ byte [] encryptedData ;
73+ try {
74+ encryptedData = encrypt (payload .toByteArray (), key );
75+ } catch (Throwable e ) {
76+ throw new DataConverterException (e );
77+ }
5778
58- public CryptDataConverter (DataConverter converter ) {
59- this .converter = converter ;
79+ return Payload .newBuilder ()
80+ .putMetadata (EncodingKeys .METADATA_ENCODING_KEY , METADATA_ENCODING )
81+ .putMetadata (METADATA_ENCRYPTION_CIPHER_KEY , METADATA_ENCRYPTION_CIPHER )
82+ .putMetadata (METADATA_ENCRYPTION_KEY_ID_KEY , ByteString .copyFromUtf8 (keyId ))
83+ .setData (ByteString .copyFrom (encryptedData ))
84+ .build ();
85+ }
86+
87+ private Payload decodePayload (Payload payload ) {
88+ if (METADATA_ENCODING .equals (
89+ payload .getMetadataOrDefault (EncodingKeys .METADATA_ENCODING_KEY , null ))) {
90+ String keyId ;
91+ try {
92+ keyId = payload .getMetadataOrThrow (METADATA_ENCRYPTION_KEY_ID_KEY ).toString (UTF_8 );
93+ } catch (Exception e ) {
94+ throw new PayloadCodecException (e );
95+ }
96+ SecretKey key = getKey (keyId );
97+
98+ byte [] plainData ;
99+ Payload decryptedPayload ;
100+
101+ try {
102+ plainData = decrypt (payload .getData ().toByteArray (), key );
103+ decryptedPayload = Payload .parseFrom (plainData );
104+ return decryptedPayload ;
105+ } catch (Throwable e ) {
106+ throw new PayloadCodecException (e );
107+ }
108+ } else {
109+ return payload ;
110+ }
60111 }
61112
62113 private String getKeyId () {
@@ -106,105 +157,4 @@ private byte[] decrypt(byte[] encryptedDataWithNonce, SecretKey key) throws Exce
106157
107158 return cipher .doFinal (encryptedData );
108159 }
109-
110- @ Override
111- public <T > Optional <Payload > toPayload (T value ) throws DataConverterException {
112- return converter .toPayload (value );
113- }
114-
115- public <T > Optional <Payload > toEncryptedPayload (T value ) throws DataConverterException {
116- Optional <Payload > optionalPayload = converter .toPayload (value );
117-
118- if (!optionalPayload .isPresent ()) {
119- return optionalPayload ;
120- }
121-
122- Payload innerPayload = optionalPayload .get ();
123-
124- String keyId = getKeyId ();
125- SecretKey key = getKey (keyId );
126-
127- byte [] encryptedData ;
128- try {
129- encryptedData = encrypt (innerPayload .toByteArray (), key );
130- } catch (Throwable e ) {
131- throw new DataConverterException (e );
132- }
133-
134- Payload encryptedPayload =
135- Payload .newBuilder ()
136- .putMetadata (METADATA_ENCODING_KEY , METADATA_ENCODING )
137- .putMetadata (METADATA_ENCRYPTION_CIPHER_KEY , METADATA_ENCRYPTION_CIPHER )
138- .putMetadata (METADATA_ENCRYPTION_KEY_ID_KEY , ByteString .copyFromUtf8 (keyId ))
139- .setData (ByteString .copyFrom (encryptedData ))
140- .build ();
141-
142- return Optional .of (encryptedPayload );
143- }
144-
145- @ Override
146- public <T > T fromPayload (Payload payload , Class <T > valueClass , Type valueType ) {
147- ByteString encoding = payload .getMetadataOrDefault (METADATA_ENCODING_KEY , null );
148- if (!encoding .equals (METADATA_ENCODING )) {
149- return converter .fromPayload (payload , valueClass , valueType );
150- }
151-
152- String keyId ;
153- try {
154- keyId = payload .getMetadataOrThrow (METADATA_ENCRYPTION_KEY_ID_KEY ).toString (UTF_8 );
155- } catch (Exception e ) {
156- throw new DataConverterException (payload , valueClass , e );
157- }
158- SecretKey key = getKey (keyId );
159-
160- byte [] plainData ;
161- Payload decryptedPayload ;
162-
163- try {
164- plainData = decrypt (payload .getData ().toByteArray (), key );
165- decryptedPayload = Payload .parseFrom (plainData );
166- } catch (Throwable e ) {
167- throw new DataConverterException (e );
168- }
169-
170- return converter .fromPayload (decryptedPayload , valueClass , valueType );
171- }
172-
173- @ Override
174- public Optional <Payloads > toPayloads (Object ... values ) throws DataConverterException {
175- if (values == null || values .length == 0 ) {
176- return Optional .empty ();
177- }
178- try {
179- Payloads .Builder result = Payloads .newBuilder ();
180- for (Object value : values ) {
181- Optional <Payload > payload = toEncryptedPayload (value );
182- if (payload .isPresent ()) {
183- result .addPayloads (payload .get ());
184- } else {
185- result .addPayloads (Payload .getDefaultInstance ());
186- }
187- }
188- return Optional .of (result .build ());
189- } catch (DataConverterException e ) {
190- throw e ;
191- } catch (Throwable e ) {
192- throw new DataConverterException (e );
193- }
194- }
195-
196- @ Override
197- public <T > T fromPayloads (
198- int index , Optional <Payloads > content , Class <T > parameterType , Type genericParameterType )
199- throws DataConverterException {
200- if (!content .isPresent ()) {
201- return (T ) Defaults .defaultValue ((Class <?>) parameterType );
202- }
203- int count = content .get ().getPayloadsCount ();
204- // To make adding arguments a backwards compatible change
205- if (index >= count ) {
206- return (T ) Defaults .defaultValue ((Class <?>) parameterType );
207- }
208- return fromPayload (content .get ().getPayloads (index ), parameterType , genericParameterType );
209- }
210160}
0 commit comments