You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2016/02/14 16:51:17 UTC

mina-sshd git commit: [SSHD-642] Pad RSA signatures with zeroes if necessary to complete the expected signature size

Repository: mina-sshd
Updated Branches:
  refs/heads/master 509c871ea -> ccb7b6058


[SSHD-642] Pad RSA signatures with zeroes if necessary to complete the expected signature size


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/ccb7b605
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/ccb7b605
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/ccb7b605

Branch: refs/heads/master
Commit: ccb7b605884779c4f973d669d89b25ffef205dec
Parents: 509c871
Author: Eugene Petrenko <eu...@gmail.com>
Authored: Sun Feb 14 17:51:06 2016 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Sun Feb 14 17:51:06 2016 +0200

----------------------------------------------------------------------
 .../common/signature/AbstractSignature.java     |  36 +++++-
 .../sshd/common/signature/SignatureDSA.java     |   1 +
 .../sshd/common/signature/SignatureECDSA.java   |   1 +
 .../sshd/common/signature/SignatureRSA.java     |  37 +++++-
 .../sshd/common/signature/SignatureRSATest.java | 119 +++++++++++++++++++
 5 files changed, 188 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ccb7b605/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
index e67748b..a22b97b 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/AbstractSignature.java
@@ -19,6 +19,7 @@
 package org.apache.sshd.common.signature;
 
 import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SignatureException;
