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/jws/JwsJsonOutputStream.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonOutputStream.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonOutputStream.java new file mode 100644 index 0000000..7fe6059 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonOutputStream.java @@ -0,0 +1,100 @@ +/** + * 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.jws; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.cxf.common.util.Base64UrlUtility; +import org.apache.cxf.common.util.StringUtils; + +public class JwsJsonOutputStream extends FilterOutputStream { + private boolean flushed; + private List<String> protectedHeaders; + private List<JwsSignature> signatures; + private ExecutorService executor; + public JwsJsonOutputStream(OutputStream out, + List<String> protectedHeaders, + List<JwsSignature> signatures) { + super(out); + this.protectedHeaders = protectedHeaders; + this.signatures = signatures; + // This can be further optimized by having a dedicated thread per signature, + // can make a difference if a number of signatures to be created is > 2 + this.executor = Executors.newSingleThreadExecutor(); + } + + @Override + public void write(int value) throws IOException { + byte[] bytes = ByteBuffer.allocate(Integer.SIZE / 8).putInt(value).array(); + write(bytes, 0, bytes.length); + } + + @Override + public void write(final byte b[], final int off, final int len) throws IOException { + //TODO: Review if it is at least theoretically possible that a given b[] region + // can be modified in a subsequent write which might affect the signature calculation + executor.execute(new Runnable() { + public void run() { + for (JwsSignature signature : signatures) { + signature.update(b, off, len); + } + } + }); + out.write(b, off, len); + out.flush(); + } + @Override + public void flush() throws IOException { + if (flushed) { + return; + } + out.write(StringUtils.toBytesUTF8("\",\"signatures\":[")); + shutdownExecutor(); + for (int i = 0; i < signatures.size(); i++) { + if (i > 0) { + out.write(new byte[]{','}); + } + out.write(StringUtils.toBytesUTF8("{\"protected\":\"" + + protectedHeaders.get(i) + + "\",\"signature\":\"")); + byte[] sign = signatures.get(i).sign(); + Base64UrlUtility.encodeAndStream(sign, 0, sign.length, out); + out.write(StringUtils.toBytesUTF8("\"}")); + } + out.write(StringUtils.toBytesUTF8("]}")); + flushed = true; + } + private void shutdownExecutor() { + executor.shutdown(); + while (!executor.isTerminated()) { + try { + executor.awaitTermination(1, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + throw new RuntimeException(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/jws/JwsJsonProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProducer.java new file mode 100644 index 0000000..e96a630 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProducer.java @@ -0,0 +1,212 @@ +/** + * 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.jws; + +import java.security.PrivateKey; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.Base64UrlUtility; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; +import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.common.JoseHeaders; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; + +public class JwsJsonProducer { + protected static final Logger LOG = LogUtils.getL7dLogger(JwsJsonProducer.class); + private boolean supportFlattened; + private String plainPayload; + private String encodedPayload; + private List<JwsJsonSignatureEntry> signatures = new LinkedList<JwsJsonSignatureEntry>(); + private JsonMapObjectReaderWriter writer = new JsonMapObjectReaderWriter(); + public JwsJsonProducer(String tbsDocument) { + this(tbsDocument, false); + } + public JwsJsonProducer(String tbsDocument, boolean supportFlattened) { + this.supportFlattened = supportFlattened; + this.plainPayload = tbsDocument; + } + + public String getPlainPayload() { + return plainPayload; + } + public String getUnsignedEncodedPayload() { + if (encodedPayload == null) { + encodedPayload = Base64UrlUtility.encode(getPlainPayload()); + } + return encodedPayload; + } + public String getJwsJsonSignedDocument() { + return getJwsJsonSignedDocument(false); + } + public String getJwsJsonSignedDocument(boolean detached) { + if (signatures.isEmpty()) { + return null; + } + + Boolean b64Status = validateB64Status(signatures); + StringBuilder sb = new StringBuilder(); + sb.append("{"); + if (!detached) { + sb.append("\"payload\":\"" + getActualPayload(b64Status) + "\""); + sb.append(","); + } + if (!supportFlattened || signatures.size() > 1) { + sb.append("\"signatures\":["); + for (int i = 0; i < signatures.size(); i++) { + JwsJsonSignatureEntry signature = signatures.get(i); + if (i > 0) { + sb.append(","); + } + sb.append(signature.toJson()); + } + sb.append("]"); + } else { + sb.append(signatures.get(0).toJson(true)); + } + sb.append("}"); + return sb.toString(); + } + public List<JwsJsonSignatureEntry> getSignatureEntries() { + return signatures; + } + + /* + * TODO + public MultivaluedMap<SignatureAlgorithm, JwsJsonSignatureEntry> getSignatureEntryMap() { + return JwsUtils.getJwsJsonSignatureMap(signatures); + } + */ + public String signWith(List<JwsSignatureProvider> signers) { + for (JwsSignatureProvider signer : signers) { + signWith(signer); + } + return getJwsJsonSignedDocument(); + } + public String signWith(JwsSignatureProvider signer) { + JwsHeaders headers = new JwsHeaders(); + headers.setSignatureAlgorithm(signer.getAlgorithm()); + return signWith(signer, headers); + } + public String signWith(JwsSignatureProvider signer, + JwsHeaders protectedHeader) { + return signWith(signer, protectedHeader, null); + } + public String signWith(JsonWebKey jwk) { + return signWith(JwsUtils.getSignatureProvider(jwk)); + } + public String signWith(PrivateKey key, SignatureAlgorithm algo) { + return signWith(JwsUtils.getPrivateKeySignatureProvider(key, algo)); + } + public String signWith(byte[] key, SignatureAlgorithm algo) { + return signWith(JwsUtils.getHmacSignatureProvider(key, algo)); + } + public String signWith(JwsSignatureProvider signer, + JwsHeaders protectedHeader, + JwsHeaders unprotectedHeader) { + JwsHeaders unionHeaders = new JwsHeaders(); + + if (protectedHeader != null) { + unionHeaders.asMap().putAll(protectedHeader.asMap()); + } + if (unprotectedHeader != null) { + checkUnprotectedHeaders(unprotectedHeader, + JoseConstants.HEADER_CRITICAL, + JoseConstants.JWS_HEADER_B64_STATUS_HEADER); + if (!Collections.disjoint(unionHeaders.asMap().keySet(), + unprotectedHeader.asMap().keySet())) { + LOG.warning("Protected and unprotected headers have duplicate values"); + throw new JwsException(JwsException.Error.INVALID_JSON_JWS); + } + unionHeaders.asMap().putAll(unprotectedHeader.asMap()); + } + if (unionHeaders.getSignatureAlgorithm() == null) { + LOG.warning("Algorithm header is not set"); + throw new JwsException(JwsException.Error.INVALID_JSON_JWS); + } + String sequenceToBeSigned; + String actualPayload = protectedHeader != null + ? getActualPayload(protectedHeader.getPayloadEncodingStatus()) + : getUnsignedEncodedPayload(); + if (protectedHeader != null) { + sequenceToBeSigned = Base64UrlUtility.encode(writer.toJson(protectedHeader)) + + "." + actualPayload; + } else { + sequenceToBeSigned = "." + getUnsignedEncodedPayload(); + } + byte[] bytesToBeSigned = StringUtils.toBytesUTF8(sequenceToBeSigned); + + byte[] signatureBytes = signer.sign(unionHeaders, bytesToBeSigned); + + String encodedSignatureBytes = Base64UrlUtility.encode(signatureBytes); + JwsJsonSignatureEntry signature; + if (protectedHeader != null) { + signature = new JwsJsonSignatureEntry(actualPayload, + Base64UrlUtility.encode(writer.toJson(protectedHeader)), + encodedSignatureBytes, + unprotectedHeader); + } else { + signature = new JwsJsonSignatureEntry(getUnsignedEncodedPayload(), + null, + encodedSignatureBytes, + unprotectedHeader); + } + return updateJwsJsonSignedDocument(signature); + } + private String getActualPayload(Boolean payloadEncodingStatus) { + return Boolean.FALSE == payloadEncodingStatus + ? getPlainPayload() : this.getUnsignedEncodedPayload(); + } + private String updateJwsJsonSignedDocument(JwsJsonSignatureEntry signature) { + signatures.add(signature); + return getJwsJsonSignedDocument(); + } + private static void checkUnprotectedHeaders(JoseHeaders unprotected, String... headerNames) { + for (String headerName : headerNames) { + if (unprotected.containsHeader(headerName)) { + LOG.warning("Unprotected headers contain a header \"" + + headerName + "\" which must be protected"); + throw new JwsException(JwsException.Error.INVALID_JSON_JWS); + } + } + } + static Boolean validateB64Status(List<JwsJsonSignatureEntry> signatures) { + Set<Boolean> b64Set = new LinkedHashSet<Boolean>(); + for (JwsJsonSignatureEntry entry : signatures) { + JwsHeaders headers = entry.getProtectedHeader(); + Boolean status = headers != null ? headers.getPayloadEncodingStatus() : null; + if (status == null) { + status = Boolean.TRUE; + } + b64Set.add(status); + } + if (b64Set.size() > 1) { + LOG.warning("Each signature entry can sign only encoded or only unencoded payload"); + throw new JwsException(JwsException.Error.INVALID_JSON_JWS); + } + return b64Set.iterator().next(); + } +} 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/jws/JwsJsonSignatureEntry.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonSignatureEntry.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonSignatureEntry.java new file mode 100644 index 0000000..85f89df --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonSignatureEntry.java @@ -0,0 +1,163 @@ +/** + * 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.jws; + +import java.util.Collections; +import java.util.logging.Logger; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.Base64UrlUtility; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; +import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.common.JoseUtils; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; + + +public class JwsJsonSignatureEntry { + protected static final Logger LOG = LogUtils.getL7dLogger(JwsJsonSignatureEntry.class); + private String jwsPayload; + private String encodedProtectedHeader; + private String encodedSignature; + private JwsHeaders protectedHeader; + private JwsHeaders unprotectedHeader; + private JwsHeaders unionHeaders; + private JsonMapObjectReaderWriter writer = new JsonMapObjectReaderWriter(); + + public JwsJsonSignatureEntry(String jwsPayload, + String encodedProtectedHeader, + String encodedSignature, + JwsHeaders unprotectedHeader) { + if (encodedProtectedHeader == null && unprotectedHeader == null || encodedSignature == null) { + LOG.warning("Invalid Signature entry"); + throw new JwsException(JwsException.Error.INVALID_JSON_JWS); + } + + this.jwsPayload = jwsPayload; + this.encodedProtectedHeader = encodedProtectedHeader; + this.encodedSignature = encodedSignature; + this.unprotectedHeader = unprotectedHeader; + if (encodedProtectedHeader != null) { + this.protectedHeader = new JwsHeaders(writer.fromJson(JoseUtils.decodeToString(encodedProtectedHeader))); + } + prepare(); + } + private void prepare() { + unionHeaders = new JwsHeaders(); + + if (protectedHeader != null) { + unionHeaders.asMap().putAll(protectedHeader.asMap()); + } + if (unprotectedHeader != null) { + if (!Collections.disjoint(unionHeaders.asMap().keySet(), + unprotectedHeader.asMap().keySet())) { + LOG.warning("Protected and unprotected headers have duplicate values"); + throw new JwsException(JwsException.Error.INVALID_JSON_JWS); + } + unionHeaders.asMap().putAll(unprotectedHeader.asMap()); + } + } + public String getJwsPayload() { + return jwsPayload; + } + public String getDecodedJwsPayload() { + if (protectedHeader == null || !JwsUtils.isPayloadUnencoded(protectedHeader)) { + return JoseUtils.decodeToString(jwsPayload); + } else { + return jwsPayload; + } + } + public byte[] getDecodedJwsPayloadBytes() { + return StringUtils.toBytesUTF8(getDecodedJwsPayload()); + } + public String getEncodedProtectedHeader() { + return encodedProtectedHeader; + } + public JwsHeaders getProtectedHeader() { + return protectedHeader; + } + public JwsHeaders getUnprotectedHeader() { + return unprotectedHeader; + } + public JwsHeaders getUnionHeader() { + return unionHeaders; + } + public String getEncodedSignature() { + return encodedSignature; + } + public byte[] getDecodedSignature() { + return JoseUtils.decode(getEncodedSignature()); + } + public String getUnsignedSequence() { + if (getEncodedProtectedHeader() != null) { + return getEncodedProtectedHeader() + "." + getJwsPayload(); + } else { + return "." + getJwsPayload(); + } + } + public String getKeyId() { + return getUnionHeader().getKeyId(); + } + public boolean verifySignatureWith(JwsSignatureVerifier validator) { + try { + if (validator.verify(getUnionHeader(), + getUnsignedSequence(), + getDecodedSignature())) { + return true; + } + } catch (JwsException ex) { + // ignore + } + LOG.warning("Invalid Signature Entry"); + return false; + } + public boolean verifySignatureWith(JsonWebKey key) { + return verifySignatureWith(JwsUtils.getSignatureVerifier(key)); + } + public boolean validateCriticalHeaders() { + if (this.getUnprotectedHeader().getHeader(JoseConstants.HEADER_CRITICAL) != null) { + return false; + } + return JwsUtils.validateCriticalHeaders(getUnionHeader()); + } + public String toJson() { + return toJson(false); + } + public String toJson(boolean flattenedMode) { + StringBuilder sb = new StringBuilder(); + if (!flattenedMode) { + sb.append("{"); + } + if (protectedHeader != null) { + sb.append("\"protected\":\"" + Base64UrlUtility.encode(writer.toJson(protectedHeader)) + "\""); + } + if (unprotectedHeader != null) { + if (protectedHeader != null) { + sb.append(","); + } + sb.append("\"header\":" + writer.toJson(unprotectedHeader)); + } + sb.append(","); + sb.append("\"signature\":\"" + encodedSignature + "\""); + if (!flattenedMode) { + sb.append("}"); + } + return sb.toString(); + } +} 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/jws/JwsJwtCompactConsumer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJwtCompactConsumer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJwtCompactConsumer.java new file mode 100644 index 0000000..e2bcfa8 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJwtCompactConsumer.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.jws; + +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenReaderWriter; + +public class JwsJwtCompactConsumer extends JwsCompactConsumer { + private JwtToken token; + public JwsJwtCompactConsumer(String encodedJws) { + super(encodedJws, null, new JwtTokenReaderWriter()); + } + public JwtClaims getJwtClaims() { + return getJwtToken().getClaims(); + } + public JwtToken getJwtToken() { + if (token == null) { + JwsHeaders theHeaders = super.getJwsHeaders(); + JwtClaims theClaims = + ((JwtTokenReaderWriter)getReader()).fromJsonClaims(getDecodedJwsPayload()); + token = new JwtToken(theHeaders, theClaims); + } + return token; + } + +} 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/jws/JwsJwtCompactProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJwtCompactProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJwtCompactProducer.java new file mode 100644 index 0000000..beaa598 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJwtCompactProducer.java @@ -0,0 +1,44 @@ +/** + * 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.jws; +import org.apache.cxf.rs.security.jose.common.JoseHeaders; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenReaderWriter; +import org.apache.cxf.rs.security.jose.jwt.JwtUtils; + + +public class JwsJwtCompactProducer extends JwsCompactProducer { + + public JwsJwtCompactProducer(JwtToken token) { + this(token, null); + } + public JwsJwtCompactProducer(JwtClaims claims) { + this(new JwtToken(null, claims), null); + } + public JwsJwtCompactProducer(JoseHeaders headers, JwtClaims claims) { + this(new JwtToken(headers, claims), null); + } + protected JwsJwtCompactProducer(JwtToken token, JwtTokenReaderWriter w) { + super(new JwsHeaders(token.getHeaders()), w, + JwtUtils.claimsToJson(token.getClaims(), w)); + } + + +} 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/jws/JwsOutputStream.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsOutputStream.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsOutputStream.java new file mode 100644 index 0000000..53b592f --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsOutputStream.java @@ -0,0 +1,59 @@ +/** + * 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.jws; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +import org.apache.cxf.common.util.Base64UrlUtility; + +public class JwsOutputStream extends FilterOutputStream { + private boolean flushed; + private JwsSignature signature; + public JwsOutputStream(OutputStream out, JwsSignature signature) { + super(out); + this.signature = signature; + } + + @Override + public void write(int value) throws IOException { + byte[] bytes = ByteBuffer.allocate(Integer.SIZE / 8).putInt(value).array(); + write(bytes, 0, bytes.length); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + signature.update(b, off, len); + out.write(b, off, len); + out.flush(); + } + @Override + public void flush() throws IOException { + if (flushed) { + return; + } + byte[] finalBytes = signature.sign(); + out.write(new byte[]{'.'}); + Base64UrlUtility.encodeAndStream(finalBytes, 0, finalBytes.length, out); + flushed = true; + } + +} 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/jws/JwsSignature.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignature.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignature.java new file mode 100644 index 0000000..778b5cb --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignature.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.jws; + + +public interface JwsSignature { + void update(byte[] src, int off, int len); + byte[] sign(); +} 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/jws/JwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureProvider.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureProvider.java new file mode 100644 index 0000000..00f0c2a --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureProvider.java @@ -0,0 +1,31 @@ +/** + * 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.jws; + +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; + + +public interface JwsSignatureProvider { + SignatureAlgorithm getAlgorithm(); + byte[] sign(JwsHeaders headers, byte[] content); + /** + * Create a signature handler capable of updating the signature input (optional operation) + */ + JwsSignature createJwsSignature(JwsHeaders headers); +} 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/jws/JwsSignatureVerifier.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureVerifier.java new file mode 100644 index 0000000..c44a678 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureVerifier.java @@ -0,0 +1,26 @@ +/** + * 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.jws; + +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; + +public interface JwsSignatureVerifier { + SignatureAlgorithm getAlgorithm(); + boolean verify(JwsHeaders headers, String unsignedText, byte[] signature); +} 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/jws/JwsUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java new file mode 100644 index 0000000..958c52e --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -0,0 +1,426 @@ +/** + * 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.jws; + +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Logger; + +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.common.JoseConstants; +import org.apache.cxf.rs.security.jose.common.JoseHeaders; +import org.apache.cxf.rs.security.jose.common.JoseUtils; +import org.apache.cxf.rs.security.jose.common.KeyManagementUtils; +import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jwk.JwkUtils; +import org.apache.cxf.rs.security.jose.jwk.KeyOperation; +import org.apache.cxf.rs.security.jose.jwk.KeyType; + +public final class JwsUtils { + private static final Logger LOG = LogUtils.getL7dLogger(JwsUtils.class); + + private JwsUtils() { + + } + public static String sign(PrivateKey key, SignatureAlgorithm algo, String content) { + return sign(key, algo, content, null); + } + + + public static String sign(PrivateKey key, SignatureAlgorithm algo, String content, String ct) { + return sign(getPrivateKeySignatureProvider(key, algo), content, ct); + } + public static String sign(byte[] key, SignatureAlgorithm algo, String content) { + return sign(key, algo, content, null); + } + public static String sign(byte[] key, SignatureAlgorithm algo, String content, String ct) { + return sign(getHmacSignatureProvider(key, algo), content, ct); + } + public static String verify(PublicKey key, SignatureAlgorithm algo, String content) { + JwsCompactConsumer jws = verify(getPublicKeySignatureVerifier(key, algo), content); + return jws.getDecodedJwsPayload(); + } + public static String verify(byte[] key, SignatureAlgorithm algo, String content) { + JwsCompactConsumer jws = verify(getHmacSignatureVerifier(key, algo), content); + return jws.getDecodedJwsPayload(); + } + public static JwsSignatureProvider getSignatureProvider(JsonWebKey jwk) { + return getSignatureProvider(jwk, null); + } + public static JwsSignatureProvider getSignatureProvider(JsonWebKey jwk, + SignatureAlgorithm defaultAlgorithm) { + SignatureAlgorithm sigAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm + : SignatureAlgorithm.getAlgorithm(jwk.getAlgorithm()); + JwsSignatureProvider theSigProvider = null; + KeyType keyType = jwk.getKeyType(); + if (KeyType.RSA == keyType) { + theSigProvider = getPrivateKeySignatureProvider(JwkUtils.toRSAPrivateKey(jwk), + sigAlgo); + } else if (KeyType.OCTET == keyType) { + byte[] key = JoseUtils.decode((String)jwk.getProperty(JsonWebKey.OCTET_KEY_VALUE)); + theSigProvider = getHmacSignatureProvider(key, sigAlgo); + } else if (KeyType.EC == jwk.getKeyType()) { + theSigProvider = getPrivateKeySignatureProvider(JwkUtils.toECPrivateKey(jwk), + sigAlgo); + } + return theSigProvider; + } + public static JwsSignatureProvider getPrivateKeySignatureProvider(PrivateKey key, SignatureAlgorithm algo) { + if (algo == null) { + LOG.warning("No signature algorithm was defined"); + throw new JwsException(JwsException.Error.ALGORITHM_NOT_SET); + } + if (key instanceof ECPrivateKey) { + return new EcDsaJwsSignatureProvider((ECPrivateKey)key, algo); + } else if (key instanceof RSAPrivateKey) { + return new PrivateKeyJwsSignatureProvider(key, algo); + } + + return null; + } + public static JwsSignatureProvider getHmacSignatureProvider(byte[] key, SignatureAlgorithm algo) { + if (algo == null) { + LOG.warning("No signature algorithm was defined"); + throw new JwsException(JwsException.Error.ALGORITHM_NOT_SET); + } + if (AlgorithmUtils.isHmacSign(algo.getJwaName())) { + return new HmacJwsSignatureProvider(key, algo); + } + return null; + } + public static JwsSignatureVerifier getSignatureVerifier(JsonWebKey jwk) { + return getSignatureVerifier(jwk, null); + } + public static JwsSignatureVerifier getSignatureVerifier(JsonWebKey jwk, SignatureAlgorithm defaultAlgorithm) { + SignatureAlgorithm sigAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm + : SignatureAlgorithm.getAlgorithm(jwk.getAlgorithm()); + JwsSignatureVerifier theVerifier = null; + KeyType keyType = jwk.getKeyType(); + if (KeyType.RSA == keyType) { + theVerifier = getPublicKeySignatureVerifier(JwkUtils.toRSAPublicKey(jwk, true), sigAlgo); + } else if (KeyType.OCTET == keyType) { + byte[] key = JoseUtils.decode((String)jwk.getProperty(JsonWebKey.OCTET_KEY_VALUE)); + theVerifier = getHmacSignatureVerifier(key, sigAlgo); + } else if (KeyType.EC == keyType) { + theVerifier = getPublicKeySignatureVerifier(JwkUtils.toECPublicKey(jwk), sigAlgo); + } + return theVerifier; + } + public static JwsSignatureVerifier getPublicKeySignatureVerifier(X509Certificate cert, SignatureAlgorithm algo) { + return getPublicKeySignatureVerifier(cert.getPublicKey(), algo); + } + public static JwsSignatureVerifier getPublicKeySignatureVerifier(PublicKey key, SignatureAlgorithm algo) { + if (algo == null) { + LOG.warning("No signature algorithm was defined"); + throw new JwsException(JwsException.Error.ALGORITHM_NOT_SET); + } + + if (key instanceof RSAPublicKey) { + return new PublicKeyJwsSignatureVerifier(key, algo); + } else if (key instanceof ECPublicKey) { + return new EcDsaJwsSignatureVerifier(key, algo); + } + + return null; + } + public static JwsSignatureVerifier getHmacSignatureVerifier(byte[] key, SignatureAlgorithm algo) { + if (algo == null) { + LOG.warning("No signature algorithm was defined"); + throw new JwsException(JwsException.Error.ALGORITHM_NOT_SET); + } + + if (AlgorithmUtils.isHmacSign(algo.getJwaName())) { + return new HmacJwsSignatureVerifier(key, algo); + } + return null; + } + + public static Map<SignatureAlgorithm, List<JwsJsonSignatureEntry>> getJwsJsonSignatureMap( + List<JwsJsonSignatureEntry> signatures) { + Map<SignatureAlgorithm, List<JwsJsonSignatureEntry>> map = + new HashMap<SignatureAlgorithm, List<JwsJsonSignatureEntry>>(); + for (JwsJsonSignatureEntry entry : signatures) { + SignatureAlgorithm sigAlgorithm = entry.getUnionHeader().getSignatureAlgorithm(); + List<JwsJsonSignatureEntry> entries = map.get(sigAlgorithm); + if (entries == null) { + entries = new ArrayList<JwsJsonSignatureEntry>(); + } + entries.add(entry); + map.put(sigAlgorithm, entries); + } + return map; + } + + public static JwsSignatureProvider loadSignatureProvider(boolean required) { + return loadSignatureProvider(null, required); + } + public static JwsSignatureProvider loadSignatureProvider(JwsHeaders headers, boolean required) { + Message m = PhaseInterceptorChain.getCurrentMessage(); + Properties props = KeyManagementUtils.loadStoreProperties(m, required, + JoseConstants.RSSEC_SIGNATURE_OUT_PROPS, + JoseConstants.RSSEC_SIGNATURE_PROPS); + if (props == null) { + return null; + } + JwsSignatureProvider theSigProvider = loadSignatureProvider(m, props, headers, false); + if (headers != null) { + headers.setSignatureAlgorithm(theSigProvider.getAlgorithm()); + } + return theSigProvider; + } + public static JwsSignatureVerifier loadSignatureVerifier(boolean required) { + return loadSignatureVerifier(null, required); + } + public static JwsSignatureVerifier loadSignatureVerifier(JwsHeaders headers, boolean required) { + Message m = PhaseInterceptorChain.getCurrentMessage(); + Properties props = KeyManagementUtils.loadStoreProperties(m, required, + JoseConstants.RSSEC_SIGNATURE_IN_PROPS, + JoseConstants.RSSEC_SIGNATURE_PROPS); + return loadSignatureVerifier(m, props, headers, false); + } + public static List<JwsSignatureProvider> loadSignatureProviders(String propLoc, Message m) { + Properties props = loadJwsProperties(m, propLoc); + JwsSignatureProvider theSigProvider = loadSignatureProvider(m, props, null, true); + if (theSigProvider != null) { + return Collections.singletonList(theSigProvider); + } + List<JwsSignatureProvider> theSigProviders = null; + if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { + List<JsonWebKey> jwks = JwkUtils.loadJsonWebKeys(m, props, KeyOperation.SIGN); + if (jwks != null) { + theSigProviders = new ArrayList<JwsSignatureProvider>(jwks.size()); + for (JsonWebKey jwk : jwks) { + theSigProviders.add(JwsUtils.getSignatureProvider(jwk)); + } + } + } + if (theSigProviders == null) { + LOG.warning("Providers are not available"); + throw new JwsException(JwsException.Error.NO_PROVIDER); + } + return theSigProviders; + } + + public static List<JwsSignatureVerifier> loadSignatureVerifiers(String propLoc, Message m) { + Properties props = loadJwsProperties(m, propLoc); + JwsSignatureVerifier theVerifier = loadSignatureVerifier(m, props, null, true); + if (theVerifier != null) { + return Collections.singletonList(theVerifier); + } + List<JwsSignatureVerifier> theVerifiers = null; + if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { + List<JsonWebKey> jwks = JwkUtils.loadJsonWebKeys(m, props, KeyOperation.VERIFY); + if (jwks != null) { + theVerifiers = new ArrayList<JwsSignatureVerifier>(jwks.size()); + for (JsonWebKey jwk : jwks) { + theVerifiers.add(JwsUtils.getSignatureVerifier(jwk)); + } + } + } + if (theVerifiers == null) { + LOG.warning("Verifiers are not available"); + throw new JwsException(JwsException.Error.NO_VERIFIER); + } + return theVerifiers; + } + public static boolean validateCriticalHeaders(JoseHeaders headers) { + //TODO: validate JWS specific constraints + return JoseUtils.validateCriticalHeaders(headers); + } + private static JwsSignatureProvider loadSignatureProvider(Message m, + Properties props, + JoseHeaders headers, + boolean ignoreNullProvider) { + JwsSignatureProvider theSigProvider = null; + + boolean reportPublicKey = headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_REPORT_KEY_PROP, + JoseConstants.RSSEC_REPORT_KEY_PROP)); + boolean reportPublicKeyId = + headers != null && MessageUtils.isTrue( + MessageUtils.getContextualProperty(m, JoseConstants.RSSEC_REPORT_KEY_ID_PROP, + JoseConstants.RSSEC_REPORT_KEY_ID_PROP)); + if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { + JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.SIGN); + if (jwk != null) { + String signatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); + theSigProvider = JwsUtils.getSignatureProvider(jwk, SignatureAlgorithm.getAlgorithm(signatureAlgo)); + if (reportPublicKey || reportPublicKeyId) { + JwkUtils.setPublicKeyInfo(jwk, headers, signatureAlgo, reportPublicKey, reportPublicKeyId); + } + } + } else { + String signatureAlgo = getSignatureAlgo(m, props, null, null); + PrivateKey pk = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.SIGN); + theSigProvider = getPrivateKeySignatureProvider(pk, + SignatureAlgorithm.getAlgorithm(signatureAlgo)); + if (reportPublicKey) { + headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); + } + } + if (theSigProvider == null && !ignoreNullProvider) { + LOG.warning("Provider is not available"); + throw new JwsException(JwsException.Error.NO_PROVIDER); + } + return theSigProvider; + } + private static JwsSignatureVerifier loadSignatureVerifier(Message m, + Properties props, + JwsHeaders inHeaders, + boolean ignoreNullVerifier) { + JwsSignatureVerifier theVerifier = null; + String inHeaderKid = null; + if (inHeaders != null) { + inHeaderKid = inHeaders.getKeyId(); + //TODO: optionally validate inHeaders.getAlgorithm against a property in props + if (inHeaders.getHeader(JoseConstants.HEADER_JSON_WEB_KEY) != null) { + JsonWebKey publicJwk = inHeaders.getJsonWebKey(); + if (inHeaderKid != null && !inHeaderKid.equals(publicJwk.getKeyId()) + || !MessageUtils.getContextualBoolean(m, JoseConstants.RSSEC_ACCEPT_PUBLIC_KEY_PROP, true)) { + throw new JwsException(JwsException.Error.INVALID_KEY); + } + return getSignatureVerifier(publicJwk, + inHeaders.getSignatureAlgorithm()); + } else if (inHeaders.getHeader(JoseConstants.HEADER_X509_CHAIN) != null) { + List<X509Certificate> chain = KeyManagementUtils.toX509CertificateChain(inHeaders.getX509Chain()); + KeyManagementUtils.validateCertificateChain(props, chain); + return getPublicKeySignatureVerifier(chain.get(0).getPublicKey(), + inHeaders.getSignatureAlgorithm()); + } + } + + if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { + JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.VERIFY, inHeaderKid); + if (jwk != null) { + String signatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); + theVerifier = getSignatureVerifier(jwk, SignatureAlgorithm.getAlgorithm(signatureAlgo)); + } + + } else { + String signatureAlgo = getSignatureAlgo(m, props, null, null); + if (SignatureAlgorithm.getAlgorithm(signatureAlgo) == SignatureAlgorithm.NONE + && SignatureAlgorithm.NONE.getJwaName().equals(inHeaders.getAlgorithm())) { + theVerifier = new NoneJwsSignatureVerifier(); + } else { + theVerifier = getPublicKeySignatureVerifier( + KeyManagementUtils.loadPublicKey(m, props), + SignatureAlgorithm.getAlgorithm(signatureAlgo)); + } + } + if (theVerifier == null && !ignoreNullVerifier) { + LOG.warning("Verifier is not available"); + throw new JwsException(JwsException.Error.NO_VERIFIER); + } + return theVerifier; + } + private static Properties loadJwsProperties(Message m, String propLoc) { + try { + return JoseUtils.loadProperties(propLoc, m.getExchange().getBus()); + } catch (Exception ex) { + LOG.warning("JWS init properties are not available"); + throw new JwsException(JwsException.Error.NO_INIT_PROPERTIES); + } + } + + @SuppressWarnings("deprecation") + public static String getSignatureAlgo(Message m, Properties props, String algo, String defaultAlgo) { + if (algo == null) { + if (defaultAlgo == null) { + defaultAlgo = AlgorithmUtils.RS_SHA_256_ALGO; + } + + // Check for deprecated identifier first + String sigAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_SIGNATURE_ALGORITHM); + if (sigAlgo == null) { + sigAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_SIGNATURE_ALGORITHM); + } + if (sigAlgo != null) { + return sigAlgo; + } + + // Otherwise check newer identifier + return KeyManagementUtils.getKeyAlgorithm(m, props, + JoseConstants.RSSEC_SIGNATURE_ALGORITHM, defaultAlgo); + } + return algo; + } + private static String getDefaultKeyAlgo(JsonWebKey jwk) { + KeyType keyType = jwk.getKeyType(); + if (KeyType.OCTET == keyType) { + return AlgorithmUtils.HMAC_SHA_256_ALGO; + } else if (KeyType.EC == keyType) { + return AlgorithmUtils.ES_SHA_256_ALGO; + } else { + return AlgorithmUtils.RS_SHA_256_ALGO; + } + } + private static JwsCompactConsumer verify(JwsSignatureVerifier v, String content) { + JwsCompactConsumer jws = new JwsCompactConsumer(content); + if (!jws.verifySignatureWith(v)) { + throw new JwsException(JwsException.Error.INVALID_SIGNATURE); + } + return jws; + } + private static String sign(JwsSignatureProvider jwsSig, String content, String ct) { + JwsHeaders headers = new JwsHeaders(); + if (ct != null) { + headers.setContentType(ct); + } + JwsCompactProducer jws = new JwsCompactProducer(headers, content); + jws.signWith(jwsSig); + return jws.getSignedEncodedJws(); + } + public static void validateJwsCertificateChain(List<X509Certificate> certs) { + + Message m = PhaseInterceptorChain.getCurrentMessage(); + Properties props = KeyManagementUtils.loadStoreProperties(m, true, + JoseConstants.RSSEC_SIGNATURE_IN_PROPS, + JoseConstants.RSSEC_SIGNATURE_PROPS); + KeyManagementUtils.validateCertificateChain(props, certs); + } + public static boolean isPayloadUnencoded(JwsHeaders jwsHeaders) { + return jwsHeaders.getPayloadEncodingStatus() == Boolean.FALSE; + } + + public static void checkSignatureKeySize(Key key) { + if (key instanceof RSAKey && ((RSAKey)key).getModulus().bitLength() < 2048) { + LOG.fine("A key of size: " + ((RSAKey)key).getModulus().bitLength() + + " was used with an RSA signature algorithm. 2048 is the minimum size that is accepted"); + throw new JwsException(JwsException.Error.INVALID_KEY); + } + } +} 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/jws/NoneJwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureProvider.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureProvider.java new file mode 100644 index 0000000..6ed31fd --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureProvider.java @@ -0,0 +1,55 @@ +/** + * 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.jws; + +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; + +public class NoneJwsSignatureProvider implements JwsSignatureProvider { + + @Override + public SignatureAlgorithm getAlgorithm() { + return SignatureAlgorithm.NONE; + } + + @Override + public JwsSignature createJwsSignature(JwsHeaders headers) { + return new NoneJwsSignature(); + } + + @Override + public byte[] sign(JwsHeaders headers, byte[] content) { + JwsSignature sig = createJwsSignature(headers); + sig.update(content, 0, content.length); + return sig.sign(); + } + private static class NoneJwsSignature implements JwsSignature { + + @Override + public void update(byte[] src, int off, int len) { + // complete + } + + @Override + public byte[] sign() { + return new byte[]{}; + } + + } +} + 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/jws/NoneJwsSignatureVerifier.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureVerifier.java new file mode 100644 index 0000000..441261b --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureVerifier.java @@ -0,0 +1,36 @@ +/** + * 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.jws; + +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; + +public class NoneJwsSignatureVerifier implements JwsSignatureVerifier { + + @Override + public boolean verify(JwsHeaders headers, String unsignedText, byte[] signature) { + return headers.getSignatureAlgorithm() == getAlgorithm() + && signature.length == 0; + } + + @Override + public SignatureAlgorithm getAlgorithm() { + return SignatureAlgorithm.NONE; + } + +} 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/jws/PrivateKeyJwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PrivateKeyJwsSignatureProvider.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PrivateKeyJwsSignatureProvider.java new file mode 100644 index 0000000..476d90f --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PrivateKeyJwsSignatureProvider.java @@ -0,0 +1,91 @@ +/** + * 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.jws; + +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rt.security.crypto.CryptoUtils; + +public class PrivateKeyJwsSignatureProvider extends AbstractJwsSignatureProvider { + private PrivateKey key; + private SecureRandom random; + private AlgorithmParameterSpec signatureSpec; + + public PrivateKeyJwsSignatureProvider(PrivateKey key, SignatureAlgorithm algo) { + this(key, null, algo); + } + public PrivateKeyJwsSignatureProvider(PrivateKey key, AlgorithmParameterSpec spec, SignatureAlgorithm algo) { + this(key, null, spec, algo); + } + public PrivateKeyJwsSignatureProvider(PrivateKey key, SecureRandom random, + AlgorithmParameterSpec spec, SignatureAlgorithm algo) { + super(algo); + this.key = key; + this.random = random; + this.signatureSpec = spec; + } + protected JwsSignature doCreateJwsSignature(JwsHeaders headers) { + final String sigAlgo = headers.getSignatureAlgorithm().getJwaName(); + final Signature s = CryptoUtils.getSignature(key, + AlgorithmUtils.toJavaName(sigAlgo), + random, + signatureSpec); + return doCreateJwsSignature(s); + } + protected JwsSignature doCreateJwsSignature(Signature s) { + return new PrivateKeyJwsSignature(s); + } + + @Override + protected boolean isValidAlgorithmFamily(String algo) { + return AlgorithmUtils.isRsaSign(algo); + } + + protected static class PrivateKeyJwsSignature implements JwsSignature { + private Signature s; + public PrivateKeyJwsSignature(Signature s) { + this.s = s; + } + @Override + public void update(byte[] src, int off, int len) { + try { + s.update(src, off, len); + } catch (SignatureException ex) { + throw new JwsException(JwsException.Error.SIGNATURE_FAILURE, ex); + } + } + + @Override + public byte[] sign() { + try { + return s.sign(); + } catch (SignatureException ex) { + throw new JwsException(JwsException.Error.SIGNATURE_FAILURE, 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/jws/PublicKeyJwsSignatureVerifier.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java new file mode 100644 index 0000000..65a2a15 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.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.jws; + +import java.security.PublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.util.logging.Logger; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rt.security.crypto.CryptoUtils; + +public class PublicKeyJwsSignatureVerifier implements JwsSignatureVerifier { + protected static final Logger LOG = LogUtils.getL7dLogger(PublicKeyJwsSignatureVerifier.class); + private PublicKey key; + private AlgorithmParameterSpec signatureSpec; + private SignatureAlgorithm supportedAlgo; + + public PublicKeyJwsSignatureVerifier(PublicKey key, SignatureAlgorithm supportedAlgorithm) { + this(key, null, supportedAlgorithm); + } + public PublicKeyJwsSignatureVerifier(PublicKey key, AlgorithmParameterSpec spec, SignatureAlgorithm supportedAlgo) { + this.key = key; + this.signatureSpec = spec; + this.supportedAlgo = supportedAlgo; + JwsUtils.checkSignatureKeySize(key); + } + @Override + public boolean verify(JwsHeaders headers, String unsignedText, byte[] signature) { + try { + return CryptoUtils.verifySignature(StringUtils.toBytesUTF8(unsignedText), + signature, + key, + AlgorithmUtils.toJavaName(checkAlgorithm( + headers.getSignatureAlgorithm())), + signatureSpec); + } catch (Exception ex) { + LOG.warning("Invalid signature: " + ex.getMessage()); + throw new JwsException(JwsException.Error.INVALID_SIGNATURE, ex); + } + } + protected String checkAlgorithm(SignatureAlgorithm sigAlgo) { + String algo = sigAlgo.getJwaName(); + if (algo == null) { + LOG.warning("Signature algorithm is not set"); + throw new JwsException(JwsException.Error.ALGORITHM_NOT_SET); + } + if (!isValidAlgorithmFamily(algo) + || !algo.equals(supportedAlgo.getJwaName())) { + LOG.warning("Invalid signature algorithm: " + algo); + throw new JwsException(JwsException.Error.INVALID_ALGORITHM); + } + return algo; + } + protected boolean isValidAlgorithmFamily(String algo) { + return AlgorithmUtils.isRsaSign(algo); + } + @Override + public SignatureAlgorithm getAlgorithm() { + return supportedAlgo; + } + +} 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/jwt/AbstractJoseJwtConsumer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/AbstractJoseJwtConsumer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/AbstractJoseJwtConsumer.java new file mode 100644 index 0000000..daea97b --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/AbstractJoseJwtConsumer.java @@ -0,0 +1,106 @@ +/** + * 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.jwt; + +import org.apache.cxf.rs.security.jose.common.AbstractJoseConsumer; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jws.JwsHeaders; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; + +public abstract class AbstractJoseJwtConsumer extends AbstractJoseConsumer { + private boolean jwsRequired = true; + private boolean jweRequired; + + + protected JwtToken getJwtToken(String wrappedJwtToken) { + return getJwtToken(wrappedJwtToken, null, null); + } + protected JwtToken getJwtToken(String wrappedJwtToken, + JweDecryptionProvider jweDecryptor, + JwsSignatureVerifier theSigVerifier) { + if (!isJwsRequired() && !isJweRequired()) { + throw new JwtException("Unable to process JWT"); + } + + if (isJweRequired()) { + if (jweDecryptor == null) { + jweDecryptor = getInitializedDecryptionProvider(); + } + if (jweDecryptor == null) { + throw new JwtException("Unable to decrypt JWT"); + } + + if (!isJwsRequired()) { + return new JweJwtCompactConsumer(wrappedJwtToken).decryptWith(jweDecryptor); + } + wrappedJwtToken = jweDecryptor.decrypt(wrappedJwtToken).getContentText(); + } + + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(wrappedJwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + if (isJwsRequired()) { + if (theSigVerifier == null) { + theSigVerifier = getInitializedSignatureVerifier(jwt); + } + if (theSigVerifier == null) { + throw new JwtException("Unable to validate JWT"); + } + + if (!jwtConsumer.verifySignatureWith(theSigVerifier)) { + throw new JwtException("Invalid Signature"); + } + } + + validateToken(jwt); + return jwt; + } + protected JwsSignatureVerifier getInitializedSignatureVerifier(JwtToken jwt) { + if (super.getJwsVerifier() != null) { + return super.getJwsVerifier(); + } + + if (jwt.getHeaders() instanceof JwsHeaders) { + return JwsUtils.loadSignatureVerifier((JwsHeaders)jwt.getHeaders(), false); + } + + return super.getInitializedSignatureVerifier(); + } + protected void validateToken(JwtToken jwt) { + } + public boolean isJwsRequired() { + return jwsRequired; + } + + public void setJwsRequired(boolean jwsRequired) { + this.jwsRequired = jwsRequired; + } + + public boolean isJweRequired() { + return jweRequired; + } + + public void setJweRequired(boolean jweRequired) { + this.jweRequired = jweRequired; + } + +} 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/jwt/AbstractJoseJwtProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/AbstractJoseJwtProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/AbstractJoseJwtProducer.java new file mode 100644 index 0000000..a5f5c37 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/AbstractJoseJwtProducer.java @@ -0,0 +1,90 @@ +/** + * 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.jwt; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.rs.security.jose.common.AbstractJoseProducer; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweJwtCompactProducer; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; + +public abstract class AbstractJoseJwtProducer extends AbstractJoseProducer { + private boolean jwsRequired = true; + private boolean jweRequired; + + protected String processJwt(JwtToken jwt) { + return processJwt(jwt, null, null); + } + protected String processJwt(JwtToken jwt, + JweEncryptionProvider theEncProvider, + JwsSignatureProvider theSigProvider) { + if (!isJwsRequired() && !isJweRequired()) { + throw new JwtException("Unable to secure JWT"); + } + String data = null; + + if (isJweRequired() && theEncProvider == null) { + theEncProvider = getInitializedEncryptionProvider(); + if (theEncProvider == null) { + throw new JwtException("Unable to encrypt JWT"); + } + } + + if (isJwsRequired()) { + JwsJwtCompactProducer jws = new JwsJwtCompactProducer(jwt); + if (jws.isPlainText()) { + data = jws.getSignedEncodedJws(); + } else { + if (theSigProvider == null) { + theSigProvider = getInitializedSignatureProvider(); + } + + if (theSigProvider == null) { + throw new JwtException("Unable to sign JWT"); + } + + data = jws.signWith(theSigProvider); + } + if (theEncProvider != null) { + data = theEncProvider.encrypt(StringUtils.toBytesUTF8(data), null); + } + } else { + JweJwtCompactProducer jwe = new JweJwtCompactProducer(jwt); + data = jwe.encryptWith(theEncProvider); + } + return data; + } + + public boolean isJwsRequired() { + return jwsRequired; + } + + public void setJwsRequired(boolean jwsRequired) { + this.jwsRequired = jwsRequired; + } + + public boolean isJweRequired() { + return jweRequired; + } + + public void setJweRequired(boolean jweRequired) { + this.jweRequired = jweRequired; + } +} 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/jwt/JwtClaims.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtClaims.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtClaims.java new file mode 100644 index 0000000..6fcc85d --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtClaims.java @@ -0,0 +1,102 @@ +/** + * 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.jwt; + +import java.util.Map; + +import org.apache.cxf.jaxrs.json.basic.JsonMapObject; + + + + +public class JwtClaims extends JsonMapObject { + + public JwtClaims() { + } + + public JwtClaims(Map<String, Object> values) { + super(values); + } + + public void setIssuer(String issuer) { + setClaim(JwtConstants.CLAIM_ISSUER, issuer); + } + + public String getIssuer() { + return (String)getClaim(JwtConstants.CLAIM_ISSUER); + } + + public void setSubject(String subject) { + setClaim(JwtConstants.CLAIM_SUBJECT, subject); + } + + public String getSubject() { + return (String)getClaim(JwtConstants.CLAIM_SUBJECT); + } + + public void setAudience(String audience) { + setClaim(JwtConstants.CLAIM_AUDIENCE, audience); + } + + public String getAudience() { + return (String)getClaim(JwtConstants.CLAIM_AUDIENCE); + } + + public void setExpiryTime(Long expiresIn) { + setClaim(JwtConstants.CLAIM_EXPIRY, expiresIn); + } + + public Long getExpiryTime() { + return getLongProperty(JwtConstants.CLAIM_EXPIRY); + } + + public void setNotBefore(Long notBefore) { + setClaim(JwtConstants.CLAIM_NOT_BEFORE, notBefore); + } + + public Long getNotBefore() { + return getLongProperty(JwtConstants.CLAIM_NOT_BEFORE); + } + + public void setIssuedAt(Long issuedAt) { + setClaim(JwtConstants.CLAIM_ISSUED_AT, issuedAt); + } + + public Long getIssuedAt() { + return getLongProperty(JwtConstants.CLAIM_ISSUED_AT); + } + + public void setTokenId(String id) { + setClaim(JwtConstants.CLAIM_JWT_ID, id); + } + + public String getTokenId() { + return (String)getClaim(JwtConstants.CLAIM_JWT_ID); + } + + public JwtClaims setClaim(String name, Object value) { + setProperty(name, value); + return this; + } + + public Object getClaim(String name) { + return getProperty(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/jwt/JwtConstants.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtConstants.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtConstants.java new file mode 100644 index 0000000..d0a663d --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtConstants.java @@ -0,0 +1,39 @@ +/** + * 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.jwt; + +public final class JwtConstants { + + public static final String CLAIM_ISSUER = "iss"; + public static final String CLAIM_SUBJECT = "sub"; + public static final String CLAIM_AUDIENCE = "aud"; + public static final String CLAIM_EXPIRY = "exp"; + public static final String CLAIM_NOT_BEFORE = "nbf"; + public static final String CLAIM_ISSUED_AT = "iat"; + public static final String CLAIM_JWT_ID = "jti"; + + public static final String JWT_TOKEN = "jwt.token"; + public static final String JWT_CLAIMS = "jwt.claims"; + + + private JwtConstants() { + + } +} 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/jwt/JwtException.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtException.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtException.java new file mode 100644 index 0000000..5b8db17 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtException.java @@ -0,0 +1,35 @@ +/** + * 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.jwt; + +import org.apache.cxf.rs.security.jose.common.JoseException; + +public class JwtException extends JoseException { + + private static final long serialVersionUID = 4118589816228511524L; + public JwtException() { + + } + public JwtException(String error) { + super(error); + } + public JwtException(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/jwt/JwtToken.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtToken.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtToken.java new file mode 100644 index 0000000..069b8f2 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtToken.java @@ -0,0 +1,56 @@ +/** + * 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.jwt; + +import org.apache.cxf.rs.security.jose.common.JoseHeaders; + + + +public class JwtToken { + private JoseHeaders headers; + private JwtClaims claims; + public JwtToken(JwtClaims claims) { + this(new JoseHeaders() { }, claims); + } + public JwtToken(JoseHeaders headers, JwtClaims claims) { + this.headers = headers; + this.claims = claims; + } + public JoseHeaders getHeaders() { + return headers; + } + public JwtClaims getClaims() { + return claims; + } + public Object getHeader(String name) { + return headers.getHeader(name); + } + public Object getClaim(String name) { + return claims.getClaim(name); + } + public int hashCode() { + return headers.hashCode() + 37 * claims.hashCode(); + } + + public boolean equals(Object obj) { + return obj instanceof JwtToken + && ((JwtToken)obj).headers.equals(this.headers) + && ((JwtToken)obj).claims.equals(this.claims); + } +}
