cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From serg...@apache.org
Subject cxf git commit: [CXF-6202] Attempting to fix a der conversion routine and applying a 2nd patch from Daniel Torkian
Date Wed, 21 Jan 2015 23:06:55 GMT
Repository: cxf
Updated Branches:
  refs/heads/master 07c6322a5 -> 43a42f25e


[CXF-6202] Attempting to fix a der conversion routine and applying a 2nd patch from Daniel
Torkian


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/43a42f25
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/43a42f25
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/43a42f25

Branch: refs/heads/master
Commit: 43a42f25ee36c6dad87f600721c78bbcb5650a8d
Parents: 07c6322
Author: Sergey Beryozkin <sberyozkin@talend.com>
Authored: Wed Jan 21 23:06:34 2015 +0000
Committer: Sergey Beryozkin <sberyozkin@talend.com>
Committed: Wed Jan 21 23:06:34 2015 +0000

----------------------------------------------------------------------
 .../jose/jws/EcDsaJwsSignatureProvider.java     |  62 ++--
 .../jose/jws/EcDsaJwsSignatureVerifier.java     |  26 +-
 .../security/jose/jws/JwsCompactConsumer.java   |   3 +
 .../security/jose/jws/JwsCompactProducer.java   |   3 +
 .../rs/security/jose/jws/JwsJsonConsumer.java   |   3 +
 .../jose/cookbook/JwsJoseCookBookTest.java      | 298 +++++++++++++++++++
 6 files changed, 366 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/43a42f25/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 3e4fcda..431c211 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