@@ -36,7 +37,7 @@ import org.apache.sshd.common.util.buffer.BufferUtils;
  */
 public abstract class AbstractSignature implements Signature {
 
-    protected java.security.Signature signature;
+    private java.security.Signature signatureInstance;
     private final String algorithm;
 
     protected AbstractSignature(String algorithm) {
@@ -48,17 +49,40 @@ public abstract class AbstractSignature implements Signature {
         return algorithm;
     }
 
+    /**
+     * Initializes the internal signature instance
+     *
+     * @param algo The signature's algorithm
+     * @param forSigning If {@code true} then it is being initialized for signing,
+     * otherwise for verifying a signature
+     * @return The {@link java.security.Signature} instance
+     * @throws GeneralSecurityException if failed to initialize
+     */
+    protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+        return SecurityUtils.getSignature(algo);
+    }
+
+    /**
+     * @return The current {@link java.security.Signature} instance
+     * - {@code null} if not initialized
+     * @see #doInitSignature(String, boolean)
+     */
+    protected java.security.Signature getSignature() {
+        return signatureInstance;
+    }
 
     @Override
     public void initVerifier(PublicKey key) throws Exception {
-        signature = SecurityUtils.getSignature(getAlgorithm());
-        signature.initVerify(ValidateUtils.checkNotNull(key, "No public key provided"));
+        String algo = getAlgorithm();
+        signatureInstance = ValidateUtils.checkNotNull(doInitSignature(algo, false), "No signature instance create");
+        signatureInstance.initVerify(ValidateUtils.checkNotNull(key, "No public key provided"));
     }
 
     @Override
     public void initSigner(PrivateKey key) throws Exception {
-        signature = SecurityUtils.getSignature(getAlgorithm());
-        signature.initSign(ValidateUtils.checkNotNull(key, "No private key provided"));
+        String algo = getAlgorithm();
+        signatureInstance = ValidateUtils.checkNotNull(doInitSignature(algo, true), "No signature instance create");
+        signatureInstance.initSign(ValidateUtils.checkNotNull(key, "No private key provided"));
     }
 
     @Override
@@ -68,6 +92,7 @@ public abstract class AbstractSignature implements Signature {
 
     @Override
     public void update(byte[] hash, int off, int len) throws Exception {
+        java.security.Signature signature = ValidateUtils.checkNotNull(getSignature(), "Signature not initialized");
         signature.update(hash, off, len);
     }
 
@@ -112,6 +137,7 @@ public abstract class AbstractSignature implements Signature {
     }
 
     protected boolean doVerify(byte[] data) throws SignatureException {
+        java.security.Signature signature = ValidateUtils.checkNotNull(getSignature(), "Signature not initialized");
         return signature.verify(data);
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ccb7b605/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
index 4007fad..8f95a46 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureDSA.java
@@ -54,6 +54,7 @@ public class SignatureDSA extends AbstractSignature {
 
     @Override
     public byte[] sign() throws Exception {
+        java.security.Signature signature = ValidateUtils.checkNotNull(getSignature(), "Signature not initialized");
         byte[] sig = signature.sign();
 
         try (DERParser parser = new DERParser(sig)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ccb7b605/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
index e9b49c5..41bb88e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureECDSA.java
@@ -66,6 +66,7 @@ public class SignatureECDSA extends AbstractSignature {
 
     @Override
     public byte[] sign() throws Exception {
+        java.security.Signature signature = ValidateUtils.checkNotNull(getSignature(), "Signature not initialized");
         byte[] sig = signature.sign();
 
         try (DERParser parser = new DERParser(sig)) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ccb7b605/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
index e5d2d4e..bc96d08 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
@@ -18,6 +18,10 @@
  */
 package org.apache.sshd.common.signature;
 
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.interfaces.RSAKey;
+
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.Pair;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -31,6 +35,8 @@ import org.apache.sshd.common.util.ValidateUtils;
 public class SignatureRSA extends AbstractSignature {
     public static final String DEFAULT_ALGORITHM = "SHA1withRSA";
 
+    private int verifierSignatureSize = -1;
+
     public SignatureRSA() {
         super(DEFAULT_ALGORITHM);
     }
@@ -41,9 +47,30 @@ public class SignatureRSA extends AbstractSignature {
 
     @Override
     public byte[] sign() throws Exception {
+        java.security.Signature signature = ValidateUtils.checkNotNull(getSignature(), "Signature not initialized");
         return signature.sign();
     }
 
+    /**
+     * @return The expected number of bytes in the signature - non-positive
+     * if not initialized or not intended to be used for verification
+     */
+    protected int getVerifierSignatureSize() {
+        return verifierSignatureSize;
+    }
+
+    @Override
+    public void initVerifier(PublicKey key) throws Exception {
+        super.initVerifier(key);
+        ValidateUtils.checkTrue(key instanceof RSAKey, "Not an RSA key");
+        verifierSignatureSize = getVerifierSignatureSize((RSAKey) key);
+    }
+
+    public static int getVerifierSignatureSize(RSAKey key) {
+        BigInteger modulus = key.getModulus();
+        return (modulus.bitLength() + Byte.SIZE - 1) / Byte.SIZE;
+    }
+
     @Override
     public boolean verify(byte[] sig) throws Exception {
         byte[] data = sig;
@@ -54,7 +81,15 @@ public class SignatureRSA extends AbstractSignature {
             data = encoding.getSecond();
         }
 
+        int expectedSize = getVerifierSignatureSize();
+        ValidateUtils.checkTrue(expectedSize > 0, "Signature verification size has not been initialized");
+        // Pad with zero if value is trimmed
+        if (data.length < expectedSize) {
+            byte[] pad = new byte[expectedSize];
+            System.arraycopy(data, 0, pad, pad.length - data.length, data.length);
+            data = pad;
+        }
+
         return doVerify(data);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ccb7b605/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
new file mode 100644
index 0000000..4cbe017
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.sshd.common.signature;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.util.Base64;
+import org.apache.sshd.common.util.Pair;
+import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SignatureRSATest extends BaseTestSupport {
+    private static final byte[] TEST_MSG =
+            Base64.decodeString("AAAAFPHgK1MeV9zNnok3pwNJhCd8SONqMgAAAAlidWlsZHVzZXIAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAAAdzc2gtcnNhAAABFQAAAAdzc2gtcnNhAAAAASMAAAEBAMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
+    private static final byte[] TEST_SIGNATURE =
+            Base64.decodeString("AAAAB3NzaC1yc2EAAAD/+Ntnf4qfr2J1voDS6I+u3VRjtMn+LdWJsAZfkLDxRkK1rQxP7QAjLdNqpT4CkWHp8dtoTGFlBFt6NieNJCMTA2KSOxJMZKsX7e/lHkh7C+vhQvJ9eLTKWjCxSFUrcM0NvFhmwbRCffwXSHvAKak4wbmofxQMpd+G4jZkNMz5kGpmeICBcNjRLPb7oXzuGr/g4x/3ge5Qaawqrg/gcZr/sKN6SdE8SszgKYO0SB320N4gcUoShVdLYr9uwdJ+kJoobfkUK6Or171JCctP/cu2nM79lDqVnJw/2jOG8OnTc8zRDXAh0RKoR5rOU8cOHm0Ls2MATsFdnyRU5FGUxqZ+");
+    private static PublicKey testKey;
+
+    public SignatureRSATest() {
+        super();
+    }
+
+    @BeforeClass
+    public static void initializeTestKey() throws GeneralSecurityException {
+        byte[] exp = Base64.decodeString("Iw==");
+        byte[] mod = Base64.decodeString("AMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
+        KeyFactory kf = SecurityUtils.getKeyFactory("RSA");
+        testKey = kf.generatePublic(new RSAPublicKeySpec(new BigInteger(mod), new BigInteger(exp)));
+    }
+
+    @Test   // see SSHD-642
+    public void testLeadingZeroesBC() throws Throwable {
+        testLeadingZeroes(new Factory<SignatureRSA>() {
+            @Override
+            public SignatureRSA create() {
+                return new SignatureRSA() {
+                    @Override
+                    protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+                        assertFalse("Signature not initialized for verification", forSigning);
+                        java.security.Signature signature = super.doInitSignature(algo, forSigning);
+                        if (SecurityUtils.isBouncyCastleRegistered()) {
+                            Provider provider = signature.getProvider();
+                            String name = provider.getName();
+                            assertEquals("Mismatched BC provider name", SecurityUtils.BOUNCY_CASTLE, name);
+                        }
+                        return signature;
+                    }
+                };
+            }
+        });
+    }
+
+    @Test   // see SSHD-642
+    public void testLeadingZeroesJCE() throws Throwable {
+        testLeadingZeroes(new Factory<SignatureRSA>() {
+            @Override
+            public SignatureRSA create() {
+                return new SignatureRSA() {
+                    @Override
+                    protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+                        assertFalse("Signature not initialized for verification", forSigning);
+                        java.security.Signature signature = java.security.Signature.getInstance(algo);
+                        Provider provider = signature.getProvider();
+                        String name = provider.getName();
+                        assertNotEquals("BC provider used although not required", SecurityUtils.BOUNCY_CASTLE, name);
+                        return signature;
+                    }
+                };
+            }
+        });
+    }
+
+    private void testLeadingZeroes(Factory<? extends SignatureRSA> factory) throws Exception {
+        SignatureRSA rsa = factory.create();
+        rsa.initVerifier(testKey);
+
+        int vSize = rsa.getVerifierSignatureSize();
+        assertTrue("Verifier signature size not initialized", vSize > 0);
+
+        // make sure padding is required
+        Pair<String, byte[]> encoding = rsa.extractEncodedSignature(TEST_SIGNATURE);
+        assertNotNull("Signature is not encoded", encoding);
+        byte[] data = encoding.getSecond();
+        assertTrue("Signature data size (" + data.length + ") not below verifier size (" + vSize + ")", data.length < vSize);
+
+        rsa.update(TEST_MSG);
+        assertTrue("Failed to verify", rsa.verify(TEST_SIGNATURE));
+    }
+}