Repository: cxf Updated Branches: refs/heads/3.0.x-fixes 679e18b05 -> a2ab2aec8
Making the way the elliptic key signature is prepared JWS compliant Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/a2ab2aec Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/a2ab2aec Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/a2ab2aec Branch: refs/heads/3.0.x-fixes Commit: a2ab2aec8d4ee4d4a5c30296cb2306ef03057682 Parents: 679e18b Author: Sergey Beryozkin <[email protected]> Authored: Mon Nov 24 16:48:15 2014 +0000 Committer: Sergey Beryozkin <[email protected]> Committed: Mon Nov 24 16:49:10 2014 +0000 ---------------------------------------------------------------------- .../jose/jws/EcDsaJwsSignatureProvider.java | 41 ++++++++++++++++ .../jose/jws/EcDsaJwsSignatureVerifier.java | 37 +++++++++++++++ .../jws/PrivateKeyJwsSignatureProvider.java | 49 +++++++++++--------- .../security/jose/jws/JwsJsonConsumerTest.java | 2 +- 4 files changed, 107 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/a2ab2aec/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java index b6da904..3e4fcda 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.jose.jws; import java.security.SecureRandom; +import java.security.Signature; import java.security.interfaces.ECPrivateKey; import java.security.spec.AlgorithmParameterSpec; @@ -39,4 +40,44 @@ public class EcDsaJwsSignatureProvider extends PrivateKeyJwsSignatureProvider { protected boolean isValidAlgorithmFamily(String algo) { return Algorithm.isEcDsaSign(algo); } + @Override + protected JwsSignature doCreateJwsSignature(Signature s) { + return new EcDsaPrivateKeyJwsSignature(s); + } + + protected static class EcDsaPrivateKeyJwsSignature extends PrivateKeyJwsSignature { + public EcDsaPrivateKeyJwsSignature(Signature s) { + super(s); + } + @Override + public byte[] sign() { + byte[] der = super.sign(); + return jcaOutputToJoseOutput(der); + } + } + + private static byte[] jcaOutputToJoseOutput(byte jcaDer[]) { + // DER uses a pattern of type-length-value triplets + // http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One#Example_encoded_in_DER + + // The algorithm implementation guarantees the correct DER format so no extra validation + + // ECDSA signature production: + // 48 (SEQUENCE) + total length + R & S triples, where every triple is 2 + // (INTEGER TYPE + length + the actual integer) + + int rPartLen = jcaDer[3]; + int rOffset = rPartLen % 8; + int rValueStart = 4 + rOffset; + int sPartStart = 4 + rPartLen; + int sPartLen = jcaDer[sPartStart + 1]; + int sOffset = sPartLen % 8; + int sValueStart = sPartStart + 2 + sOffset; + + int partLen = rPartLen - rOffset; + byte[] result = new byte[partLen * 2]; + System.arraycopy(jcaDer, rValueStart, result, 0, partLen); + System.arraycopy(jcaDer, sValueStart, result, partLen, partLen); + return result; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/a2ab2aec/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java index 6670367..10341e5 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java @@ -20,10 +20,20 @@ package org.apache.cxf.rs.security.jose.jws; import java.security.PublicKey; import java.security.spec.AlgorithmParameterSpec; +import java.util.HashMap; +import java.util.Map; +import org.apache.cxf.rs.security.jose.JoseHeaders; import org.apache.cxf.rs.security.jose.jwa.Algorithm; public class EcDsaJwsSignatureVerifier extends PublicKeyJwsSignatureVerifier { + private static final Map<String, Integer> SIGNATURE_LENGTH_MAP; + static { + SIGNATURE_LENGTH_MAP = new HashMap<String, Integer>(); + SIGNATURE_LENGTH_MAP.put(Algorithm.SHA256withECDSA.getJwtName(), 64); + SIGNATURE_LENGTH_MAP.put(Algorithm.SHA384withECDSA.getJwtName(), 96); + SIGNATURE_LENGTH_MAP.put(Algorithm.SHA512withECDSA.getJwtName(), 132); + } public EcDsaJwsSignatureVerifier(PublicKey key, String supportedAlgo) { this(key, null, supportedAlgo); } @@ -31,7 +41,34 @@ public class EcDsaJwsSignatureVerifier extends PublicKeyJwsSignatureVerifier { super(key, spec, supportedAlgo); } @Override + public boolean verify(JoseHeaders headers, String unsignedText, byte[] signature) { + if (SIGNATURE_LENGTH_MAP.get(super.getAlgorithm()) != signature.length) { + throw new SecurityException(); + } + byte[] der = signatureToDer(signature); + return super.verify(headers, unsignedText, der); + } + @Override protected boolean isValidAlgorithmFamily(String algo) { return Algorithm.isEcDsaSign(algo); } + private static byte[] signatureToDer(byte joseSig[]) { + int partLen = joseSig.length / 2; + // 0 needs to be appended if the first byte is negative + int rOffset = joseSig[0] < 0 ? 1 : 0; + int sOffset = joseSig[partLen] < 0 ? 1 : 0; + + byte[] der = new byte[6 + joseSig.length + rOffset + sOffset]; + der[0] = 48; + der[1] = (byte)(der.length - 2); + der[2] = 2; + der[3] = (byte)(partLen + rOffset); + int sPartStart = 4 + der[3]; + der[sPartStart] = 2; + der[sPartStart + 1] = (byte)(partLen + sOffset); + System.arraycopy(joseSig, 0, der, 4 + rOffset, partLen); + System.arraycopy(joseSig, partLen, der, sPartStart + 2 + sOffset, partLen); + return der; + } + } http://git-wip-us.apache.org/repos/asf/cxf/blob/a2ab2aec/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PrivateKeyJwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PrivateKeyJwsSignatureProvider.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PrivateKeyJwsSignatureProvider.java index 784248d..1f38972 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PrivateKeyJwsSignatureProvider.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PrivateKeyJwsSignatureProvider.java @@ -51,27 +51,10 @@ public class PrivateKeyJwsSignatureProvider extends AbstractJwsSignatureProvider Algorithm.toJavaName(headers.getAlgorithm()), random, signatureSpec); - return new JwsSignature() { - - @Override - public void update(byte[] src, int off, int len) { - try { - s.update(src, off, len); - } catch (SignatureException ex) { - throw new SecurityException(); - } - } - - @Override - public byte[] sign() { - try { - return s.sign(); - } catch (SignatureException ex) { - throw new SecurityException(); - } - } - - }; + return doCreateJwsSignature(s); + } + protected JwsSignature doCreateJwsSignature(Signature s) { + return new PrivateKeyJwsSignature(s); } @Override protected void checkAlgorithm(String algo) { @@ -85,4 +68,28 @@ public class PrivateKeyJwsSignatureProvider extends AbstractJwsSignatureProvider return Algorithm.isRsaShaSign(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 SecurityException(); + } + } + + @Override + public byte[] sign() { + try { + return s.sign(); + } catch (SignatureException ex) { + throw new SecurityException(); + } + } + + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/a2ab2aec/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java index e46b5cd..ce611e2 100644 --- a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java @@ -69,7 +69,7 @@ public class JwsJsonConsumerTest extends Assert { assertEquals(KID_OF_THE_SECOND_SIGNER, secondKid); JsonWebKey ecKey = jwks.getKey(secondKid); assertNotNull(ecKey); - //assertTrue(sigEntries.get(1).verifySignatureWith(ecKey)); + assertTrue(sigEntries.get(1).verifySignatureWith(ecKey)); } public JsonWebKeys readKeySet(String fileName) throws Exception { InputStream is = JwsJsonConsumerTest.class.getResourceAsStream(fileName);
