2
2
3
3
import static io .quarkus .smallrye .jwt .deployment .SmallRyeJwtProcessor .MP_JWT_VERIFY_KEY_LOCATION ;
4
4
5
+ import java .io .File ;
6
+ import java .io .FileWriter ;
7
+ import java .io .IOException ;
8
+ import java .nio .file .Path ;
9
+ import java .security .GeneralSecurityException ;
5
10
import java .security .Key ;
6
11
import java .security .KeyPair ;
7
12
import java .security .NoSuchAlgorithmException ;
8
13
import java .util .Base64 ;
14
+ import java .util .Collection ;
9
15
import java .util .HashMap ;
10
16
import java .util .Map ;
17
+ import java .util .Optional ;
11
18
import java .util .Set ;
12
19
import java .util .stream .Collectors ;
13
20
14
21
import org .eclipse .microprofile .config .ConfigProvider ;
15
22
import org .jboss .logging .Logger ;
16
23
24
+ import io .quarkus .bootstrap .workspace .ArtifactSources ;
25
+ import io .quarkus .bootstrap .workspace .SourceDir ;
17
26
import io .quarkus .deployment .Feature ;
18
27
import io .quarkus .deployment .IsNormal ;
19
28
import io .quarkus .deployment .annotations .BuildProducer ;
20
29
import io .quarkus .deployment .annotations .BuildStep ;
21
30
import io .quarkus .deployment .builditem .DevServicesResultBuildItem ;
22
31
import io .quarkus .deployment .builditem .LiveReloadBuildItem ;
32
+ import io .quarkus .deployment .pkg .builditem .CurateOutcomeBuildItem ;
23
33
import io .smallrye .jwt .util .KeyUtils ;
24
34
25
35
public class SmallryeJwtDevModeProcessor {
26
36
27
37
private static final Logger LOGGER = Logger .getLogger (SmallryeJwtDevModeProcessor .class );
28
38
29
- private static final String MP_JWT_VERIFY_PUBLIC_KEY = "mp.jwt.verify.publickey" ;
39
+ public static final String MP_JWT_VERIFY_PUBLIC_KEY = "mp.jwt.verify.publickey" ;
30
40
private static final String MP_JWT_VERIFY_ISSUER = "mp.jwt.verify.issuer" ;
41
+ private static final String SMALLRYE_JWT_DECRYPT_KEY = "smallrye.jwt.decrypt.key" ; // no MP equivalent
31
42
private static final String MP_JWT_DECRYPT_KEY_LOCATION = "mp.jwt.decrypt.key.location" ;
32
43
33
44
private static final String SMALLRYE_JWT_NEW_TOKEN_ISSUER = "smallrye.jwt.new-token.issuer" ;
34
45
private static final String SMALLRYE_JWT_SIGN_KEY_LOCATION = "smallrye.jwt.sign.key.location" ;
35
- private static final String SMALLRYE_JWT_SIGN_KEY = "smallrye.jwt.sign.key" ;
46
+ public static final String SMALLRYE_JWT_SIGN_KEY = "smallrye.jwt.sign.key" ;
47
+ private static final String SMALLRYE_JWT_ENCRYPT_KEY = "smallrye.jwt.encrypt.key" ;
36
48
private static final String SMALLRYE_JWT_ENCRYPT_KEY_LOCATION = "smallrye.jwt.encrypt.key.location" ;
37
49
38
50
private static final String NONE = "NONE" ;
@@ -43,10 +55,14 @@ public class SmallryeJwtDevModeProcessor {
43
55
private static final Set <String > JWT_SIGN_KEY_PROPERTIES = Set .of (
44
56
MP_JWT_VERIFY_KEY_LOCATION ,
45
57
MP_JWT_VERIFY_PUBLIC_KEY ,
58
+ SMALLRYE_JWT_DECRYPT_KEY ,
46
59
MP_JWT_DECRYPT_KEY_LOCATION ,
47
60
SMALLRYE_JWT_SIGN_KEY_LOCATION ,
48
61
SMALLRYE_JWT_SIGN_KEY ,
62
+ SMALLRYE_JWT_ENCRYPT_KEY ,
49
63
SMALLRYE_JWT_ENCRYPT_KEY_LOCATION );
64
+ public static final String DEV_PRIVATE_KEY_PEM = "dev.privateKey.pem" ;
65
+ public static final String DEV_PUBLIC_KEY_PEM = "dev.publicKey.pem" ;
50
66
51
67
/**
52
68
* This build step generates an RSA-256 key pair for development and test modes.
@@ -58,10 +74,15 @@ public class SmallryeJwtDevModeProcessor {
58
74
* this build step will add a default issuer, regardless of the above condition.
59
75
*
60
76
* @throws NoSuchAlgorithmException if RSA-256 key generation fails.
77
+ * @throws IOException if persistent key storage fails
61
78
*/
62
79
@ BuildStep (onlyIfNot = { IsNormal .class })
63
80
void generateSignKeys (BuildProducer <DevServicesResultBuildItem > devServices ,
64
- LiveReloadBuildItem liveReloadBuildItem ) throws NoSuchAlgorithmException {
81
+ LiveReloadBuildItem liveReloadBuildItem ,
82
+ CurateOutcomeBuildItem curateOutcomeBuildItem ,
83
+ Optional <GeneratePersistentDevModeJwtKeysBuildItem > generatePersistentDevModeJwtKeysBuildItem ,
84
+ Optional <GenerateEncryptedDevModeJwtKeysBuildItem > generateEncryptedDevModeJwtKeysBuildItem )
85
+ throws GeneralSecurityException , IOException {
65
86
66
87
Set <String > userProps = JWT_SIGN_KEY_PROPERTIES
67
88
.stream ()
@@ -71,7 +92,8 @@ void generateSignKeys(BuildProducer<DevServicesResultBuildItem> devServices,
71
92
if (!userProps .isEmpty ()) {
72
93
// If the user has set the property, we need to avoid adding or overriding it with the
73
94
// smallrye default configuration
74
- Map <String , String > devServiceProps = addDefaultSmallryePropertiesIfMissing (userProps );
95
+ Map <String , String > devServiceProps = addDefaultSmallryePropertiesIfMissing (userProps ,
96
+ generateEncryptedDevModeJwtKeysBuildItem );
75
97
76
98
if (!isConfigPresent (MP_JWT_VERIFY_ISSUER ) && !isConfigPresent (SMALLRYE_JWT_NEW_TOKEN_ISSUER )) {
77
99
devServiceProps .put (MP_JWT_VERIFY_ISSUER , DEFAULT_ISSUER );
@@ -88,11 +110,12 @@ void generateSignKeys(BuildProducer<DevServicesResultBuildItem> devServices,
88
110
"Please ensure the correct keys/locations are set in production to avoid potential issues." );
89
111
if (ctx == null && !liveReloadBuildItem .isLiveReload ()) {
90
112
// first execution
91
- KeyPair keyPair = KeyUtils . generateKeyPair ( KEY_SIZE );
113
+ KeyPair keyPair = generateOrReloadKeyPair ( curateOutcomeBuildItem , generatePersistentDevModeJwtKeysBuildItem );
92
114
String publicKey = getStringKey (keyPair .getPublic ());
93
115
String privateKey = getStringKey (keyPair .getPrivate ());
94
116
95
- Map <String , String > devServiceProps = generateDevServiceProperties (publicKey , privateKey );
117
+ Map <String , String > devServiceProps = generateDevServiceProperties (publicKey , privateKey ,
118
+ generateEncryptedDevModeJwtKeysBuildItem );
96
119
97
120
if (!isConfigPresent (MP_JWT_VERIFY_ISSUER ) && !isConfigPresent (SMALLRYE_JWT_NEW_TOKEN_ISSUER )) {
98
121
devServiceProps .put (MP_JWT_VERIFY_ISSUER , DEFAULT_ISSUER );
@@ -110,7 +133,67 @@ void generateSignKeys(BuildProducer<DevServicesResultBuildItem> devServices,
110
133
}
111
134
}
112
135
113
- private Map <String , String > addDefaultSmallryePropertiesIfMissing (Set <String > userConfigs ) {
136
+ private KeyPair generateOrReloadKeyPair (CurateOutcomeBuildItem curateOutcomeBuildItem ,
137
+ Optional <GeneratePersistentDevModeJwtKeysBuildItem > generatePersistentDevModeJwtKeysBuildItem )
138
+ throws GeneralSecurityException , IOException {
139
+ if (generatePersistentDevModeJwtKeysBuildItem .isPresent ()) {
140
+ File buildDir = getBuildDir (curateOutcomeBuildItem );
141
+
142
+ buildDir .mkdirs ();
143
+ File privateKey = new File (buildDir , DEV_PRIVATE_KEY_PEM );
144
+ File publicKey = new File (buildDir , DEV_PUBLIC_KEY_PEM );
145
+ if (!privateKey .exists () || !publicKey .exists ()) {
146
+ KeyPair keyPair = KeyUtils .generateKeyPair (KEY_SIZE );
147
+ LOGGER .infof ("Generating private/public keys for DEV/TEST in %s and %s" , privateKey , publicKey );
148
+ try (FileWriter fw = new FileWriter (privateKey )) {
149
+ fw .append ("-----BEGIN PRIVATE KEY-----\n " );
150
+ fw .append (Base64 .getMimeEncoder ().encodeToString (keyPair .getPrivate ().getEncoded ()));
151
+ fw .append ("\n " );
152
+ fw .append ("-----END PRIVATE KEY-----\n " );
153
+ }
154
+ try (FileWriter fw = new FileWriter (publicKey )) {
155
+ fw .append ("-----BEGIN PUBLIC KEY-----\n " );
156
+ fw .append (Base64 .getMimeEncoder ().encodeToString (keyPair .getPublic ().getEncoded ()));
157
+ fw .append ("\n " );
158
+ fw .append ("-----END PUBLIC KEY-----\n " );
159
+ }
160
+ return keyPair ;
161
+ } else {
162
+ // read from disk
163
+ return new KeyPair (KeyUtils .readPublicKey (publicKey .getName ()),
164
+ KeyUtils .readPrivateKey (privateKey .getName ()));
165
+ }
166
+ } else {
167
+ return KeyUtils .generateKeyPair (KEY_SIZE );
168
+ }
169
+ }
170
+
171
+ public static File getBuildDir (CurateOutcomeBuildItem curateOutcomeBuildItem ) {
172
+ File buildDir = null ;
173
+ ArtifactSources src = curateOutcomeBuildItem .getApplicationModel ().getAppArtifact ().getSources ();
174
+ if (src != null ) { // shouldn't be null in dev mode
175
+ Collection <SourceDir > srcDirs = src .getResourceDirs ();
176
+ if (srcDirs .isEmpty ()) {
177
+ // if the module has no resources dir?
178
+ srcDirs = src .getSourceDirs ();
179
+ }
180
+ if (!srcDirs .isEmpty ()) {
181
+ // pick the first resources output dir
182
+ Path resourcesOutputDir = srcDirs .iterator ().next ().getOutputDir ();
183
+ buildDir = resourcesOutputDir .toFile ();
184
+ }
185
+ }
186
+ if (buildDir == null ) {
187
+ // the module doesn't have any sources nor resources, stick to the build dir
188
+ buildDir = new File (
189
+ curateOutcomeBuildItem .getApplicationModel ().getAppArtifact ().getWorkspaceModule ().getBuildDir (),
190
+ "classes" );
191
+ }
192
+ return buildDir ;
193
+ }
194
+
195
+ private Map <String , String > addDefaultSmallryePropertiesIfMissing (Set <String > userConfigs ,
196
+ Optional <GenerateEncryptedDevModeJwtKeysBuildItem > generateEncryptedDevModeJwtKeysBuildItem ) {
114
197
HashMap <String , String > devServiceConfigs = new HashMap <>();
115
198
if (!userConfigs .contains (SMALLRYE_JWT_SIGN_KEY )) {
116
199
devServiceConfigs .put (SMALLRYE_JWT_SIGN_KEY , NONE );
@@ -120,6 +203,16 @@ private Map<String, String> addDefaultSmallryePropertiesIfMissing(Set<String> us
120
203
devServiceConfigs .put (MP_JWT_VERIFY_PUBLIC_KEY , NONE );
121
204
}
122
205
206
+ if (generateEncryptedDevModeJwtKeysBuildItem .isPresent ()) {
207
+ if (!userConfigs .contains (SMALLRYE_JWT_ENCRYPT_KEY ) && !userConfigs .contains (SMALLRYE_JWT_ENCRYPT_KEY_LOCATION )) {
208
+ devServiceConfigs .put (SMALLRYE_JWT_ENCRYPT_KEY , NONE );
209
+ }
210
+
211
+ if (!userConfigs .contains (SMALLRYE_JWT_DECRYPT_KEY ) && !userConfigs .contains (MP_JWT_DECRYPT_KEY_LOCATION )) {
212
+ devServiceConfigs .put (SMALLRYE_JWT_DECRYPT_KEY , NONE );
213
+ }
214
+ }
215
+
123
216
return devServiceConfigs ;
124
217
}
125
218
@@ -133,10 +226,15 @@ private DevServicesResultBuildItem smallryeJwtDevServiceWith(Map<String, String>
133
226
Feature .SMALLRYE_JWT .name (), null , properties );
134
227
}
135
228
136
- private static Map <String , String > generateDevServiceProperties (String publicKey , String privateKey ) {
229
+ private static Map <String , String > generateDevServiceProperties (String publicKey , String privateKey ,
230
+ Optional <GenerateEncryptedDevModeJwtKeysBuildItem > generateEncryptedDevModeJwtKeysBuildItem ) {
137
231
HashMap <String , String > properties = new HashMap <>();
138
232
properties .put (MP_JWT_VERIFY_PUBLIC_KEY , publicKey );
139
233
properties .put (SMALLRYE_JWT_SIGN_KEY , privateKey );
234
+ if (generateEncryptedDevModeJwtKeysBuildItem .isPresent ()) {
235
+ properties .put (SMALLRYE_JWT_ENCRYPT_KEY , publicKey );
236
+ properties .put (SMALLRYE_JWT_DECRYPT_KEY , privateKey );
237
+ }
140
238
return properties ;
141
239
}
142
240
0 commit comments