|
56 | 56 |
|
57 | 57 | import java.nio.charset.StandardCharsets; |
58 | 58 | import java.text.ParseException; |
| 59 | +import java.util.Collections; |
59 | 60 | import java.util.EnumSet; |
60 | 61 | import java.util.Objects; |
61 | 62 | import java.util.regex.Pattern; |
62 | 63 |
|
| 64 | +import com.fasterxml.jackson.core.JsonProcessingException; |
63 | 65 | import com.google.auto.service.AutoService; |
64 | 66 | import com.nimbusds.jose.JOSEException; |
65 | 67 | import com.nimbusds.jose.JWEObjectJSON; |
| 68 | +import com.nimbusds.jose.Payload; |
66 | 69 | import com.nimbusds.jose.crypto.MultiDecrypter; |
| 70 | +import com.nimbusds.jose.jwk.JWK; |
67 | 71 |
|
68 | 72 | @AutoService(Vault.class) |
69 | 73 | public class CryptoVault extends AbstractVault { |
70 | 74 | private static final Logger log = LogManager.getLogger(CryptoVault.class); |
71 | 75 |
|
| 76 | + private static final String UVF_SPEC_VERSION_KEY_PARAM = "uvf.spec.version"; |
| 77 | + |
72 | 78 | private static final String REGULAR_FILE_EXTENSION = ".uvf"; |
73 | 79 | private static final String FILENAME_DIRECTORYID = "dir"; |
74 | 80 | private static final String DIRECTORY_METADATA_FILENAME = String.format("%s%s", FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION); |
@@ -128,20 +134,40 @@ public AbstractVault create(final Session<?> session, final String region, final |
128 | 134 | return this; |
129 | 135 | } |
130 | 136 |
|
| 137 | + public static String decryptWithJWK(final String jwe, final JWK jwk) throws ParseException, JOSEException, JsonProcessingException, VaultException { |
| 138 | + final JWEObjectJSON jweObject = JWEObjectJSON.parse(jwe); |
| 139 | + jweObject.decrypt(new MultiDecrypter(jwk, Collections.singleton(UVF_SPEC_VERSION_KEY_PARAM))); |
| 140 | + |
| 141 | + // https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.11 |
| 142 | + // Recipients MAY consider the JWS to be invalid if the critical |
| 143 | + // list contains any Header Parameter names defined by this |
| 144 | + // specification or [JWA] for use with JWS or if any other constraints on its use are violated. |
| 145 | + final Object uvfSpecVersion = jweObject.getHeader().getCustomParams().get(UVF_SPEC_VERSION_KEY_PARAM); |
| 146 | + if(uvfSpecVersion.equals(1)) { |
| 147 | + throw new VaultException(String.format("Unexpected value for critical header %s: found %s, expected \"1\"", UVF_SPEC_VERSION_KEY_PARAM, uvfSpecVersion)); |
| 148 | + } |
| 149 | + |
| 150 | + final Payload payload = jweObject.getPayload(); |
| 151 | + return payload.toString(); |
| 152 | + } |
| 153 | + |
| 154 | + |
131 | 155 | // load -> unlock -> open |
132 | 156 | @Override |
133 | 157 | public CryptoVault load(final Session<?> session, final PasswordCallback callback, final VaultMetadataProvider metadata) throws BackgroundException { |
134 | | - final JWKCallback jwk = JWKCallback.cast(callback); |
| 158 | + final JWKCallback jwkCallback = JWKCallback.cast(callback); |
135 | 159 | final VaultMetadataUVFProvider metadataProvider = VaultMetadataUVFProvider.cast(metadata); |
136 | | - final JWEObjectJSON jweObject; |
| 160 | + final String uvfMetadata; |
137 | 161 | try { |
138 | | - jweObject = JWEObjectJSON.parse(new String(metadataProvider.getMetadata(), StandardCharsets.US_ASCII)); |
139 | | - jweObject.decrypt(new MultiDecrypter(jwk.prompt(session.getHost(), StringUtils.EMPTY, StringUtils.EMPTY, new LoginOptions()).getKey())); |
| 162 | + final String jwe = new String(metadataProvider.getMetadata(), StandardCharsets.US_ASCII); |
| 163 | + final JWK jwk = jwkCallback.prompt(session.getHost(), StringUtils.EMPTY, StringUtils.EMPTY, new LoginOptions()).getKey(); |
| 164 | + uvfMetadata = decryptWithJWK(jwe, jwk); |
140 | 165 | } |
141 | | - catch(ParseException | JOSEException e) { |
| 166 | + catch(ParseException | JOSEException | JsonProcessingException e) { |
142 | 167 | throw new VaultException("Failure retrieving key material", e); |
143 | 168 | } |
144 | | - masterKey = UVFMasterkey.fromDecryptedPayload(jweObject.getPayload().toString()); |
| 169 | + |
| 170 | + masterKey = UVFMasterkey.fromDecryptedPayload(uvfMetadata); |
145 | 171 | final CryptorProvider provider = CryptorProvider.forScheme(CryptorProvider.Scheme.UVF_DRAFT); |
146 | 172 | log.debug("Initialized crypto provider {}", provider); |
147 | 173 | this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide()); |
|
0 commit comments