http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java new file mode 100644 index 0000000..0a7c98f --- /dev/null +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java @@ -0,0 +1,135 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.jaxrs; + +import java.io.IOException; +import java.util.logging.Logger; + +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.HttpHeaders; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; +import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.common.JoseException; +import org.apache.cxf.rs.security.jose.common.JoseUtils; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jwt.AbstractJoseJwtConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtUtils; +import org.apache.cxf.security.SecurityContext; + +@PreMatching +@Priority(Priorities.AUTHENTICATION) +public class JwtAuthenticationFilter extends AbstractJoseJwtConsumer implements ContainerRequestFilter { + protected static final Logger LOG = LogUtils.getL7dLogger(JwtAuthenticationFilter.class); + + private static final String DEFAULT_AUTH_SCHEME = "JWT"; + private String expectedAuthScheme = DEFAULT_AUTH_SCHEME; + private int clockOffset; + private int ttl; + private String roleClaim; + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + String auth = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); + String[] parts = auth == null ? null : auth.split(" "); + if (parts == null || !expectedAuthScheme.equals(parts[0]) || parts.length != 2) { + throw new JoseException(expectedAuthScheme + " scheme is expected"); + } + JwtToken token = super.getJwtToken(parts[1]); + JoseUtils.setMessageContextProperty(token.getHeaders()); + + SecurityContext securityContext = configureSecurityContext(token); + if (securityContext != null) { + JAXRSUtils.getCurrentMessage().put(SecurityContext.class, securityContext); + } + } + + protected SecurityContext configureSecurityContext(JwtToken jwt) { + Message m = JAXRSUtils.getCurrentMessage(); + boolean enableUnsignedJwt = + MessageUtils.getContextualBoolean(m, JoseConstants.ENABLE_UNSIGNED_JWT_PRINCIPAL, false); + + // The token must be signed/verified with a public key to set up the security context, + // unless we directly configure otherwise + if (isVerifiedWithAPublicKey(jwt) || enableUnsignedJwt) { + return new JwtTokenSecurityContext(jwt, roleClaim); + } + return null; + } + + private boolean isVerifiedWithAPublicKey(JwtToken jwt) { + if (isJwsRequired()) { + String alg = (String)jwt.getHeader(JoseConstants.HEADER_ALGORITHM); + SignatureAlgorithm sigAlg = SignatureAlgorithm.getAlgorithm(alg); + return SignatureAlgorithm.isPublicKeyAlgorithm(sigAlg); + } + + return false; + } + + + public void setExpectedAuthScheme(String expectedAuthScheme) { + this.expectedAuthScheme = expectedAuthScheme; + } + + @Override + protected void validateToken(JwtToken jwt) { + // If we have no issued time then we need to have an expiry + boolean expiredRequired = jwt.getClaims().getIssuedAt() == null; + JwtUtils.validateJwtExpiry(jwt.getClaims(), clockOffset, expiredRequired); + + JwtUtils.validateJwtNotBefore(jwt.getClaims(), clockOffset, false); + + // If we have no expiry then we must have an issued at + boolean issuedAtRequired = jwt.getClaims().getExpiryTime() == null; + JwtUtils.validateJwtIssuedAt(jwt.getClaims(), ttl, clockOffset, issuedAtRequired); + } + + public int getClockOffset() { + return clockOffset; + } + + public void setClockOffset(int clockOffset) { + this.clockOffset = clockOffset; + } + + public int getTtl() { + return ttl; + } + + public void setTtl(int ttl) { + this.ttl = ttl; + } + + public String getRoleClaim() { + return roleClaim; + } + + public void setRoleClaim(String roleClaim) { + this.roleClaim = roleClaim; + } +}
http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtTokenSecurityContext.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtTokenSecurityContext.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtTokenSecurityContext.java new file mode 100644 index 0000000..427ad73 --- /dev/null +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtTokenSecurityContext.java @@ -0,0 +1,81 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.jaxrs; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.security.auth.Subject; + +import org.apache.cxf.common.security.SimpleGroup; +import org.apache.cxf.common.security.SimplePrincipal; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.security.LoginSecurityContext; + +public class JwtTokenSecurityContext implements LoginSecurityContext { + private final JwtToken token; + private final Principal principal; + private final Set<Principal> roles; + + public JwtTokenSecurityContext(JwtToken jwt, String roleClaim) { + principal = new SimplePrincipal(jwt.getClaims().getSubject()); + this.token = jwt; + if (roleClaim != null && jwt.getClaims().containsProperty(roleClaim)) { + roles = new HashSet<Principal>(); + String role = jwt.getClaims().getStringProperty(roleClaim).trim(); + for (String r : role.split(",")) { + roles.add(new SimpleGroup(r)); + } + } else { + roles = Collections.emptySet(); + } + } + + public JwtToken getToken() { + return token; + } + + @Override + public Subject getSubject() { + return null; + } + + @Override + public Set<Principal> getUserRoles() { + return Collections.unmodifiableSet(roles); + } + + @Override + public Principal getUserPrincipal() { + return principal; + } + + @Override + public boolean isUserInRole(String role) { + for (Principal principalRole : roles) { + if (principalRole != principal && principalRole.getName().equals(role)) { + return true; + } + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/Priorities.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/Priorities.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/Priorities.java new file mode 100644 index 0000000..877ff0c --- /dev/null +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/Priorities.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.jaxrs; + +public final class Priorities { + public static final int JWE_SERVER_READ_PRIORITY = 1001; + public static final int JWS_SERVER_READ_PRIORITY = 1002; + + public static final int JWE_WRITE_PRIORITY = 1001; + public static final int JWS_WRITE_PRIORITY = 1002; + + public static final int JWE_CLIENT_READ_PRIORITY = 1002; + public static final int JWS_CLIENT_READ_PRIORITY = 1001; + + private Priorities() { + + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/pom.xml ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/pom.xml b/rt/rs/security/jose-parent/jose/pom.xml new file mode 100644 index 0000000..e1d878b --- /dev/null +++ b/rt/rs/security/jose-parent/jose/pom.xml @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <artifactId>cxf-rt-rs-security-jose</artifactId> + <packaging>bundle</packaging> + <name>Apache CXF Runtime JOSE Core</name> + <description>Apache CXF Runtime JOSE Core</description> + <url>http://cxf.apache.org</url> + <parent> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-parent</artifactId> + <version>3.0.7-SNAPSHOT</version> + <relativePath>../../../../../parent/pom.xml</relativePath> + </parent> + <dependencies> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-security</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-json-basic</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcprov-ext-jdk15on</artifactId> + <version>${cxf.bcprov.version}</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <!--test dependencies--> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/AbstractJoseConsumer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/AbstractJoseConsumer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/AbstractJoseConsumer.java new file mode 100644 index 0000000..ddf1d4f --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/AbstractJoseConsumer.java @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.common; + +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweUtils; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; + +public abstract class AbstractJoseConsumer { + private JweDecryptionProvider jweDecryptor; + private JwsSignatureVerifier jwsVerifier; + + public void setJweDecryptor(JweDecryptionProvider jweDecryptor) { + this.jweDecryptor = jweDecryptor; + } + + public JweDecryptionProvider getJweDecryptor() { + return jweDecryptor; + } + + public void setJwsVerifier(JwsSignatureVerifier theJwsVerifier) { + this.jwsVerifier = theJwsVerifier; + } + + public JwsSignatureVerifier getJwsVerifier() { + return jwsVerifier; + } + + protected JweDecryptionProvider getInitializedDecryptionProvider() { + if (jweDecryptor != null) { + return jweDecryptor; + } + return JweUtils.loadDecryptionProvider(false); + } + protected JwsSignatureVerifier getInitializedSignatureVerifier() { + if (jwsVerifier != null) { + return jwsVerifier; + } + + return JwsUtils.loadSignatureVerifier(false); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/AbstractJoseProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/AbstractJoseProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/AbstractJoseProducer.java new file mode 100644 index 0000000..fe9832f --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/AbstractJoseProducer.java @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.common; + +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweUtils; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; + +public abstract class AbstractJoseProducer { + private JwsSignatureProvider sigProvider; + private JweEncryptionProvider encryptionProvider; + + protected JwsSignatureProvider getInitializedSignatureProvider() { + if (sigProvider != null) { + return sigProvider; + } + + return JwsUtils.loadSignatureProvider(false); + } + protected JweEncryptionProvider getInitializedEncryptionProvider() { + if (encryptionProvider != null) { + return encryptionProvider; + } + return JweUtils.loadEncryptionProvider(false); + } + + public void setEncryptionProvider(JweEncryptionProvider encryptionProvider) { + this.encryptionProvider = encryptionProvider; + } + + public void setSignatureProvider(JwsSignatureProvider signatureProvider) { + this.sigProvider = signatureProvider; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java new file mode 100644 index 0000000..f2c0b75 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java @@ -0,0 +1,229 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.rs.security.jose.common; + +public final class JoseConstants { + public static final String HEADER_TYPE = "typ"; + public static final String HEADER_ALGORITHM = "alg"; + public static final String HEADER_CONTENT_TYPE = "cty"; + public static final String HEADER_CRITICAL = "crit"; + + public static final String HEADER_KEY_ID = "kid"; + public static final String HEADER_X509_URL = "x5u"; + public static final String HEADER_X509_CHAIN = "x5c"; + public static final String HEADER_X509_THUMBPRINT = "x5t"; + public static final String HEADER_X509_THUMBPRINT_SHA256 = "x5t#S256"; + public static final String HEADER_JSON_WEB_KEY = "jwk"; + public static final String HEADER_JSON_WEB_KEY_SET = "jku"; + + public static final String JWE_HEADER_KEY_ENC_ALGORITHM = HEADER_ALGORITHM; + public static final String JWE_HEADER_CONTENT_ENC_ALGORITHM = "enc"; + public static final String JWE_HEADER_ZIP_ALGORITHM = "zip"; + public static final String JWE_DEFLATE_ZIP_ALGORITHM = "DEF"; + + public static final String JWS_HEADER_B64_STATUS_HEADER = "b64"; + + public static final String TYPE_JWT = "JWT"; + public static final String TYPE_JOSE = "JOSE"; + public static final String TYPE_JOSE_JSON = "JOSE+JSON"; + public static final String MEDIA_TYPE_JOSE = "application/jose"; + public static final String MEDIA_TYPE_JOSE_JSON = "application/jose+json"; + + public static final String JOSE_CONTEXT_PROPERTY = "org.apache.cxf.jose.context"; + + // + // JOSE Configuration constants + // + + // + // Shared Keys/keystore configuration + // + + /** + * The keystore type. Suitable values are "jks" or "jwk". + */ + public static final String RSSEC_KEY_STORE_TYPE = "rs.security.keystore.type"; + + /** + * The password required to access the keystore. + */ + public static final String RSSEC_KEY_STORE_PSWD = "rs.security.keystore.password"; + + /** + * The password required to access the private key (in the keystore). + */ + public static final String RSSEC_KEY_PSWD = "rs.security.key.password"; + + /** + * The keystore alias corresponding to the key to use + */ + public static final String RSSEC_KEY_STORE_ALIAS = "rs.security.keystore.alias"; + + /** + * The keystore aliases corresponding to the keys to use, when using the JSON serialization form. + */ + public static final String RSSEC_KEY_STORE_ALIASES = "rs.security.keystore.aliases"; + + /** + * The path to the keystore file. + */ + public static final String RSSEC_KEY_STORE_FILE = "rs.security.keystore.file"; + + /** + * A reference to a PrivateKeyPasswordProvider instance used to retrieve passwords to access keys. + */ + public static final String RSSEC_KEY_PSWD_PROVIDER = "rs.security.key.password.provider"; + + /** + * TODO documentation for these + */ + public static final String RSSEC_REPORT_KEY_PROP = "rs.security.report.public.key"; + public static final String RSSEC_REPORT_KEY_ID_PROP = "rs.security.report.public.key.id"; + public static final String RSSEC_ACCEPT_PUBLIC_KEY_PROP = "rs.security.accept.public.key.properties"; + public static final String RSSEC_KEY_STORE_JWKSET = "rs.security.keystore.jwkset"; + public static final String RSSEC_KEY_STORE_JWKKEY = "rs.security.keystore.jwkkey"; + + // + // JWS specific Configuration + // + + /** + * A reference to a PrivateKeyPasswordProvider instance used to retrieve passwords to access keys + * for signature. If this is not specified it falls back to use the RSSEC_KEY_PSWD_PROVIDER. + */ + public static final String RSSEC_SIGNATURE_KEY_PSWD_PROVIDER = "rs.security.signature.key.password.provider"; + + /** + * The signature algorithm to use. The default algorithm if not specified is 'RS256'. + */ + public static final String RSSEC_SIGNATURE_ALGORITHM = "rs.security.signature.algorithm"; + + /** + * The OLD signature algorithm identifier. Use RSSEC_SIGNATURE_ALGORITHM instead. + */ + @Deprecated + public static final String DEPR_RSSEC_SIGNATURE_ALGORITHM = "rs.security.jws.content.signature.algorithm"; + + /** + * The signature properties file for signature creation. If not specified then it falls back to + * RSSEC_SIGNATURE_PROPS. + */ + public static final String RSSEC_SIGNATURE_OUT_PROPS = "rs.security.signature.out.properties"; + + /** + * The signature properties file for signature verification. If not specified then it falls back to + * RSSEC_SIGNATURE_PROPS. + */ + public static final String RSSEC_SIGNATURE_IN_PROPS = "rs.security.signature.in.properties"; + + /** + * The signature properties file for signature creation/verification. + */ + public static final String RSSEC_SIGNATURE_PROPS = "rs.security.signature.properties"; + + /** + * TODO documentation for these + */ + public static final String RSSEC_SIGNATURE_REPORT_KEY_PROP = "rs.security.signature.report.public.key"; + public static final String RSSEC_SIGNATURE_REPORT_KEY_ID_PROP = "rs.security.signature.report.public.key.id"; + + // + // JWE specific Configuration + // + + /** + * A reference to a PrivateKeyPasswordProvider instance used to retrieve passwords to access keys + * for decryption. If this is not specified it falls back to use the RSSEC_KEY_PSWD_PROVIDER. + */ + public static final String RSSEC_DECRYPTION_KEY_PSWD_PROVIDER = "rs.security.decryption.key.password.provider"; + + /** + * The encryption content algorithm to use. The default algorithm if not specified is 'A128GCM'. + */ + public static final String RSSEC_ENCRYPTION_CONTENT_ALGORITHM = "rs.security.encryption.content.algorithm"; + + /** + * The OLD encryption content algorithm to use. Use RSSEC_ENCRYPTION_CONTENT_ALGORITHM instead. + */ + @Deprecated + public static final String DEPR_RSSEC_ENCRYPTION_CONTENT_ALGORITHM = + "rs.security.jwe.content.encryption.algorithm"; + + /** + * The encryption key algorithm to use. The default algorithm if not specified is 'RSA-OAEP' if the key is an + * RSA key, and 'A128GCMKW' if it is an octet sequence. + */ + public static final String RSSEC_ENCRYPTION_KEY_ALGORITHM = "rs.security.encryption.key.algorithm"; + + /** + * The OLD encryption key algorithm to use. Use RSSEC_ENCRYPTION_KEY_ALGORITHM instead. + */ + @Deprecated + public static final String DEPR_RSSEC_ENCRYPTION_KEY_ALGORITHM = "rs.security.jwe.key.encryption.algorithm"; + + /** + * The encryption zip algorithm to use. + */ + public static final String RSSEC_ENCRYPTION_ZIP_ALGORITHM = "rs.security.encryption.zip.algorithm"; + + /** + * The OLD encryption zip algorithm to use. Use RSSEC_ENCRYPTION_ZIP_ALGORITHM instead. + */ + @Deprecated + public static final String DEPR_RSSEC_ENCRYPTION_ZIP_ALGORITHM = "rs.security.jwe.zip.algorithm"; + + /** + * The encryption properties file for encryption creation. If not specified then it falls back to + * RSSEC_ENCRYPTION_PROPS. + */ + public static final String RSSEC_ENCRYPTION_OUT_PROPS = "rs.security.encryption.out.properties"; + + /** + * The decryption properties file for decryption. If not specified then it falls back to + * RSSEC_ENCRYPTION_PROPS. + */ + public static final String RSSEC_ENCRYPTION_IN_PROPS = "rs.security.encryption.in.properties"; + + /** + * The encryption/decryption properties file + */ + public static final String RSSEC_ENCRYPTION_PROPS = "rs.security.encryption.properties"; + + /** + * TODO documentation for these + */ + public static final String RSSEC_ENCRYPTION_REPORT_KEY_PROP = "rs.security.encryption.report.public.key"; + public static final String RSSEC_ENCRYPTION_REPORT_KEY_ID_PROP = "rs.security.encryption.report.public.key.id"; + + // + // JWT specific configuration + // + + /** + * Whether to allow unsigned JWT tokens as SecurityContext Principals. The default is false. + */ + public static final String ENABLE_UNSIGNED_JWT_PRINCIPAL = "rs.security.enable.unsigned-jwt.principal"; + + + + private JoseConstants() { + + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseException.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseException.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseException.java new file mode 100644 index 0000000..ffaf2d1 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseException.java @@ -0,0 +1,33 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.common; + +public class JoseException extends RuntimeException { + + private static final long serialVersionUID = 4118589816228511524L; + public JoseException() { + + } + public JoseException(String error) { + super(error); + } + public JoseException(Throwable cause) { + super(cause); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseHeaders.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseHeaders.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseHeaders.java new file mode 100644 index 0000000..3160232 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseHeaders.java @@ -0,0 +1,179 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.rs.security.jose.common; + +import java.util.List; +import java.util.Map; + +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.jaxrs.json.basic.JsonMapObject; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; + +public abstract class JoseHeaders extends JsonMapObject { + public JoseHeaders() { + } + + public JoseHeaders(JoseType type) { + init(type); + } + + public JoseHeaders(JoseHeaders headers) { + this(headers.asMap()); + } + + public JoseHeaders(Map<String, Object> values) { + super(values); + } + private void init(JoseType type) { + setType(type); + } + public void setType(JoseType type) { + setHeader(JoseConstants.HEADER_TYPE, type.toString()); + } + + public JoseType getType() { + Object prop = getHeader(JoseConstants.HEADER_TYPE); + return prop == null ? null : JoseType.getType(prop.toString()); + } + + public void setContentType(String type) { + setHeader(JoseConstants.HEADER_CONTENT_TYPE, type); + } + + public String getContentType() { + return (String)getHeader(JoseConstants.HEADER_CONTENT_TYPE); + } + + public void setAlgorithm(String algo) { + setHeader(JoseConstants.HEADER_ALGORITHM, algo); + } + + public String getAlgorithm() { + Object prop = getHeader(JoseConstants.HEADER_ALGORITHM); + return prop == null ? null : prop.toString(); + } + + public void setKeyId(String kid) { + setHeader(JoseConstants.HEADER_KEY_ID, kid); + } + + public String getKeyId() { + return (String)getHeader(JoseConstants.HEADER_KEY_ID); + } + + public void setX509Url(String x509Url) { + setHeader(JoseConstants.HEADER_X509_URL, x509Url); + } + + public String getX509Url() { + return (String)getHeader(JoseConstants.HEADER_X509_URL); + } + + public void setX509Chain(List<String> x509Chain) { + setProperty(JoseConstants.HEADER_X509_CHAIN, x509Chain); + } + + public List<String> getX509Chain() { + return CastUtils.cast((List<?>)getProperty(JoseConstants.HEADER_X509_CHAIN)); + } + + public void setX509Thumbprint(String x509Thumbprint) { + setHeader(JoseConstants.HEADER_X509_THUMBPRINT, x509Thumbprint); + } + + public String getX509Thumbprint() { + return (String)getHeader(JoseConstants.HEADER_X509_THUMBPRINT); + } + + public void setX509ThumbprintSHA256(String x509Thumbprint) { + setHeader(JoseConstants.HEADER_X509_THUMBPRINT_SHA256, x509Thumbprint); + } + + public String getX509ThumbprintSHA256() { + return (String)getHeader(JoseConstants.HEADER_X509_THUMBPRINT_SHA256); + } + + public void setCritical(List<String> crit) { + setHeader(JoseConstants.HEADER_CRITICAL, crit); + } + + public List<String> getCritical() { + return CastUtils.cast((List<?>)getHeader(JoseConstants.HEADER_CRITICAL)); + } + + public void setJsonWebKey(JsonWebKey key) { + setJsonWebKey(JoseConstants.HEADER_JSON_WEB_KEY, key); + } + + public void setJsonWebKey(String headerName, JsonWebKey key) { + setHeader(headerName, key); + } + + public void setJsonWebKeysUrl(String url) { + setHeader(JoseConstants.HEADER_JSON_WEB_KEY_SET, url); + } + + public String getJsonWebKeysUrl() { + return (String)getHeader(JoseConstants.HEADER_JSON_WEB_KEY_SET); + } + + public JsonWebKey getJsonWebKey() { + return getJsonWebKey(JoseConstants.HEADER_JSON_WEB_KEY); + } + public JsonWebKey getJsonWebKey(String headerName) { + Object jsonWebKey = getHeader(headerName); + if (jsonWebKey == null || jsonWebKey instanceof JsonWebKey) { + return (JsonWebKey)jsonWebKey; + } + Map<String, Object> map = CastUtils.cast((Map<?, ?>)jsonWebKey); + return new JsonWebKey(map); + } + + public JoseHeaders setHeader(String name, Object value) { + setProperty(name, value); + return this; + } + + public Object getHeader(String name) { + return getProperty(name); + } + + public JoseHeaders setIntegerHeader(String name, Integer value) { + setHeader(name, value); + return this; + } + + public Integer getIntegerHeader(String name) { + return getIntegerProperty(name); + } + public JoseHeaders setLongHeader(String name, Long value) { + setHeader(name, value); + return this; + } + + public Long getLongHeader(String name) { + return getLongProperty(name); + } + + public boolean containsHeader(String name) { + return containsProperty(name); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseType.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseType.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseType.java new file mode 100644 index 0000000..595a3f7 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseType.java @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.common; + + +public enum JoseType { + JOSE(JoseConstants.TYPE_JOSE), + JOSE_JSON(JoseConstants.TYPE_JOSE_JSON), + JWT(JoseConstants.TYPE_JWT); + + private final String type; + private JoseType(String type) { + this.type = type; + } + public static JoseType getType(String type) { + if (type == null) { + return null; + } else if (JoseConstants.TYPE_JOSE_JSON.equals(type)) { + return JOSE_JSON; + } else { + return valueOf(type); + } + } + public String toString() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseUtils.java new file mode 100644 index 0000000..7c2f4eb --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseUtils.java @@ -0,0 +1,200 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.common; + +import java.io.File; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.logging.Logger; + +import org.apache.cxf.Bus; +import org.apache.cxf.common.classloader.ClassLoaderUtils; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.PhaseInterceptorChain; +import org.apache.cxf.resource.ResourceManager; +import org.apache.cxf.rt.security.crypto.CryptoUtils; + +public final class JoseUtils { + private static final Logger LOG = LogUtils.getL7dLogger(JoseUtils.class); + private static final String CLASSPATH_PREFIX = "classpath:"; + + private JoseUtils() { + + } + public static String[] getCompactParts(String compactContent) { + if (compactContent.startsWith("\"") && compactContent.endsWith("\"")) { + compactContent = compactContent.substring(1, compactContent.length() - 1); + } + return StringUtils.split(compactContent, "\\."); + } + public static void setJoseContextProperty(JoseHeaders headers) { + Message message = PhaseInterceptorChain.getCurrentMessage(); + String context = (String)message.get(JoseConstants.JOSE_CONTEXT_PROPERTY); + if (context != null) { + headers.setHeader(JoseConstants.JOSE_CONTEXT_PROPERTY, context); + } + } + public static void setJoseMessageContextProperty(JoseHeaders headers, String value) { + headers.setHeader(JoseConstants.JOSE_CONTEXT_PROPERTY, value); + Message message = PhaseInterceptorChain.getCurrentMessage(); + message.put(JoseConstants.JOSE_CONTEXT_PROPERTY, value); + } + public static void setMessageContextProperty(JoseHeaders headers) { + String context = (String)headers.getHeader(JoseConstants.JOSE_CONTEXT_PROPERTY); + if (context != null) { + Message message = PhaseInterceptorChain.getCurrentMessage(); + message.put(JoseConstants.JOSE_CONTEXT_PROPERTY, context); + } + } + public static void validateRequestContextProperty(JoseHeaders headers) { + Message message = PhaseInterceptorChain.getCurrentMessage(); + Object requestContext = message.get(JoseConstants.JOSE_CONTEXT_PROPERTY); + Object headerContext = headers.getHeader(JoseConstants.JOSE_CONTEXT_PROPERTY); + if (requestContext == null && headerContext == null) { + return; + } + if (requestContext == null && headerContext != null + || requestContext != null && headerContext == null + || !requestContext.equals(headerContext)) { + LOG.warning("Invalid JOSE context property"); + throw new JoseException(); + } + } + + public static String checkContentType(String contentType, String defaultType) { + if (contentType != null) { + int paramIndex = contentType.indexOf(';'); + String typeWithoutParams = paramIndex == -1 ? contentType : contentType.substring(0, paramIndex); + if (typeWithoutParams.indexOf('/') == -1) { + contentType = "application/" + contentType; + } + } else { + contentType = defaultType; + } + return contentType; + } + public static String expandContentType(String contentType) { + int paramIndex = contentType.indexOf(';'); + String typeWithoutParams = paramIndex == -1 ? contentType : contentType.substring(0, paramIndex); + if (typeWithoutParams.indexOf('/') == -1) { + contentType = "application/" + contentType; + } + return contentType; + } + + public static String decodeToString(String encoded) { + try { + return new String(decode(encoded), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new JoseException(ex); + } + + } + public static byte[] decode(String encoded) { + return CryptoUtils.decodeSequence(encoded); + } + + public static boolean validateCriticalHeaders(JoseHeaders headers) { + List<String> critical = headers.getCritical(); + if (critical == null) { + return true; + } + // The "crit" value MUST NOT be empty "[]" or contain either duplicate values or "crit" + if (critical.isEmpty() + || detectDoubleEntry(critical) + || critical.contains(JoseConstants.HEADER_CRITICAL)) { + return false; + } + + // Check that the headers contain these critical headers + return headers.asMap().keySet().containsAll(critical); + } + private static boolean detectDoubleEntry(List<?> list) { + Set<Object> inputSet = new HashSet<Object>(list); + return list.size() > inputSet.size(); + } + + // + // <Start> Copied from JAX-RS RT FRONTEND ResourceUtils + // + + public static InputStream getResourceStream(String loc, Bus bus) throws Exception { + URL url = getResourceURL(loc, bus); + return url == null ? null : url.openStream(); + } + + public static URL getResourceURL(String loc, Bus bus) throws Exception { + URL url = null; + if (loc.startsWith(CLASSPATH_PREFIX)) { + String path = loc.substring(CLASSPATH_PREFIX.length()); + url = JoseUtils.getClasspathResourceURL(path, JoseUtils.class, bus); + } else { + try { + url = new URL(loc); + } catch (Exception ex) { + // it can be either a classpath or file resource without a scheme + url = JoseUtils.getClasspathResourceURL(loc, JoseUtils.class, bus); + if (url == null) { + File file = new File(loc); + if (file.exists()) { + url = file.toURI().toURL(); + } + } + } + } + if (url == null) { + LOG.warning("No resource " + loc + " is available"); + } + return url; + } + + public static URL getClasspathResourceURL(String path, Class<?> callingClass, Bus bus) { + URL url = ClassLoaderUtils.getResource(path, callingClass); + return url == null ? getResource(path, URL.class, bus) : url; + } + + public static <T> T getResource(String path, Class<T> resourceClass, Bus bus) { + if (bus != null) { + ResourceManager rm = bus.getExtension(ResourceManager.class); + if (rm != null) { + return rm.resolveResource(path, resourceClass); + } + } + return null; + } + + public static Properties loadProperties(String propertiesLocation, Bus bus) throws Exception { + Properties props = new Properties(); + InputStream is = getResourceStream(propertiesLocation, bus); + props.load(is); + return props; + } + + // + // <End> Copied from JAX-RS RT FRONTEND ResourceUtils + // + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java new file mode 100644 index 0000000..0c32919 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java @@ -0,0 +1,366 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.rs.security.jose.common; + +import java.io.InputStream; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.CertPath; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertPathBuilderResult; +import java.security.cert.CertPathValidator; +import java.security.cert.CertStore; +import java.security.cert.Certificate; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.logging.Logger; + +import org.apache.cxf.Bus; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; +import org.apache.cxf.phase.PhaseInterceptorChain; +import org.apache.cxf.rs.security.jose.jwk.KeyOperation; +import org.apache.cxf.rt.security.crypto.CryptoUtils; + +/** + * Encryption helpers + */ +public final class KeyManagementUtils { + private static final Logger LOG = LogUtils.getL7dLogger(KeyManagementUtils.class); + + private KeyManagementUtils() { + } + public static List<String> loadAndEncodeX509CertificateOrChain(Message m, Properties props) { + X509Certificate[] chain = loadX509CertificateOrChain(m, props); + return encodeX509CertificateChain(chain); + } + public static X509Certificate[] loadX509CertificateOrChain(Message m, Properties props) { + KeyStore keyStore = KeyManagementUtils.loadPersistKeyStore(m, props); + String alias = props.getProperty(JoseConstants.RSSEC_KEY_STORE_ALIAS); + return loadX509CertificateOrChain(keyStore, alias); + } + private static X509Certificate[] loadX509CertificateOrChain(KeyStore keyStore, String alias) { + try { + Certificate[] certs = keyStore.getCertificateChain(alias); + if (certs != null) { + return Arrays.copyOf(certs, certs.length, X509Certificate[].class); + } else { + return new X509Certificate[]{(X509Certificate)CryptoUtils.loadCertificate(keyStore, alias)}; + } + } catch (Exception ex) { + LOG.warning("X509 Certificates can not be created"); + throw new JoseException(ex); + } + } + + public static PublicKey loadPublicKey(Message m, Properties props) { + KeyStore keyStore = KeyManagementUtils.loadPersistKeyStore(m, props); + return CryptoUtils.loadPublicKey(keyStore, props.getProperty(JoseConstants.RSSEC_KEY_STORE_ALIAS)); + } + public static PublicKey loadPublicKey(Message m, String keyStoreLocProp) { + return loadPublicKey(m, keyStoreLocProp, null); + } + public static PublicKey loadPublicKey(Message m, String keyStoreLocPropPreferred, String keyStoreLocPropDefault) { + String keyStoreLoc = getMessageProperty(m, keyStoreLocPropPreferred, keyStoreLocPropDefault); + Bus bus = m.getExchange().getBus(); + try { + Properties props = JoseUtils.loadProperties(keyStoreLoc, bus); + return KeyManagementUtils.loadPublicKey(m, props); + } catch (Exception ex) { + LOG.warning("Public key can not be loaded"); + throw new JoseException(ex); + } + } + private static String getMessageProperty(Message m, String keyStoreLocPropPreferred, + String keyStoreLocPropDefault) { + String propLoc = + (String)MessageUtils.getContextualProperty(m, keyStoreLocPropPreferred, keyStoreLocPropDefault); + if (propLoc == null) { + LOG.warning("Properties resource is not identified"); + throw new JoseException(); + } + return propLoc; + } + private static PrivateKey loadPrivateKey(KeyStore keyStore, + Message m, + Properties props, + Bus bus, + PrivateKeyPasswordProvider provider, + KeyOperation keyOper, + String alias) { + + String keyPswd = props.getProperty(JoseConstants.RSSEC_KEY_PSWD); + String theAlias = alias != null ? alias : getKeyId(m, props, JoseConstants.RSSEC_KEY_STORE_ALIAS, keyOper); + char[] keyPswdChars = provider != null ? provider.getPassword(props) + : keyPswd != null ? keyPswd.toCharArray() : null; + return CryptoUtils.loadPrivateKey(keyStore, keyPswdChars, theAlias); + } + + public static PrivateKey loadPrivateKey(Message m, String keyStoreLocProp, KeyOperation keyOper) { + return loadPrivateKey(m, keyStoreLocProp, null, keyOper); + } + public static PrivateKey loadPrivateKey(Message m, String keyStoreLocPropPreferred, + String keyStoreLocPropDefault, KeyOperation keyOper) { + String keyStoreLoc = getMessageProperty(m, keyStoreLocPropPreferred, keyStoreLocPropDefault); + Bus bus = m.getExchange().getBus(); + try { + Properties props = JoseUtils.loadProperties(keyStoreLoc, bus); + return loadPrivateKey(m, props, keyOper); + } catch (Exception ex) { + throw new SecurityException(ex); + } + } + + public static String getKeyId(Message m, Properties props, + String preferredPropertyName, + KeyOperation keyOper) { + String kid = null; + String altPropertyName = null; + if (keyOper != null) { + if (keyOper == KeyOperation.ENCRYPT || keyOper == KeyOperation.DECRYPT) { + altPropertyName = preferredPropertyName + ".jwe"; + } else if (keyOper == KeyOperation.SIGN || keyOper == KeyOperation.VERIFY) { + altPropertyName = preferredPropertyName + ".jws"; + } + String direction = m.getExchange().getOutMessage() == m ? ".out" : ".in"; + kid = (String)MessageUtils.getContextualProperty(m, preferredPropertyName, altPropertyName + direction); + // Check whether the direction is not set for the altPropertyName + if (kid == null && altPropertyName != null) { + kid = (String)m.getContextualProperty(altPropertyName); + } + } + + if (kid == null) { + kid = props.getProperty(preferredPropertyName); + } + if (kid == null && altPropertyName != null) { + kid = props.getProperty(altPropertyName); + } + return kid; + } + public static PrivateKeyPasswordProvider loadPasswordProvider(Message m, Properties props, KeyOperation keyOper) { + PrivateKeyPasswordProvider cb = null; + if (keyOper != null) { + String propName = keyOper == KeyOperation.SIGN ? JoseConstants.RSSEC_SIGNATURE_KEY_PSWD_PROVIDER + : keyOper == KeyOperation.DECRYPT + ? JoseConstants.RSSEC_DECRYPTION_KEY_PSWD_PROVIDER : null; + if (propName != null) { + cb = (PrivateKeyPasswordProvider)m.getContextualProperty(propName); + } + } + if (cb == null) { + cb = (PrivateKeyPasswordProvider)m.getContextualProperty(JoseConstants.RSSEC_KEY_PSWD_PROVIDER); + } + return cb; + } + + public static PrivateKey loadPrivateKey(Message m, Properties props, KeyOperation keyOper) { + KeyStore keyStore = loadPersistKeyStore(m, props); + return loadPrivateKey(keyStore, m, props, keyOper, null); + } + private static PrivateKey loadPrivateKey(KeyStore keyStore, Message m, Properties props, KeyOperation keyOper, + String alias) { + Bus bus = m.getExchange().getBus(); + PrivateKeyPasswordProvider cb = loadPasswordProvider(m, props, keyOper); + return loadPrivateKey(keyStore, m, props, bus, cb, keyOper, alias); + } + public static KeyStore loadPersistKeyStore(Message m, Properties props) { + if (!props.containsKey(JoseConstants.RSSEC_KEY_STORE_FILE)) { + LOG.warning("No keystore file has been configured"); + throw new JoseException("No keystore file has been configured"); + } + KeyStore keyStore = (KeyStore)m.getExchange().get(props.get(JoseConstants.RSSEC_KEY_STORE_FILE)); + if (keyStore == null) { + keyStore = loadKeyStore(props, m.getExchange().getBus()); + m.getExchange().put((String)props.get(JoseConstants.RSSEC_KEY_STORE_FILE), keyStore); + } + return keyStore; + } + public static KeyStore loadKeyStore(Properties props, Bus bus) { + String keyStoreType = props.getProperty(JoseConstants.RSSEC_KEY_STORE_TYPE); + String keyStoreLoc = props.getProperty(JoseConstants.RSSEC_KEY_STORE_FILE); + String keyStorePswd = props.getProperty(JoseConstants.RSSEC_KEY_STORE_PSWD); + + if (keyStorePswd == null) { + throw new JoseException("No keystore password was defined"); + } + try { + InputStream is = JoseUtils.getResourceStream(keyStoreLoc, bus); + return CryptoUtils.loadKeyStore(is, keyStorePswd.toCharArray(), keyStoreType); + } catch (Exception ex) { + LOG.warning("Key store can not be loaded"); + throw new JoseException(ex); + } + } + public static List<String> encodeX509CertificateChain(X509Certificate[] chain) { + return encodeX509CertificateChain(Arrays.asList(chain)); + } + public static List<String> encodeX509CertificateChain(List<X509Certificate> chain) { + List<String> encodedChain = new ArrayList<String>(chain.size()); + for (X509Certificate cert : chain) { + try { + encodedChain.add(CryptoUtils.encodeCertificate(cert)); + } catch (Exception ex) { + LOG.warning("X509 Certificate can not be encoded"); + throw new JoseException(ex); + } + } + return encodedChain; + } + public static List<X509Certificate> toX509CertificateChain(List<String> base64EncodedChain) { + if (base64EncodedChain != null) { + List<X509Certificate> certs = new ArrayList<X509Certificate>(base64EncodedChain.size()); + for (String encodedCert : base64EncodedChain) { + try { + certs.add((X509Certificate)CryptoUtils.decodeCertificate(encodedCert)); + } catch (Exception ex) { + LOG.warning("X509 Certificate can not be decoded"); + throw new JoseException(ex); + } + } + return certs; + } else { + return null; + } + } + //TODO: enhance the certificate validation code + public static void validateCertificateChain(Properties storeProperties, List<X509Certificate> inCerts) { + Message message = PhaseInterceptorChain.getCurrentMessage(); + KeyStore ks = loadPersistKeyStore(message, storeProperties); + validateCertificateChain(ks, inCerts); + } + public static void validateCertificateChain(KeyStore ks, List<X509Certificate> inCerts) { + // Initial chain validation, to be enhanced as needed + try { + X509CertSelector certSelect = new X509CertSelector(); + certSelect.setCertificate((X509Certificate) inCerts.get(0)); + PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ks, certSelect); + pbParams.addCertStore(CertStore.getInstance("Collection", + new CollectionCertStoreParameters(inCerts))); + pbParams.setMaxPathLength(-1); + pbParams.setRevocationEnabled(false); + CertPathBuilderResult buildResult = CertPathBuilder.getInstance("PKIX").build(pbParams); + CertPath certPath = buildResult.getCertPath(); + CertPathValidator.getInstance("PKIX").validate(certPath, pbParams); + } catch (Exception ex) { + LOG.warning("Certificate path validation error"); + throw new JoseException(ex); + } + } + public static X509Certificate[] toX509CertificateChainArray(List<String> base64EncodedChain) { + List<X509Certificate> chain = toX509CertificateChain(base64EncodedChain); + return chain == null ? null : chain.toArray(new X509Certificate[]{}); + } + public static String getKeyAlgorithm(Message m, Properties props, String propName, String defaultAlg) { + String algo = props.getProperty(propName); + if (algo == null) { + algo = (String)m.getContextualProperty(propName); + } + if (algo == null) { + algo = defaultAlg; + } + return algo; + } + + public static Properties loadStoreProperties(Message m, boolean required, + String storeProp1, String storeProp2) { + if (m == null) { + if (required) { + throw new JoseException(); + } + return null; + } + Properties props = null; + String propLoc = + (String)MessageUtils.getContextualProperty(m, storeProp1, storeProp2); + if (propLoc != null) { + try { + props = JoseUtils.loadProperties(propLoc, m.getExchange().getBus()); + } catch (Exception ex) { + LOG.warning("Properties resource is not identified"); + throw new JoseException(ex); + } + } else { + String keyFile = (String)m.getContextualProperty(JoseConstants.RSSEC_KEY_STORE_FILE); + if (keyFile != null) { + props = new Properties(); + props.setProperty(JoseConstants.RSSEC_KEY_STORE_FILE, keyFile); + String type = (String)m.getContextualProperty(JoseConstants.RSSEC_KEY_STORE_TYPE); + if (type == null) { + type = "jwk"; + } + props.setProperty(JoseConstants.RSSEC_KEY_STORE_TYPE, type); + String alias = (String)m.getContextualProperty(JoseConstants.RSSEC_KEY_STORE_ALIAS); + if (alias != null) { + props.setProperty(JoseConstants.RSSEC_KEY_STORE_ALIAS, alias); + } + String keystorePassword = (String)m.getContextualProperty(JoseConstants.RSSEC_KEY_STORE_PSWD); + if (keystorePassword != null) { + props.setProperty(JoseConstants.RSSEC_KEY_STORE_PSWD, keystorePassword); + } + String keyPassword = (String)m.getContextualProperty(JoseConstants.RSSEC_KEY_PSWD); + if (keyPassword != null) { + props.setProperty(JoseConstants.RSSEC_KEY_PSWD, keyPassword); + } + } + } + if (props == null) { + if (required) { + LOG.warning("Properties resource is not identified"); + throw new JoseException(); + } + props = new Properties(); + } + return props; + } + public static PrivateKey loadPrivateKey(Message m, Properties props, + List<X509Certificate> inCerts, + KeyOperation keyOper) { + KeyStore ks = loadPersistKeyStore(m, props); + + try { + String alias = ks.getCertificateAlias(inCerts.get(0)); + if (alias != null) { + for (Enumeration<String> e = ks.aliases(); e.hasMoreElements();) { + String currentAlias = e.nextElement(); + X509Certificate[] currentCertArray = loadX509CertificateOrChain(ks, currentAlias); + if (currentCertArray != null) { + alias = currentAlias; + break; + } + } + } + return loadPrivateKey(ks, m, props, keyOper, alias); + + } catch (Exception ex) { + LOG.warning("Private key can not be loaded"); + throw new JoseException(ex); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/PrivateKeyPasswordProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/PrivateKeyPasswordProvider.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/PrivateKeyPasswordProvider.java new file mode 100644 index 0000000..fc58ee5 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/PrivateKeyPasswordProvider.java @@ -0,0 +1,25 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.common; + +import java.util.Properties; + +public interface PrivateKeyPasswordProvider { + char[] getPassword(Properties storeProperties); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java new file mode 100644 index 0000000..76854ca --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java @@ -0,0 +1,271 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.rs.security.jose.jwa; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + + + + +public final class AlgorithmUtils { + public static final String AES = "AES"; + + // Key Encryption + // JWA + public static final String RSA_OAEP_ALGO = "RSA-OAEP"; + public static final String RSA_OAEP_256_ALGO = "RSA-OAEP-256"; + public static final String RSA1_5_ALGO = "RSA1_5"; + public static final String A128KW_ALGO = "A128KW"; + public static final String A192KW_ALGO = "A192KW"; + public static final String A256KW_ALGO = "A256KW"; + public static final String A128GCMKW_ALGO = "A128GCMKW"; + public static final String A192GCMKW_ALGO = "A192GCMKW"; + public static final String A256GCMKW_ALGO = "A256GCMKW"; + public static final String ECDH_ES_A128KW_ALGO = "ECDH-ES+A128KW"; + public static final String ECDH_ES_A192KW_ALGO = "ECDH-ES+A192KW"; + public static final String ECDH_ES_A256KW_ALGO = "ECDH-ES+A256KW"; + public static final String PBES2_HS256_A128KW_ALGO = "PBES2-HS256+A128KW"; + public static final String PBES2_HS384_A192KW_ALGO = "PBES2-HS384+A192KW"; + public static final String PBES2_HS512_A256KW_ALGO = "PBES2-HS512+A256KW"; + public static final String ECDH_ES_DIRECT_ALGO = "ECDH-ES"; + // Java + public static final String RSA_OAEP_ALGO_JAVA = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; + public static final String RSA_OAEP_256_ALGO_JAVA = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; + public static final String RSA_1_5_ALGO_JAVA = "RSA/ECB/PKCS1Padding"; + public static final String AES_WRAP_ALGO_JAVA = AES + "Wrap"; + // Content Encryption + // JWA + public static final String A128CBC_HS256_ALGO = "A128CBC-HS256"; + public static final String A192CBC_HS384_ALGO = "A192CBC-HS384"; + public static final String A256CBC_HS512_ALGO = "A256CBC-HS512"; + public static final String A128GCM_ALGO = "A128GCM"; + public static final String A192GCM_ALGO = "A192GCM"; + public static final String A256GCM_ALGO = "A256GCM"; + // Java + public static final String AES_GCM_ALGO_JAVA = AES + "/GCM/NoPadding"; + public static final String AES_CBC_ALGO_JAVA = AES + "/CBC/PKCS7Padding"; + // Signature + // JWA + public static final String HMAC_SHA_256_ALGO = "HS256"; + public static final String HMAC_SHA_384_ALGO = "HS384"; + public static final String HMAC_SHA_512_ALGO = "HS512"; + public static final String RS_SHA_256_ALGO = "RS256"; + public static final String RS_SHA_384_ALGO = "RS384"; + public static final String RS_SHA_512_ALGO = "RS512"; + public static final String PS_SHA_256_ALGO = "PS256"; + public static final String PS_SHA_384_ALGO = "PS384"; + public static final String PS_SHA_512_ALGO = "PS512"; + public static final String ES_SHA_256_ALGO = "ES256"; + public static final String ES_SHA_384_ALGO = "ES384"; + public static final String ES_SHA_512_ALGO = "ES512"; + public static final String NONE_TEXT_ALGO = "none"; + // Java + public static final String HMAC_SHA_256_JAVA = "HmacSHA256"; + public static final String HMAC_SHA_384_JAVA = "HmacSHA384"; + public static final String HMAC_SHA_512_JAVA = "HmacSHA512"; + public static final String RS_SHA_256_JAVA = "SHA256withRSA"; + public static final String RS_SHA_384_JAVA = "SHA384withRSA"; + public static final String RS_SHA_512_JAVA = "SHA512withRSA"; + public static final String PS_SHA_256_JAVA = "SHA256withRSAandMGF1"; + public static final String PS_SHA_384_JAVA = "SHA384withRSAandMGF1"; + public static final String PS_SHA_512_JAVA = "SHA512withRSAandMGF1"; + public static final String ES_SHA_256_JAVA = "SHA256withECDSA"; + public static final String ES_SHA_384_JAVA = "SHA384withECDSA"; + public static final String ES_SHA_512_JAVA = "SHA512withECDSA"; + + public static final Set<String> HMAC_SIGN_SET = new HashSet<String>(Arrays.asList(HMAC_SHA_256_ALGO, + HMAC_SHA_384_ALGO, + HMAC_SHA_512_ALGO)); + public static final Set<String> RSA_SHA_SIGN_SET = new HashSet<String>(Arrays.asList(RS_SHA_256_ALGO, + RS_SHA_384_ALGO, + RS_SHA_512_ALGO)); + public static final Set<String> RSA_SHA_PS_SIGN_SET = new HashSet<String>(Arrays.asList(PS_SHA_256_ALGO, + PS_SHA_384_ALGO, + PS_SHA_512_ALGO)); + public static final Set<String> EC_SHA_SIGN_SET = new HashSet<String>(Arrays.asList(ES_SHA_256_ALGO, + ES_SHA_384_ALGO, + ES_SHA_512_ALGO)); + public static final Set<String> RSA_CEK_SET = new HashSet<String>(Arrays.asList(RSA_OAEP_ALGO, + RSA_OAEP_256_ALGO, + RSA1_5_ALGO)); + public static final Set<String> AES_GCM_CEK_SET = new HashSet<String>(Arrays.asList(A128GCM_ALGO, + A192GCM_ALGO, + A256GCM_ALGO)); + public static final Set<String> AES_GCM_KW_SET = new HashSet<String>(Arrays.asList(A128GCMKW_ALGO, + A192GCMKW_ALGO, + A256GCMKW_ALGO)); + public static final Set<String> AES_KW_SET = new HashSet<String>(Arrays.asList(A128KW_ALGO, + A192KW_ALGO, + A256KW_ALGO)); + public static final Set<String> ACBC_HS_SET = new HashSet<String>(Arrays.asList(A128CBC_HS256_ALGO, + A192CBC_HS384_ALGO, + A256CBC_HS512_ALGO)); + public static final Set<String> PBES_HS_SET = new HashSet<String>(Arrays.asList(PBES2_HS256_A128KW_ALGO, + PBES2_HS384_A192KW_ALGO, + PBES2_HS512_A256KW_ALGO)); + public static final Set<String> ECDH_ES_WRAP_SET = new HashSet<String>(Arrays.asList(ECDH_ES_A128KW_ALGO, + ECDH_ES_A192KW_ALGO, + ECDH_ES_A256KW_ALGO)); + + private static final Map<String, String> JAVA_TO_JWA_NAMES; + private static final Map<String, String> JWA_TO_JAVA_NAMES; + static { + JAVA_TO_JWA_NAMES = new HashMap<String, String>(); + JAVA_TO_JWA_NAMES.put(HMAC_SHA_256_JAVA, HMAC_SHA_256_ALGO); + JAVA_TO_JWA_NAMES.put(HMAC_SHA_384_JAVA, HMAC_SHA_384_ALGO); + JAVA_TO_JWA_NAMES.put(HMAC_SHA_512_JAVA, HMAC_SHA_512_ALGO); + JAVA_TO_JWA_NAMES.put(RS_SHA_256_JAVA, RS_SHA_256_ALGO); + JAVA_TO_JWA_NAMES.put(RS_SHA_384_JAVA, RS_SHA_384_ALGO); + JAVA_TO_JWA_NAMES.put(RS_SHA_512_JAVA, RS_SHA_512_ALGO); + JAVA_TO_JWA_NAMES.put(PS_SHA_256_JAVA, PS_SHA_256_ALGO); + JAVA_TO_JWA_NAMES.put(PS_SHA_384_JAVA, PS_SHA_384_ALGO); + JAVA_TO_JWA_NAMES.put(PS_SHA_512_JAVA, PS_SHA_512_ALGO); + JAVA_TO_JWA_NAMES.put(ES_SHA_256_JAVA, ES_SHA_256_ALGO); + JAVA_TO_JWA_NAMES.put(ES_SHA_384_JAVA, ES_SHA_384_ALGO); + JAVA_TO_JWA_NAMES.put(ES_SHA_512_JAVA, ES_SHA_512_ALGO); + JAVA_TO_JWA_NAMES.put(RSA_OAEP_ALGO_JAVA, RSA_OAEP_ALGO); + JAVA_TO_JWA_NAMES.put(RSA_OAEP_256_ALGO_JAVA, RSA_OAEP_256_ALGO); + JAVA_TO_JWA_NAMES.put(RSA_1_5_ALGO_JAVA, RSA1_5_ALGO); + JAVA_TO_JWA_NAMES.put(AES_GCM_ALGO_JAVA, A256GCM_ALGO); + JAVA_TO_JWA_NAMES.put(AES_GCM_ALGO_JAVA, A192GCM_ALGO); + JAVA_TO_JWA_NAMES.put(AES_GCM_ALGO_JAVA, A128GCM_ALGO); + JAVA_TO_JWA_NAMES.put(AES_WRAP_ALGO_JAVA, A128KW_ALGO); + JAVA_TO_JWA_NAMES.put(AES_WRAP_ALGO_JAVA, A192KW_ALGO); + JAVA_TO_JWA_NAMES.put(AES_WRAP_ALGO_JAVA, A256KW_ALGO); + JAVA_TO_JWA_NAMES.put(AES_CBC_ALGO_JAVA, A128CBC_HS256_ALGO); + JAVA_TO_JWA_NAMES.put(AES_CBC_ALGO_JAVA, A192CBC_HS384_ALGO); + JAVA_TO_JWA_NAMES.put(AES_CBC_ALGO_JAVA, A256CBC_HS512_ALGO); + JWA_TO_JAVA_NAMES = new HashMap<String, String>(); + JWA_TO_JAVA_NAMES.put(HMAC_SHA_256_ALGO, HMAC_SHA_256_JAVA); + JWA_TO_JAVA_NAMES.put(HMAC_SHA_384_ALGO, HMAC_SHA_384_JAVA); + JWA_TO_JAVA_NAMES.put(HMAC_SHA_512_ALGO, HMAC_SHA_512_JAVA); + JWA_TO_JAVA_NAMES.put(RS_SHA_256_ALGO, RS_SHA_256_JAVA); + JWA_TO_JAVA_NAMES.put(RS_SHA_384_ALGO, RS_SHA_384_JAVA); + JWA_TO_JAVA_NAMES.put(RS_SHA_512_ALGO, RS_SHA_512_JAVA); + JWA_TO_JAVA_NAMES.put(PS_SHA_256_ALGO, PS_SHA_256_JAVA); + JWA_TO_JAVA_NAMES.put(PS_SHA_384_ALGO, PS_SHA_384_JAVA); + JWA_TO_JAVA_NAMES.put(PS_SHA_512_ALGO, PS_SHA_512_JAVA); + JWA_TO_JAVA_NAMES.put(ES_SHA_256_ALGO, ES_SHA_256_JAVA); + JWA_TO_JAVA_NAMES.put(ES_SHA_384_ALGO, ES_SHA_384_JAVA); + JWA_TO_JAVA_NAMES.put(ES_SHA_512_ALGO, ES_SHA_512_JAVA); + JWA_TO_JAVA_NAMES.put(RSA_OAEP_ALGO, RSA_OAEP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(RSA_OAEP_256_ALGO, RSA_OAEP_256_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(RSA1_5_ALGO, RSA_1_5_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A128KW_ALGO, AES_WRAP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A192KW_ALGO, AES_WRAP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A256KW_ALGO, AES_WRAP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A256GCM_ALGO, AES_GCM_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A192GCM_ALGO, AES_GCM_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A128GCM_ALGO, AES_GCM_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A256GCMKW_ALGO, AES_GCM_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A192GCMKW_ALGO, AES_GCM_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A128GCMKW_ALGO, AES_GCM_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A128CBC_HS256_ALGO, AES_CBC_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A192CBC_HS384_ALGO, AES_CBC_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(A256CBC_HS512_ALGO, AES_CBC_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(PBES2_HS256_A128KW_ALGO, AES_WRAP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(PBES2_HS384_A192KW_ALGO, AES_WRAP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(PBES2_HS512_A256KW_ALGO, AES_WRAP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(ECDH_ES_A128KW_ALGO, AES_WRAP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(ECDH_ES_A192KW_ALGO, AES_WRAP_ALGO_JAVA); + JWA_TO_JAVA_NAMES.put(ECDH_ES_A256KW_ALGO, AES_WRAP_ALGO_JAVA); + } + + private AlgorithmUtils() { + } + public static boolean isRsa(String algo) { + return isRsaKeyWrap(algo) || isRsaSign(algo); + } + public static boolean isRsaKeyWrap(String algo) { + return RSA_CEK_SET.contains(algo); + } + public static boolean isAesKeyWrap(String algo) { + return AES_KW_SET.contains(algo); + } + public static boolean isAesGcmKeyWrap(String algo) { + return AES_GCM_KW_SET.contains(algo); + } + public static boolean isPbesHsWrap(String algo) { + return PBES_HS_SET.contains(algo); + } + public static boolean isEcdhEsWrap(String algo) { + return ECDH_ES_WRAP_SET.contains(algo); + } + public static boolean isEcdhEsDirect(String algo) { + return ECDH_ES_DIRECT_ALGO.equals(algo); + } + public static boolean isAesGcm(String algo) { + return AES_GCM_CEK_SET.contains(algo); + } + public static boolean isAesCbcHmac(String algo) { + return ACBC_HS_SET.contains(algo); + } + public static boolean isHmacSign(String algo) { + return HMAC_SIGN_SET.contains(algo); + } + public static boolean isOctet(String algo) { + return isHmacSign(algo) + || isAesCbcHmac(algo) + || isAesGcm(algo) + || isAesGcmKeyWrap(algo) + || isAesKeyWrap(algo); + } + public static boolean isRsaSign(String algo) { + return isRsaShaSign(algo) || isRsaShaPsSign(algo); + } + public static boolean isRsaShaSign(String algo) { + return RSA_SHA_SIGN_SET.contains(algo); + } + public static boolean isRsaShaPsSign(String algo) { + return RSA_SHA_PS_SIGN_SET.contains(algo); + } + public static boolean isEcDsaSign(String algo) { + return EC_SHA_SIGN_SET.contains(algo); + } + + public static String toJwaName(String javaName, int keyBitSize) { + //TODO: perhaps a key should be a name+keysize pair + String name = JAVA_TO_JWA_NAMES.get(javaName); + if (name == null && javaName.startsWith(AES)) { + name = "A" + keyBitSize + "GCM"; + } + return name; + } + public static String toJavaName(String jwtName) { + return JWA_TO_JAVA_NAMES.get(jwtName); + } + public static String toJavaAlgoNameOnly(String jwtName) { + return stripAlgoProperties(toJavaName(jwtName)); + } + public static String stripAlgoProperties(String javaName) { + if (javaName != null) { + int index = javaName.indexOf('/'); + if (index != -1) { + javaName = javaName.substring(0, index); + } + } + return javaName; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/ContentAlgorithm.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/ContentAlgorithm.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/ContentAlgorithm.java new file mode 100644 index 0000000..1a8b9bc --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/ContentAlgorithm.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.rs.security.jose.jwa; + + + + +public enum ContentAlgorithm { + A128GCM(AlgorithmUtils.A128GCM_ALGO, "AES/GCM/NoPadding", 128), + A192GCM(AlgorithmUtils.A192GCM_ALGO, "AES/GCM/NoPadding", 192), + A256GCM(AlgorithmUtils.A256GCM_ALGO, "AES/GCM/NoPadding", 256), + //TODO: default to "AES/CBC/PKCS5Padding" if Cipher "AES/CBC/PKCS7Padding" + // can not be initialized, apparently Java 8 has decided to settle on PKCS5Padding only + A128CBC_HS256(AlgorithmUtils.A128CBC_HS256_ALGO, "AES/CBC/PKCS7Padding", 128), + A192CBC_HS384(AlgorithmUtils.A192CBC_HS384_ALGO, "AES/CBC/PKCS7Padding", 192), + A256CBC_HS512(AlgorithmUtils.A256CBC_HS512_ALGO, "AES/CBC/PKCS7Padding", 256); + + private final String jwaName; + private final String javaName; + private final int keySizeBits; + + private ContentAlgorithm(String jwaName, String javaName, int keySizeBits) { + this.jwaName = jwaName; + this.javaName = javaName; + this.keySizeBits = keySizeBits; + } + + public String getJwaName() { + return jwaName; + } + + public String getJavaName() { + return javaName == null ? name() : javaName; + } + + public String getJavaAlgoName() { + return AlgorithmUtils.stripAlgoProperties(getJavaName()); + } + + public int getKeySizeBits() { + return keySizeBits; + } + + public static ContentAlgorithm getAlgorithm(String algo) { + if (algo == null) { + return null; + } + return ContentAlgorithm.valueOf(algo.replace('-', '_') + .replace('+', '_')); + + } + +} \ No newline at end of file
