Repository: mina-sshd
Updated Branches:
  refs/heads/master d13c67944 -> e767438ae


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java 
b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
new file mode 100644
index 0000000..5f37bd7
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERParser.java
@@ -0,0 +1,151 @@
+/*
+ * 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.util.io.der;
+
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.Arrays;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+
+/**
+ * A bare minimum DER parser - just enough to be able to decode
+ * signatures and private keys
+ *
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+public class DERParser extends FilterInputStream {
+    /**
+     * Maximum size of data allowed by {@link #readLength()} - it is a bit
+     * arbitrary since one can encode 32-bit length data, but it is good
+     * enough for the keys
+     */
+    public static final int MAX_DER_VALUE_LENGTH = 2 * Short.MAX_VALUE;
+
+    private final byte[] lenBytes = new byte[Integer.BYTES];
+
+    public DERParser(byte... bytes) {
+        this(bytes, 0, NumberUtils.length(bytes));
+    }
+
+    public DERParser(byte[] bytes, int offset, int len) {
+        this(new ByteArrayInputStream(bytes, offset, len));
+    }
+
+    public DERParser(InputStream s) {
+        super(s);
+    }
+
+    /**
+     * Decode the length of the field. Can only support length
+     * encoding up to 4 octets. In BER/DER encoding, length can
+     * be encoded in 2 forms:
+     * <ul>
+     * <li><p>
+     * Short form - One octet. Bit 8 has value "0" and bits 7-1
+     * give the length.
+     * </p></li>
+     *
+     * <li><p>
+     * Long form - Two to 127 octets (only 4 is supported here).
+     * Bit 8 of first octet has value "1" and bits 7-1 give the
+     * number of additional length octets. Second and following
+     * octets give the length, base 256, most significant digit
+     * first.
+     * </p></li>
+     * </ul>
+     *
+     * @return The length as integer
+     * @throws IOException If invalid format found
+     */
+    public int readLength() throws IOException {
+        int i = read();
+        if (i == -1) {
+            throw new StreamCorruptedException("Invalid DER: length missing");
+        }
+
+        // A single byte short length
+        if ((i & ~0x7F) == 0) {
+            return i;
+        }
+
+        int num = i & 0x7F;
+        // TODO We can't handle length longer than 4 bytes
+        if ((i >= 0xFF) || (num > lenBytes.length)) {
+            throw new StreamCorruptedException("Invalid DER: length field too 
big: " + i);
+        }
+
+        // place the read bytes last so that the 1st ones are zeroes as big 
endian
+        Arrays.fill(lenBytes, (byte) 0);
+        int n = read(lenBytes, 4 - num, num);
+        if (n < num) {
+            throw new StreamCorruptedException("Invalid DER: length data too 
short: expected=" + num + ", actual=" + n);
+        }
+
+        long len = BufferUtils.getUInt(lenBytes);
+        if (len < 0x7FL) {   // according to standard: "the shortest possible 
length encoding must be used"
+            throw new StreamCorruptedException("Invalid DER: length not in 
shortest form: " + len);
+        }
+
+        if (len > MAX_DER_VALUE_LENGTH) {
+            throw new StreamCorruptedException("Invalid DER: data length too 
big: " + len + " (max=" + MAX_DER_VALUE_LENGTH + ")");
+        }
+
+        // we know the cast is safe since it is less than MAX_DER_VALUE_LENGTH 
which is ~64K
+        return (int) len;
+    }
+
+    public ASN1Object readObject() throws IOException {
+        int tag = read();
+        if (tag == -1) {
+            return null;
+        }
+
+        int length = readLength();
+        byte[] value = new byte[length];
+        int n = read(value);
+        if (n < length) {
+            throw new StreamCorruptedException("Invalid DER: stream too short, 
missing value: read " + n + " out of required " + length);
+        }
+
+        return new ASN1Object((byte) tag, length, value);
+    }
+
+    public BigInteger readBigInteger() throws IOException {
+        int type = read();
+        if (type != 0x02) {
+            throw new StreamCorruptedException("Invalid DER: data type is not 
an INTEGER: 0x" + Integer.toHexString(type));
+        }
+
+        int len = readLength();
+        byte[] value = new byte[len];
+        int n = read(value);
+        if (n < len) {
+            throw new StreamCorruptedException("Invalid DER: stream too short, 
missing value: read " + n + " out of required " + len);
+        }
+
+        return new BigInteger(value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java 
b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
new file mode 100644
index 0000000..870ea4a
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/io/der/DERWriter.java
@@ -0,0 +1,147 @@
+/*
+ * 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.util.io.der;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.sshd.common.util.NumberUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+
+/**
+ * A bare-minimum DER encoder - just enough so we can encoder signatures
+ * and keys data
+ *
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+public class DERWriter extends FilterOutputStream {
+    private final byte[] lenBytes = new byte[Integer.BYTES];
+
+    public DERWriter() {
+        this(ByteArrayBuffer.DEFAULT_SIZE);
+    }
+
+    public DERWriter(int initialSize) {
+        this(new ByteArrayOutputStream(initialSize));
+    }
+
+    public DERWriter(OutputStream stream) {
+        super(Objects.requireNonNull(stream, "No output stream"));
+    }
+
+    public DERWriter startSequence() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        AtomicBoolean dataWritten = new AtomicBoolean(false);
+        @SuppressWarnings("resource")
+        DERWriter encloser = this;
+        return new DERWriter(baos) {
+            @Override
+            public void close() throws IOException {
+                baos.close();
+
+                if (!dataWritten.getAndSet(true)) { // detect repeated calls 
and write this only once
+                    encloser.writeObject(new ASN1Object(ASN1Class.UNIVERSAL, 
ASN1Type.SEQUENCE, false, baos.size(), baos.toByteArray()));
+                }
+            }
+        };
+    }
+
+    public void writeBigInteger(BigInteger value) throws IOException {
+        writeBigInteger(Objects.requireNonNull(value, "No 
value").toByteArray());
+    }
+
+    public void writeBigInteger(byte... bytes) throws IOException {
+        writeBigInteger(bytes, 0, NumberUtils.length(bytes));
+    }
+
+    public void writeBigInteger(byte[] bytes, int off, int len) throws 
IOException {
+        // ASN.1 - zero padding if 1st byte is > 0x7F
+        int padLen = ((len > 0) && ((bytes[off] & 0x80) != 0)) ? 1 : 0;
+
+        write(0x02);    // indicate it is an INTEGER
+        writeLength(len + padLen);
+        for (int index = 0; index < padLen; index++) {
+            write(0);
+        }
+
+        write(bytes, off, len);
+    }
+
+
+    public void writeObject(ASN1Object obj) throws IOException {
+        Objects.requireNonNull(obj, "No ASN.1 object");
+
+        ASN1Type type = obj.getObjType();
+        byte typeValue = type.getTypeValue();
+        ASN1Class clazz = obj.getObjClass();
+        byte classValue = clazz.getClassValue();
+        byte tagValue = (byte) (((classValue << 6) & 0xC0) | (typeValue & 
0x1F));
+        writeObject(tagValue, obj.getLength(), obj.getValue());
+    }
+
+    public void writeObject(byte tag, int len, byte... data) throws 
IOException {
+        write(tag & 0xFF);
+        writeLength(len);
+        write(data, 0, len);
+    }
+
+    public void writeLength(int len) throws IOException {
+        ValidateUtils.checkTrue(len >= 0, "Invalid length: %d", len);
+
+        // short form - MSBit is zero
+        if (len <= 127) {
+            write(len);
+            return;
+        }
+
+        BufferUtils.putUInt(len, lenBytes);
+
+        int nonZeroPos = 0;
+        for (; nonZeroPos < lenBytes.length; nonZeroPos++) {
+            if (lenBytes[nonZeroPos] != 0) {
+                break;
+            }
+        }
+
+        if (nonZeroPos >= lenBytes.length) {
+            throw new StreamCorruptedException("All zeroes length 
representation for len=" + len);
+        }
+
+        int bytesLen = lenBytes.length - nonZeroPos;
+        write(0x80 | bytesLen); // indicate number of octets
+        write(lenBytes, nonZeroPos, bytesLen);
+    }
+
+    public byte[] toByteArray() throws IOException {
+        if (this.out instanceof ByteArrayOutputStream) {
+            return ((ByteArrayOutputStream) this.out).toByteArray();
+        } else {
+            throw new IOException("The underlying stream is not a byte[] 
stream");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
index b94ee4c..9ea871d 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java
@@ -38,7 +38,6 @@ import java.security.Signature;
 import java.security.cert.CertificateFactory;
 import java.security.spec.InvalidKeySpecException;
 import java.util.Collection;
-import java.util.LinkedList;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -59,6 +58,7 @@ import 
org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
 import 
org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser;
+import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.random.JceRandomFactory;
 import org.apache.sshd.common.random.RandomFactory;
@@ -407,6 +407,11 @@ public final class SecurityUtils {
         return new BouncyCastleGeneratorHostKeyProvider(path);
     }
 
+    public static KeyPairResourceParser getBouncycastleKeyPairResourceParser() 
{
+        ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not 
registered");
+        return BouncyCastleKeyPairResourceParser.INSTANCE;
+    }
+
     /**
      * @return If {@link #isBouncyCastleRegistered()} then a {@link 
BouncyCastleRandomFactory}
      * instance, otherwise a {@link JceRandomFactory} one
@@ -517,6 +522,14 @@ public final class SecurityUtils {
         return isEDDSACurveSupported() ? 
EdDSASecurityProvider.compareEDDSAPrivateKeys(k1, k2) : false;
     }
 
+    public static PublicKey recoverEDDSAPublicKey(PrivateKey key) throws 
GeneralSecurityException {
+        if (!isEDDSACurveSupported()) {
+            throw new NoSuchAlgorithmException(EDDSA + " provider not 
supported");
+        }
+
+        return EdDSASecurityProvider.recoverEDDSAPublicKey(key);
+    }
+
     public static PublicKey generateEDDSAPublicKey(String keyType, byte[] 
seed) throws GeneralSecurityException {
         if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
             throw new InvalidKeyException("Unsupported key type: " + keyType);
@@ -571,19 +584,20 @@ public final class SecurityUtils {
                 return parser;
             }
 
-            Collection<KeyPairResourceParser> available = new LinkedList<>();
-            available.add(OpenSSHKeyPairResourceParser.INSTANCE);
-            if (isBouncyCastleRegistered()) {
-                available.add(BouncyCastleKeyPairResourceParser.INSTANCE);
-            }
-
-            parser = KeyPairResourceParser.aggregate(available);
+            parser = KeyPairResourceParser.aggregate(
+                    PEMResourceParserUtils.PROXY,
+                    OpenSSHKeyPairResourceParser.INSTANCE);
             KEYPAIRS_PARSER_HODLER.set(parser);
         }
 
         return parser;
     }
 
+    /**
+     * @param parser The system-wide {@code KeyPairResourceParser} to use.
+     * If set to {@code null}, then the default parser will be re-constructed
+     * on next call to {@link #getKeyPairResourceParser()}
+     */
     public static void setKeyPairResourceParser(KeyPairResourceParser parser) {
         synchronized (KEYPAIRS_PARSER_HODLER) {
             KEYPAIRS_PARSER_HODLER.set(parser);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProvider.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProvider.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProvider.java
index 36abac0..e039547 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProvider.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProvider.java
@@ -19,6 +19,7 @@
 package org.apache.sshd.common.util.security.eddsa;
 
 import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
@@ -95,6 +96,18 @@ public class EdDSASecurityProvider extends Provider {
         return 
EdDSANamedCurveTable.CURVE_ED25519_SHA512.equalsIgnoreCase(algorithm);
     }
 
+    public static EdDSAPublicKey recoverEDDSAPublicKey(PrivateKey key) throws 
GeneralSecurityException {
+        ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), 
SecurityUtils.EDDSA + " not supported");
+        if (!(key instanceof EdDSAPrivateKey)) {
+            throw new InvalidKeyException("Private key is not " + 
SecurityUtils.EDDSA);
+        }
+
+        EdDSAPrivateKey prvKey = (EdDSAPrivateKey) key;
+        EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(prvKey.getSeed(), 
prvKey.getParams());
+        KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
+        return EdDSAPublicKey.class.cast(factory.generatePublic(keySpec));
+    }
+
     public static org.apache.sshd.common.signature.Signature 
getEDDSASignature() {
         ValidateUtils.checkTrue(SecurityUtils.isEDDSACurveSupported(), 
SecurityUtils.EDDSA + " not supported");
         return new SignatureEd25519();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
 
b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
index 3046eed..b05a4cc 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java
@@ -98,13 +98,7 @@ public class OpenSSHEd25519PrivateKeyEntryDecoder extends 
AbstractPrivateKeyEntr
 
     @Override
     public EdDSAPublicKey recoverPublicKey(EdDSAPrivateKey prvKey) throws 
GeneralSecurityException {
-        if (!SecurityUtils.isEDDSACurveSupported()) {
-            throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " 
provider not supported");
-        }
-
-        EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(prvKey.getSeed(), 
prvKey.getParams());
-        KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
-        return EdDSAPublicKey.class.cast(factory.generatePublic(keySpec));
+        return EdDSASecurityProvider.recoverEDDSAPublicKey(prvKey);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
index 016be06..8858d32 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
@@ -44,7 +44,7 @@ import org.apache.sshd.server.SshServer;
  * Loads server identity key files - e.g., {@code /etc/ssh/ssh_host_rsa_key}
  *
  * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
- * @see 
org.apache.sshd.common.util.security.SecurityUtils#isBouncyCastleRegistered()
+ * @see 
org.apache.sshd.common.util.security.SecurityUtils#getKeyPairResourceParser()
  */
 public final class ServerIdentity {
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
index 6f41196..896f37d 100644
--- 
a/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
+++ 
b/sshd-core/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java
@@ -33,9 +33,7 @@ import org.apache.sshd.common.config.keys.IdentityUtils;
 import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.Assume;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runners.MethodSorters;
@@ -51,8 +49,6 @@ public class ClientIdentityTest extends BaseTestSupport {
 
     @Test
     public void testLoadClientIdentities() throws Exception {
-        Assume.assumeTrue("BouncyCastle not registered", 
SecurityUtils.isBouncyCastleRegistered());
-
         Path resFolder = getClassResourcesFolder(TEST_SUBFOLDER, getClass());
         LinkOption[] options = IoUtils.getLinkOptions(false);
         Collection<BuiltinIdentities> expected = 
EnumSet.noneOf(BuiltinIdentities.class);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
new file mode 100644
index 0000000..352283a
--- /dev/null
+++ 
b/sshd-core/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.config.keys.loader;
+
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.util.List;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see 
https://github.com/junit-team/junit/wiki/Parameterized-tests
+public class AESPrivateKeyObfuscatorTest extends BaseTestSupport {
+    private static final Random RANDOMIZER = new 
Random(System.currentTimeMillis());
+
+    private final int keyLength;
+
+    public AESPrivateKeyObfuscatorTest(int keyLength) {
+        this.keyLength = keyLength;
+    }
+
+    @Parameters(name = "keyLength={0}")
+    public static List<Object[]> parameters() {
+        List<Integer> lengths = 
AESPrivateKeyObfuscator.getAvailableKeyLengths();
+        assertFalse("No lengths available", GenericUtils.isEmpty(lengths));
+        return parameterize(lengths);
+    }
+
+    @Test
+    public void testAvailableKeyLengthExists() throws GeneralSecurityException 
{
+        assertEquals("Not a BYTE size multiple", 0, keyLength % Byte.SIZE);
+
+        byte[] iv = new byte[keyLength / Byte.SIZE];
+        synchronized (RANDOMIZER) {
+            RANDOMIZER.nextBytes(iv);
+        }
+
+        Key key = new SecretKeySpec(iv, AESPrivateKeyObfuscator.CIPHER_NAME);
+        Cipher c = 
SecurityUtils.getCipher(AESPrivateKeyObfuscator.CIPHER_NAME);
+        c.init(Cipher.DECRYPT_MODE, key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java 
b/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
index 2e44d83..1fa87ad 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.sshd.common.util;
 
+import java.io.IOException;
 import java.nio.file.Path;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
@@ -33,12 +34,14 @@ import java.security.interfaces.ECPublicKey;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
 import org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider;
 import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider;
 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
@@ -62,14 +65,12 @@ public class SecurityUtilsTest extends BaseTestSupport {
     }
 
     @Test
-    public void testLoadEncryptedDESPrivateKey() {
-        Assume.assumeTrue("Bouncycastle not registered", 
SecurityUtils.isBouncyCastleRegistered());
+    public void testLoadEncryptedDESPrivateKey() throws Exception {
         testLoadEncryptedRSAPrivateKey("DES-EDE3");
     }
 
     @Test
     public void testLoadEncryptedAESPrivateKey() {
-        Assume.assumeTrue("Bouncycastle not registered", 
SecurityUtils.isBouncyCastleRegistered());
         for (BuiltinCiphers c : new BuiltinCiphers[]{
             BuiltinCiphers.aes128cbc, BuiltinCiphers.aes192cbc, 
BuiltinCiphers.aes256cbc
         }) {
@@ -78,33 +79,34 @@ public class SecurityUtilsTest extends BaseTestSupport {
                 continue;
             }
 
-            testLoadEncryptedRSAPrivateKey("AES-" + c.getKeySize());
+            try {
+                testLoadEncryptedRSAPrivateKey("AES-" + c.getKeySize());
+            } catch (Exception e) {
+                fail("Failed (" + e.getClass().getSimpleName() + " to load key 
for " + c.getName() + ": " + e.getMessage());
+            }
         }
     }
 
-    private KeyPair testLoadEncryptedRSAPrivateKey(String algorithm) {
+    private KeyPair testLoadEncryptedRSAPrivateKey(String algorithm) throws 
IOException, GeneralSecurityException {
         return testLoadRSAPrivateKey(DEFAULT_PASSWORD.replace(' ', '-') + 
"-RSA-" + algorithm.toUpperCase() + "-key");
     }
 
     @Test
-    public void testLoadUnencryptedRSAPrivateKey() {
-        Assume.assumeTrue("Bouncycastle not registered", 
SecurityUtils.isBouncyCastleRegistered());
+    public void testLoadUnencryptedRSAPrivateKey() throws Exception {
         testLoadRSAPrivateKey(getClass().getSimpleName() + "-RSA-KeyPair");
     }
 
     @Test
-    public void testLoadUnencryptedDSSPrivateKey() {
-        Assume.assumeTrue("Bouncycastle not registered", 
SecurityUtils.isBouncyCastleRegistered());
+    public void testLoadUnencryptedDSSPrivateKey() throws Exception {
         testLoadDSSPrivateKey(getClass().getSimpleName() + "-DSA-KeyPair");
     }
 
-    private KeyPair testLoadDSSPrivateKey(String name) {
+    private KeyPair testLoadDSSPrivateKey(String name) throws Exception {
         return testLoadPrivateKey(name, DSAPublicKey.class, 
DSAPrivateKey.class);
     }
 
     @Test
-    public void testLoadUnencryptedECPrivateKey() {
-        Assume.assumeTrue("Bouncycastle not registered", 
SecurityUtils.isBouncyCastleRegistered());
+    public void testLoadUnencryptedECPrivateKey() throws Exception {
         Assume.assumeTrue("EC not supported", SecurityUtils.hasEcc());
         for (ECCurves c : ECCurves.VALUES) {
             if (!c.isSupported()) {
@@ -116,22 +118,33 @@ public class SecurityUtilsTest extends BaseTestSupport {
         }
     }
 
-    private KeyPair testLoadECPrivateKey(String name) {
+    private KeyPair testLoadECPrivateKey(String name) throws IOException, 
GeneralSecurityException {
         return testLoadPrivateKey(name, ECPublicKey.class, ECPrivateKey.class);
     }
 
-    private KeyPair testLoadRSAPrivateKey(String name) {
+    private KeyPair testLoadRSAPrivateKey(String name) throws IOException, 
GeneralSecurityException {
         return testLoadPrivateKey(name, RSAPublicKey.class, 
RSAPrivateKey.class);
     }
 
-    private KeyPair testLoadPrivateKey(String name, Class<? extends PublicKey> 
pubType, Class<? extends PrivateKey> prvType) {
+    private KeyPair testLoadPrivateKey(String name, Class<? extends PublicKey> 
pubType, Class<? extends PrivateKey> prvType)
+            throws IOException, GeneralSecurityException {
         Path folder = getClassResourcesFolder(TEST_SUBFOLDER);
-        KeyPair kpFile = testLoadPrivateKeyFile(folder.resolve(name), pubType, 
prvType);
+        Path file = folder.resolve(name);
+        KeyPair kpFile = testLoadPrivateKeyFile(file, pubType, prvType);
+        if (SecurityUtils.isBouncyCastleRegistered()) {
+            KeyPairResourceLoader bcLoader = 
SecurityUtils.getBouncycastleKeyPairResourceParser();
+            Collection<KeyPair> kpList = bcLoader.loadKeyPairs(file, 
TEST_PASSWORD_PROVIDER);
+            assertEquals(name + ": Mismatched loaded BouncyCastle keys count", 
1, GenericUtils.size(kpList));
+
+            KeyPair kpBC = kpList.iterator().next();
+            assertTrue(name + ": Mismatched BouncyCastle vs. file values", 
KeyUtils.compareKeyPairs(kpFile, kpBC));
+        }
+
         Class<?> clazz = getClass();
         Package pkg = clazz.getPackage();
         KeyPair kpResource = 
testLoadPrivateKeyResource(pkg.getName().replace('.', '/') + "/" + name, 
pubType, prvType);
 
-        assertTrue("Mismatched key pairs values", 
KeyUtils.compareKeyPairs(kpFile, kpResource));
+        assertTrue(name + ": Mismatched key file vs. resource values", 
KeyUtils.compareKeyPairs(kpFile, kpResource));
         return kpResource;
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java 
b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
new file mode 100644
index 0000000..c70d250
--- /dev/null
+++ 
b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1ClassTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.util.io.der;
+
+import java.util.List;
+
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see 
https://github.com/junit-team/junit/wiki/Parameterized-tests
+public class ASN1ClassTest extends BaseTestSupport {
+    private final ASN1Class expected;
+
+    public ASN1ClassTest(ASN1Class expected) {
+        this.expected = expected;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        return parameterize(ASN1Class.VALUES);
+    }
+
+    @Test
+    public void testFromName() {
+        String name = expected.name();
+        for (int index = 1, count = name.length(); index <= count; index++) {
+            assertSame(name, expected, ASN1Class.fromName(name));
+            name = shuffleCase(name);
+        }
+    }
+
+    @Test // NOTE: this also tests "fromTypeValue" since "fromDERValue" 
invokes it
+    public void testFromDERValue() {
+        assertSame(expected, ASN1Class.fromDERValue((expected.getClassValue() 
<< 6) & 0xFF));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java 
b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
new file mode 100644
index 0000000..ce24b66
--- /dev/null
+++ 
b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/ASN1TypeTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.util.io.der;
+
+import java.util.List;
+
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see 
https://github.com/junit-team/junit/wiki/Parameterized-tests
+public class ASN1TypeTest extends BaseTestSupport {
+    private final ASN1Type expected;
+
+    public ASN1TypeTest(ASN1Type expected) {
+        this.expected = expected;
+    }
+
+    @Parameters(name = "{0}")
+    public static List<Object[]> parameters() {
+        return parameterize(ASN1Type.VALUES);
+    }
+
+    @Test
+    public void testFromName() {
+        String name = expected.name();
+        for (int index = 1, count = name.length(); index <= count; index++) {
+            assertSame(name, expected, ASN1Type.fromName(name));
+            name = shuffleCase(name);
+        }
+    }
+
+    @Test
+    public void testFromTypeValue() {
+        assertSame(expected, ASN1Type.fromTypeValue(expected.getTypeValue()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java 
b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
new file mode 100644
index 0000000..edc6768
--- /dev/null
+++ 
b/sshd-core/src/test/java/org/apache/sshd/common/util/io/der/DERParserTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util.io.der;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class DERParserTest extends BaseTestSupport {
+    public DERParserTest() {
+        super();
+    }
+
+    @Test
+    public void testReadLengthConstraint() throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            try (DERWriter w = new DERWriter(baos)) {
+                w.writeLength(DERParser.MAX_DER_VALUE_LENGTH + 1);
+            }
+        } finally {
+            baos.close();
+        }
+
+        try (DERParser parser = new DERParser(baos.toByteArray())) {
+            int len = parser.readLength();
+            fail("Unexpected success: len=" + len);
+        } catch (StreamCorruptedException e) {
+            // expected ignored
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e767438a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
index 7738669..0ff1dd6 100644
--- 
a/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
+++ 
b/sshd-core/src/test/java/org/apache/sshd/server/config/keys/ServerIdentityTest.java
@@ -34,9 +34,7 @@ import org.apache.sshd.common.config.keys.IdentityUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.Assume;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runners.MethodSorters;
@@ -52,8 +50,6 @@ public class ServerIdentityTest extends BaseTestSupport {
 
     @Test
     public void testLoadServerIdentities() throws Exception {
-        Assume.assumeTrue("BouncyCastle not registered", 
SecurityUtils.isBouncyCastleRegistered());
-
         Path resFolder = getClassResourcesFolder(TEST_SUBFOLDER, getClass());
         Collection<Path> paths = new 
ArrayList<>(BuiltinIdentities.VALUES.size());
         LinkOption[] options = IoUtils.getLinkOptions(false);

Reply via email to