@@ -42,42 +42,68 @@ public class EcDsaJwsSignatureProvider extends PrivateKeyJwsSignatureProvider
{
     }
     @Override
     protected JwsSignature doCreateJwsSignature(Signature s) {
-        return new EcDsaPrivateKeyJwsSignature(s);
+        return new EcDsaPrivateKeyJwsSignature(s, 
+            EcDsaJwsSignatureVerifier.SIGNATURE_LENGTH_MAP.get(super.getAlgorithm()));
     }
     
     protected static class EcDsaPrivateKeyJwsSignature extends PrivateKeyJwsSignature {
-        public EcDsaPrivateKeyJwsSignature(Signature s) {
+        private int outLen;
+        public EcDsaPrivateKeyJwsSignature(Signature s, int outLen) {
             super(s);
+            this.outLen = outLen;
         }
         @Override
         public byte[] sign() {
-            byte[] der = super.sign();
-            return jcaOutputToJoseOutput(der);
+            byte[] jcaDer = super.sign();
+            return jcaOutputToJoseOutput(outLen, jcaDer);
         }
     }
     
-    private static byte[] jcaOutputToJoseOutput(byte jcaDer[]) {
+    private static byte[] jcaOutputToJoseOutput(int jwsSignatureLen, 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)
+        // 48 (SEQUENCE) + Total Length (1 or 2 bytes, the 1st byte is -127 if 2 bytes) 
+        // + R & S triples, where both triples are represented as 
+        // 2(INTEGER TYPE) + length + the actual sequence of a given length;
+        // The sequence might have the extra leading zeroes which need to be skipped
+        int requiredPartLen = jwsSignatureLen / 2;
         
-        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 rsDataBlockStart = jcaDer[1] == -127 ? 4 : 3;
+        int rPartLen = jcaDer[rsDataBlockStart];
+        int rDataBlockStart = rsDataBlockStart + 1;
+        int rPartLenDiff = rPartLen - requiredPartLen; 
+        int rValueStart = rDataBlockStart + getDataBlockOffset(jcaDer, rDataBlockStart, rPartLenDiff);
         
-        int partLen = rPartLen - rOffset;
-        byte[] result = new byte[partLen * 2]; 
-        System.arraycopy(jcaDer, rValueStart, result, 0, partLen);
-        System.arraycopy(jcaDer, sValueStart, result, partLen, partLen);
+        int sPartStart = rDataBlockStart + rPartLen;
+        int sPartLen = jcaDer[sPartStart + 1];
+        int sPartLenDiff = sPartLen - requiredPartLen; 
+        int sDataBlockStart = sPartStart + 2;
+        int sValueStart = sDataBlockStart + getDataBlockOffset(jcaDer, sDataBlockStart, sPartLenDiff);
+                
+        byte[] result = new byte[jwsSignatureLen]; 
+        System.arraycopy(jcaDer, rValueStart, result, 
+            rPartLenDiff < 0 ? rPartLenDiff * -1 : 0, 
+            rPartLenDiff < 0 ? requiredPartLen + rPartLenDiff : requiredPartLen);
+        System.arraycopy(jcaDer, sValueStart, result, 
+            sPartLenDiff < 0 ? requiredPartLen + sPartLenDiff * -1 : requiredPartLen,

+            sPartLenDiff < 0 ? requiredPartLen + sPartLenDiff : requiredPartLen);
         return result;
     }
+    private static int getDataBlockOffset(byte[] jcaDer, int blockStart, int partLenDiff)
{
+        // ECDSA productions have 64, 96 or 132 output lengths. The R and S parts would be
32, 48 or 66 bytes each.
+        // If it is 32 or 48 bytes then we may have occasional extra zeroes in the JCA DER
output
+        int i = 0;
+        if (partLenDiff > 0) {
+            while (i < partLenDiff && jcaDer[blockStart + i] == 0) {
+                i++;
+            }
+        }
+        return i;
+    }
+    
+    
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/43a42f25/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 10341e5..8c788bc 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
@@ -27,7 +27,7 @@ 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 final Map<String, Integer> SIGNATURE_LENGTH_MAP;
     static {
         SIGNATURE_LENGTH_MAP = new HashMap<String, Integer>();
         SIGNATURE_LENGTH_MAP.put(Algorithm.SHA256withECDSA.getJwtName(), 64);
@@ -54,21 +54,25 @@ public class EcDsaJwsSignatureVerifier extends PublicKeyJwsSignatureVerifier
{
     }
     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];
+        int rPartLen = partLen + rOffset;
+        int sPartLen = partLen + sOffset;
+        int totalLenBytesCount = joseSig.length > 127 ? 2 : 1;
+        int rPartStart = 1 + totalLenBytesCount + 2;
+        byte[] der = new byte[rPartStart + 2 + rPartLen + sPartLen];
         der[0] = 48;
-        der[1] = (byte)(der.length - 2);
-        der[2] = 2;
-        der[3] = (byte)(partLen + rOffset);
-        int sPartStart = 4 + der[3];
+        if (totalLenBytesCount == 2) {
+            der[1] = -127;
+        }
+        der[totalLenBytesCount] = (byte)(der.length - (1 + totalLenBytesCount));
+        der[totalLenBytesCount + 1] = 2;
+        der[totalLenBytesCount + 2] = (byte)rPartLen;
+        int sPartStart = rPartStart + rPartLen;
         der[sPartStart] = 2;
-        der[sPartStart + 1] = (byte)(partLen + sOffset);
-        System.arraycopy(joseSig, 0, der, 4 + rOffset, partLen);
+        der[sPartStart + 1] = (byte)sPartLen;
+        System.arraycopy(joseSig, 0, der, rPartStart + rOffset, partLen);
         System.arraycopy(joseSig, partLen, der, sPartStart + 2 + sOffset, partLen);
         return der;
     }
-    
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/43a42f25/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java
b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java
index 5ca4861..5589f56 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java
@@ -104,6 +104,9 @@ public class JwsCompactConsumer {
     public boolean verifySignatureWith(JsonWebKey key) {
         return verifySignatureWith(JwsUtils.getSignatureVerifier(key));
     }
+    public boolean verifySignatureWith(JsonWebKey key, String algo) {
+        return verifySignatureWith(JwsUtils.getSignatureVerifier(key, algo));
+    }
     public boolean verifySignatureWith(RSAPublicKey key, String algo) {
         return verifySignatureWith(JwsUtils.getRSAKeySignatureVerifier(key, algo));
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/43a42f25/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java
b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java
index fa4674e..0bed1c4 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java
@@ -64,6 +64,9 @@ public class JwsCompactProducer {
         }
         return plainRep;
     }
+    public String getEncodedSignature() {
+        return signature;
+    }
     public String getSignedEncodedJws() {
         return getSignedEncodedJws(false);
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/43a42f25/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java
b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java
index c1690d8..2445062 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumer.java
@@ -171,6 +171,9 @@ public class JwsJsonConsumer {
     public boolean verifySignatureWith(JsonWebKey key) {
         return verifySignatureWith(JwsUtils.getSignatureVerifier(key));
     }
+    public boolean verifySignatureWith(JsonWebKey key, String algo) {
+        return verifySignatureWith(JwsUtils.getSignatureVerifier(key, algo));
+    }
     public JwsJsonProducer toProducer() {
         JwsJsonProducer p = new JwsJsonProducer(getDecodedJwsPayload());
         p.getSignatureEntries().addAll(signatureEntries);

http://git-wip-us.apache.org/repos/asf/cxf/blob/43a42f25/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java
b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java
new file mode 100644
index 0000000..2598963
--- /dev/null
+++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java
@@ -0,0 +1,298 @@
+/**
+ * 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.cookbook;
+
+import java.io.InputStream;
+import java.security.Security;
+import java.util.List;
+
+import javax.crypto.Cipher;
+
+import org.apache.cxf.common.util.Base64UrlUtility;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.provider.json.JsonMapObjectReaderWriter;
+import org.apache.cxf.rs.security.jose.JoseConstants;
+import org.apache.cxf.rs.security.jose.JoseHeaders;
+import org.apache.cxf.rs.security.jose.jwa.Algorithm;
+import org.apache.cxf.rs.security.jose.jwk.JsonWebKey;
+import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
+import org.apache.cxf.rs.security.jose.jwk.JwkUtils;
+import org.apache.cxf.rs.security.jose.jws.EcDsaJwsSignatureProvider;
+import org.apache.cxf.rs.security.jose.jws.JwsCompactConsumer;
+import org.apache.cxf.rs.security.jose.jws.JwsCompactProducer;
+import org.apache.cxf.rs.security.jose.jws.JwsJsonConsumer;
+import org.apache.cxf.rs.security.jose.jws.JwsJsonProducer;
+import org.apache.cxf.rs.security.jose.jws.JwsJsonProtectedHeader;
+import org.apache.cxf.rs.security.jose.jws.JwsUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JwsJoseCookBookTest {
+    private static final String PAYLOAD = "It’s a dangerous business, Frodo, going out
your door. "
+        + "You step onto the road, and if you don't keep your feet, "
+        + "there’s no knowing where you might be swept off to.";
+    private static final String ENCODED_PAYLOAD = "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH"
+        + "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk"
+        + "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm"
+        + "UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4";
+    private static final String RSA_KID_VALUE = "bilbo.baggins@hobbiton.example";
+    private static final String RSA_V1_5_SIGNATURE_PROTECTED_HEADER =
+          "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX"
+        + "hhbXBsZSJ9";
+    private static final String RSA_V1_5_SIGNATURE_PROTECTED_HEADER_JSON = ("{"
+        + "\"alg\": \"RS256\","
+        + "\"kid\": \"bilbo.baggins@hobbiton.example\""
+        + "}").replaceAll(" ", "");
+    private static final String RSA_V1_5_SIGNATURE_VALUE =
+          "MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmK"
+        + "ZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4J"
+        + "IwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8w"
+        + "W1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluP"
+        + "xUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_f"
+        + "cIe8u9ipH84ogoree7vjbU5y18kDquDg";
+    private static final String RSA_V1_5_JSON_GENERAL_SERIALIZATION = ("{"
+        + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg"
+        + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h"
+        + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi"
+        + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m"
+        + "ZiB0by4\","
+        + "\"signatures\": ["
+        + "{"
+        + "\"protected\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2"
+        + "dpbnNAaG9iYml0b24uZXhhbXBsZSJ9\","
+        + "\"signature\": \"MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHo"
+        + "xnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII"
+        + "7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0Rnlt"
+        + "uYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPo"
+        + "cSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxU"
+        + "Ahb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJush"
+        + "Z41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg\""
+        + "}"
+        + "]"
+        + "}").replaceAll(" ", "");
+    private static final String RSA_V1_5_JSON_FLATTENED_SERIALIZATION =  ("{"
+        + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg"
+        + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h"
+        + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi"
+        + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m"
+        + "ZiB0by4\","
+        + "\"protected\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbn"
+        + "NAaG9iYml0b24uZXhhbXBsZSJ9\","
+        + "\"signature\": \"MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2"
+        + "e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84w"
+        + "nB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_q"
+        + "HRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9U"
+        + "zpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0"
+        + "KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogore"
+        + "e7vjbU5y18kDquDg\""
+        + "}").replaceAll(" ", "");
+    private static final String RSA_PSS_SIGNATURE_PROTECTED_HEADER_JSON = ("{"
+        + "\"alg\": \"PS384\","
+        + "\"kid\": \"bilbo.baggins@hobbiton.example\""
+        + "}").replaceAll(" ", "");
+    private static final String RSA_PSS_SIGNATURE_PROTECTED_HEADER =
+          "eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX"
+        + "hhbXBsZSJ9";
+    private static final String RSA_PSS_SIGNATURE_VALUE =
+          "cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2I"
+        + "pN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXU"
+        + "vdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRX"
+        + "e8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT"
+        + "0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a"
+        + "6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw";
+    private static final String RSA_PSS_JSON_GENERAL_SERIALIZATION = ("{"
+        + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg"
+        + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h"
+        + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi"
+        + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m"
+        + "ZiB0by4\","
+        + "\"signatures\": ["
+        + "{"
+        + "\"protected\": \"eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2"
+        + "dpbnNAaG9iYml0b24uZXhhbXBsZSJ9\","
+        + "\"signature\": \"cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy"
+        + "42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5"
+        + "dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz2"
+        + "8zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vd"
+        + "z0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0q"
+        + "I0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uT"
+        + "OcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw\""
+        + "}"
+        + "]"
+        + "}").replace(" ", "");
+    private static final String RSA_PSS_JSON_FLATTENED_SERIALIZATION = ("{"
+        + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg"
+        + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h"
+        + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi"
+        + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m"
+        + "ZiB0by4\","
+        + "\"protected\": \"eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbn"
+        + "NAaG9iYml0b24uZXhhbXBsZSJ9\","
+        + "\"signature\": \"cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42mi"
+        + "Ah2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllV"
+        + "o6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf"
+        + "8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9s"
+        + "hnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQT"
+        + "lqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD"
+        + "4ifKo8DYM-X72Eaw\""
+        + "}").replace(" ", "");
+    private static final String ECDSA_KID_VALUE = "bilbo.baggins@hobbiton.example";
+    private static final String ECDSA_SIGNATURE_PROTECTED_HEADER_JSON = ("{"
+        + "\"alg\": \"ES512\","
+        + "\"kid\": \"bilbo.baggins@hobbiton.example\""
+        + "}").replace(" ", "");
+    private static final String ECSDA_SIGNATURE_PROTECTED_HEADER =
+              "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX"
+            + "hhbXBsZSJ9";
+    
+    @Test
+    public void testEncodedPayload() throws Exception {
+        assertEquals(Base64UrlUtility.encode(PAYLOAD), ENCODED_PAYLOAD);
+    }
+    @Test
+    public void testRSAv15Signature() throws Exception {
+        JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD);
+        compactProducer.getJoseHeaders().setAlgorithm(JoseConstants.RS_SHA_256_ALGO);
+        compactProducer.getJoseHeaders().setKeyId(RSA_KID_VALUE);
+        JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter();
+        assertEquals(reader.toJson(compactProducer.getJoseHeaders().asMap()), RSA_V1_5_SIGNATURE_PROTECTED_HEADER_JSON);
+        assertEquals(compactProducer.getUnsignedEncodedJws(),
+                RSA_V1_5_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD);
+        JsonWebKeys jwks = readKeySet("cookbookPrivateSet.txt");
+        List<JsonWebKey> keys = jwks.getKeys();
+        JsonWebKey rsaKey = keys.get(1);
+        compactProducer.signWith(rsaKey);
+        assertEquals(compactProducer.getSignedEncodedJws(),
+                RSA_V1_5_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD + "." + RSA_V1_5_SIGNATURE_VALUE);
+        JwsCompactConsumer compactConsumer = new JwsCompactConsumer(compactProducer.getSignedEncodedJws());
+        JsonWebKeys publicJwks = readKeySet("cookbookPublicSet.txt");
+        List<JsonWebKey> publicKeys = publicJwks.getKeys();
+        JsonWebKey rsaPublicKey = publicKeys.get(1);
+        assertTrue(compactConsumer.verifySignatureWith(rsaPublicKey, JoseConstants.RS_SHA_256_ALGO));
+
+        JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD);
+        assertEquals(jsonProducer.getPlainPayload(), PAYLOAD);
+        assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD);
+        JoseHeaders joseHeaders = new JoseHeaders();
+        joseHeaders.setAlgorithm(JoseConstants.RS_SHA_256_ALGO);
+        joseHeaders.setKeyId(RSA_KID_VALUE);
+        JwsJsonProtectedHeader protectedHeader = new JwsJsonProtectedHeader(joseHeaders);
+        jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, JoseConstants.RS_SHA_256_ALGO),
protectedHeader);
+        assertEquals(jsonProducer.getJwsJsonSignedDocument(), RSA_V1_5_JSON_GENERAL_SERIALIZATION);
+        JwsJsonConsumer jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument());
+        assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, JoseConstants.RS_SHA_256_ALGO));
+
+        jsonProducer = new JwsJsonProducer(PAYLOAD, true);
+        jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, JoseConstants.RS_SHA_256_ALGO),
protectedHeader);
+        assertEquals(jsonProducer.getJwsJsonSignedDocument(), RSA_V1_5_JSON_FLATTENED_SERIALIZATION);
+        jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument());
+        assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, JoseConstants.RS_SHA_256_ALGO));
+    }
+    @Test
+    public void testRSAPSSSignature() throws Exception {
+        try {
+            Cipher.getInstance(Algorithm.PS_SHA_384_JAVA);
+        } catch (Throwable t) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+
+        JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD);
+        compactProducer.getJoseHeaders().setAlgorithm(JoseConstants.PS_SHA_384_ALGO);
+        compactProducer.getJoseHeaders().setKeyId(RSA_KID_VALUE);
+        JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter();
+        assertEquals(reader.toJson(compactProducer.getJoseHeaders().asMap()), RSA_PSS_SIGNATURE_PROTECTED_HEADER_JSON);
+        assertEquals(compactProducer.getUnsignedEncodedJws(),
+                RSA_PSS_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD);
+        JsonWebKeys jwks = readKeySet("cookbookPrivateSet.txt");
+        List<JsonWebKey> keys = jwks.getKeys();
+        JsonWebKey rsaKey = keys.get(1);
+        compactProducer.signWith(rsaKey);
+        assertEquals(compactProducer.getSignedEncodedJws().length(),
+                (RSA_PSS_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD + "." + RSA_PSS_SIGNATURE_VALUE).length());
+        JwsCompactConsumer compactConsumer = new JwsCompactConsumer(compactProducer.getSignedEncodedJws());
+        JsonWebKeys publicJwks = readKeySet("cookbookPublicSet.txt");
+        List<JsonWebKey> publicKeys = publicJwks.getKeys();
+        JsonWebKey rsaPublicKey = publicKeys.get(1);
+        assertTrue(compactConsumer.verifySignatureWith(rsaPublicKey, JoseConstants.PS_SHA_384_ALGO));
+
+        JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD);
+        assertEquals(jsonProducer.getPlainPayload(), PAYLOAD);
+        assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD);
+        JoseHeaders joseHeaders = new JoseHeaders();
+        joseHeaders.setAlgorithm(JoseConstants.PS_SHA_384_ALGO);
+        joseHeaders.setKeyId(RSA_KID_VALUE);
+        JwsJsonProtectedHeader protectedHeader = new JwsJsonProtectedHeader(joseHeaders);
+        jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, JoseConstants.PS_SHA_384_ALGO),
protectedHeader);
+        assertEquals(jsonProducer.getJwsJsonSignedDocument().length(), RSA_PSS_JSON_GENERAL_SERIALIZATION.length());
+        JwsJsonConsumer jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument());
+        assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, JoseConstants.PS_SHA_384_ALGO));
+
+        jsonProducer = new JwsJsonProducer(PAYLOAD, true);
+        jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, JoseConstants.PS_SHA_384_ALGO),
protectedHeader);
+        assertEquals(jsonProducer.getJwsJsonSignedDocument().length(), RSA_PSS_JSON_FLATTENED_SERIALIZATION.length());
+        jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument());
+        assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, JoseConstants.PS_SHA_384_ALGO));
+
+        Security.removeProvider(BouncyCastleProvider.class.getName());
+    }
+    @Test
+    public void testECDSASignature() throws Exception {
+        
+        try {
+            Cipher.getInstance(Algorithm.ES_SHA_512_JAVA);
+        } catch (Throwable t) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+        try {
+            JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD);
+            compactProducer.getJoseHeaders().setAlgorithm(JoseConstants.ES_SHA_512_ALGO);
+            compactProducer.getJoseHeaders().setKeyId(ECDSA_KID_VALUE);
+            JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter();
+            assertEquals(reader.toJson(compactProducer.getJoseHeaders().asMap()), 
+                         ECDSA_SIGNATURE_PROTECTED_HEADER_JSON);
+            assertEquals(compactProducer.getUnsignedEncodedJws(),
+                    ECSDA_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD);
+            JsonWebKeys jwks = readKeySet("cookbookPrivateSet.txt");
+            List<JsonWebKey> keys = jwks.getKeys();
+            JsonWebKey ecKey = keys.get(0);
+            compactProducer.signWith(new EcDsaJwsSignatureProvider(JwkUtils.toECPrivateKey(ecKey),
+                    JoseConstants.ES_SHA_512_ALGO));
+            assertEquals(compactProducer.getUnsignedEncodedJws(), 
+                         ECSDA_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD);
+            assertEquals(132, Base64UrlUtility.decode(compactProducer.getEncodedSignature()).length);
+            
+            JwsCompactConsumer compactConsumer = new JwsCompactConsumer(compactProducer.getSignedEncodedJws());
+            JsonWebKeys publicJwks = readKeySet("cookbookPublicSet.txt");
+            List<JsonWebKey> publicKeys = publicJwks.getKeys();
+            JsonWebKey ecPublicKey = publicKeys.get(0);
+            assertTrue(compactConsumer.verifySignatureWith(ecPublicKey, JoseConstants.ES_SHA_512_ALGO));
+        } finally {
+            Security.removeProvider(BouncyCastleProvider.class.getName());
+        }
+    }
+    public JsonWebKeys readKeySet(String fileName) throws Exception {
+        InputStream is = JwsJoseCookBookTest.class.getResourceAsStream(fileName);
+        String s = IOUtils.readStringFromStream(is);
+        return JwkUtils.readJwkSet(s);
+    }
+}
\ No newline at end of file


Mime
View raw message