diff --git a/sshsig-bcprov/SshPublicKeyEncoderTest (bcprov)(JDK17).launch b/sshsig-bcprov/SshPublicKeyEncoderTest (bcprov)(JDK17).launch
new file mode 100644
index 0000000..411ddc9
--- /dev/null
+++ b/sshsig-bcprov/SshPublicKeyEncoderTest (bcprov)(JDK17).launch
@@ -0,0 +1,24 @@
+
+
+ *
+ * @author profhenry
+ */
+public class SshPublicKeyEncoderTest {
+
+ @BeforeAll
+ static void setup() {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ @Test
+ @Disabled
+ void testEncodeDsaPublicKey() throws Exception {
+ PublicKey tPublicKey = SshKeyUtil.readDsaKeyPair().getPublic();
+
+ SshPublicKeyEncoder tPublicKeyEncoder = new SshPublicKeyEncoder();
+ byte[] tSshEncodedPublicKey = tPublicKeyEncoder.encodePublicKey(tPublicKey);
+
+ assertThat(tSshEncodedPublicKey).asBase64Encoded()
+ .isEqualTo(
+ "AAAAB3NzaC1kc3MAAACBAP8BC5pi3uxvOtlb1ikWNeLUubiaT0l7aMOhAbOFmtivswcg0r+rZiX/UOjhxCATRiaTXV42byLnf0cKzQrZ5QEnm8uTf1aK0gdT/bdQaZwWmkMB2LdHQ4xEt3slsDLYoXwwzV+stpRcFQ1VyUlJQV754HqHZT8RbkUBJHIsIqslAAAAFQCKvLgJmuxvJy6DvdRB5Fm/VZR5PwAAAIEA1y5UQItEth5WSS6166Aujc+7x9jgaztoC4Uo0iskMM5D0oVrJSyVwCAOcNPEeya4zNnJgkD16Wco3XOryBcgjWScEoTgict2J8rnaUWDkyOMptfbiF+oU39INh3m+2tvIfsgX81bAQJD0STYiF9J3G/PQYjvRIxhybtHJHGcuaUAAACBANTDVZW7LC9PbzQ1e2SxMOznf76/WV8RfwfKALh0DpJvyaEuOQkgwO/AzSNoN1mJgkn7mjXKgsJLYB/49d1arv8n/nG+7oLGdKYlKlXOkJ2bW+yxnFwLvBTBniCbdyylPP1iNbw2SArns44xxBszjTedAZcpJQiv+ThZI3Bzg5dM");
+ }
+
+ @Test
+ void testEncodeRsaPublicKey() throws Exception {
+ PublicKey tPublicKey = SshKeyUtil.readRsaKeyPair().getPublic();
+
+ SshPublicKeyEncoder tPublicKeyEncoder = new SshPublicKeyEncoder();
+ byte[] tSshEncodedPublicKey = tPublicKeyEncoder.encodePublicKey(tPublicKey);
+
+ assertThat(tSshEncodedPublicKey).asBase64Encoded()
+ .isEqualTo(
+ "AAAAB3NzaC1yc2EAAAADAQABAAABgQDg9Lf347GyGfq+SZjWnLEFY61tz3czkkpeU71piNtCD9M18vsonIKmLRwUC4dKBE+UJQf7F79Mx/Z6XqgNCTP9tAVj1YMKtIIXbl6F4hkMWZr1XTjq78jCk3yWx7BA9CYaTxK185l3WbcZovrx9iTrVafi6+cXhxAC4HYHQYjB/1YubPhWIJ4mtqo7e22xP84Kdr/aYmSJbx0vaUjRJQaFJkYVi2Sb8GYAagd5YQ5aODU6CuY/ycp18UMQ56G/uSR19O+OGXrHbF2GEZTko6ESOAbu7EjquU1fOL3xeh/3GYNtYjeztQFCXGz2iXrKNk+wMjHGvrg8w11NdgpT983UMwA8bV8kctz4qaH/89HhR49pkLSxOc9AMzjSL8N4bueId598KnfutEzT0N+Ghwsi+1fDrohCTRx2x+PyLYe5syehjn/IxhnQlKEvtRitjUqCn32mAufx2BbCl8rykPTUxBE/QAUYPlA/Surv8j9yE8tEpDIdEBc78kpwBxH60p8=");
+ }
+
+ @Test
+ void testEncodeEd25519PublicKey() throws Exception {
+ PublicKey tPublicKey = SshKeyUtil.readEd25519KeyPair().getPublic();
+
+ SshPublicKeyEncoder tPublicKeyEncoder = new SshPublicKeyEncoder();
+ byte[] tSshEncodedPublicKey = tPublicKeyEncoder.encodePublicKey(tPublicKey);
+
+ assertThat(tSshEncodedPublicKey).asBase64Encoded()
+ .isEqualTo("AAAAC3NzaC1lZDI1NTE5AAAAIDuDUZReuNqkLI5pqXRzx6+LtMtLMji2HPoDccHOE4XF");
+ }
+}
diff --git a/sshsig-bcprov/src/test/java/de/profhenry/sshsig/bcprov/SshSignatureGeneratorTest.java b/sshsig-bcprov/src/test/java/de/profhenry/sshsig/bcprov/SshSignatureGeneratorTest.java
index c3104c3..44d3230 100644
--- a/sshsig-bcprov/src/test/java/de/profhenry/sshsig/bcprov/SshSignatureGeneratorTest.java
+++ b/sshsig-bcprov/src/test/java/de/profhenry/sshsig/bcprov/SshSignatureGeneratorTest.java
@@ -118,7 +118,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -179,7 +179,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -259,7 +259,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -327,7 +327,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -394,7 +394,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -449,7 +449,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
diff --git a/sshsig-core/SshPublicKeyEncoderTest (core)(JDK17).launch b/sshsig-core/SshPublicKeyEncoderTest (core)(JDK17).launch
new file mode 100644
index 0000000..ee63525
--- /dev/null
+++ b/sshsig-core/SshPublicKeyEncoderTest (core)(JDK17).launch
@@ -0,0 +1,24 @@
+
+
- * The SSH
*
* @author profhenry
*/
public class JcaSingingBackend implements SigningBackend
+ * RFC7468 states that the base 64 content MUST wrap after 64 chars.
+ */
+ private static final int DEFAULT_LINE_LENGTH = 64;
+
+ private final PrintWriter printWriter;
+
+ private final int lineLength;
+
+ public PemWriter(Writer aWriter, int aLineLength) {
+ printWriter = new PrintWriter(aWriter);
+ lineLength = aLineLength;
+ }
+
+ public PemWriter(Writer aWriter) {
+ this(aWriter, DEFAULT_LINE_LENGTH);
+ }
+
+ public void writeData(String aLabel, byte[] someBytes) {
+ writeHeader(aLabel);
+ writeData(someBytes);
+ writeFooter(aLabel);
+ printWriter.flush();
+ }
+
+ private void writeHeader(String aLabel) {
+ printWriter.println("-----BEGIN " + aLabel + "-----");
+ }
+
+ private void writeData(byte[] someBytes) {
+ String tEncoded = Base64.getEncoder().encodeToString(someBytes);
+
+ for (int i = 0; i < tEncoded.length(); i += lineLength) {
+ printWriter.println(tEncoded.substring(i, Math.min(tEncoded.length(), i + lineLength)));
+ }
+ }
+
+ private void writeFooter(String aLabel) {
+ printWriter.println("-----END " + aLabel + "-----");
+ }
+}
diff --git a/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshBuffer.java b/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshBuffer.java
index 6031113..44e99d0 100644
--- a/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshBuffer.java
+++ b/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshBuffer.java
@@ -129,7 +129,7 @@ public void appendBigInteger(BigInteger aBigInteger) {
/**
* Appends a sequence of a string and a byte array.
*
- * First writes the overall bytes required by this sequence followed by the string and the byte array (eqch with
+ * First writes the overall bytes required by this sequence followed by the string and the byte array (each with
* their length fields as well).
*
* Added bytes: 4 + 4 + number of chars + 4 + length of the byte array
diff --git a/sshsig-core/src/main/java/de/profhenry/sshsig/core/PublicKeyEncoder.java b/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshPublicKeyEncoder.java
similarity index 70%
rename from sshsig-core/src/main/java/de/profhenry/sshsig/core/PublicKeyEncoder.java
rename to sshsig-core/src/main/java/de/profhenry/sshsig/core/SshPublicKeyEncoder.java
index 70f535d..5c7f6f5 100644
--- a/sshsig-core/src/main/java/de/profhenry/sshsig/core/PublicKeyEncoder.java
+++ b/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshPublicKeyEncoder.java
@@ -20,29 +20,57 @@
import java.security.interfaces.RSAPublicKey;
/**
+ * Encoder for public keys in SSH format.
+ *
+ * This encoder returns a blob containing the SSH public key as specified in the SSH protocol.
+ *
* @author profhenry
*/
-public class PublicKeyEncoder {
+public class SshPublicKeyEncoder {
+ /** key format identifier for DSA keys **/
private static final String KEY_FORMAT_IDENTIFIER_DSS = "ssh-dss";
+ /** key format identifier for RSA keys **/
private static final String KEY_FORMAT_IDENTIFIER_RSA = "ssh-rsa";
+ /** key format identifier for ED25519 keys **/
private static final String KEY_FORMAT_IDENTIFIER_ED25519 = "ssh-ed25519";
+ /**
+ * Encodes a public key in SSH format.
+ *
+ *
+ * @param aPublicKey a public key
+ * @return the encoded public key
+ * @throws SshSignatureException in case the public key could no be encoded
+ */
public byte[] encodePublicKey(PublicKey aPublicKey) throws SshSignatureException {
+ // In case of DSA or RSA public keys the detection is easy because JCA provides interfaces for those.
+ // This approach should also work in case other JCA Provider (like bouncy castler) are used.
if (aPublicKey instanceof DSAPublicKey) {
return encodeDsaPublicKey((DSAPublicKey) aPublicKey);
}
if (aPublicKey instanceof RSAPublicKey) {
return encodeRsaPublicKey((RSAPublicKey) aPublicKey);
}
- if ("EdDSA".equals(aPublicKey.getAlgorithm())) {
- // used by JDK17 and net.i2p.crypto
+ // The handling of ED25519 public keys is a bit more complicated.
+ // Since JDK8 comes with no support for ED25519 there is no interface to compile against.
+ // However we decided to add some support for ED25519. For using ED25519 you need a JDK17 as runtime JVM and/or
+ // a JCA security provider which adds ED25519 support.
+
+ // We decided to use the algorithm provided by the public key as incidator if we have an ED25519 key.
+ if ("Ed25519".equals(aPublicKey.getAlgorithm())) {
+ // used by org.bouncycastle:bcprov in default configuration
return encodeEd25519PublicKey(aPublicKey);
}
- if ("Ed25519".equals(aPublicKey.getAlgorithm())) {
- // used by org.bouncycastle
+ if ("EdDSA".equals(aPublicKey.getAlgorithm())) {
+ // used by
+ // - JDK17
+ // - net.i2p.crypto:eddsa
+ // - org.bouncycastle:bcprov (with activated org.bouncycastle.emulate.oracle property)
return encodeEd25519PublicKey(aPublicKey);
}
throw new SshSignatureException("Could not encode public key (" + aPublicKey.getClass().getName() + ")!");
@@ -51,7 +79,7 @@ public byte[] encodePublicKey(PublicKey aPublicKey) throws SshSignatureException
/**
* Encodes a DSA public key.
*
- * According to RTC 4253 DSA public key encoding is
+ * According to RTC 4253 the DSA public key encoding is
*
- * According to RTC 4253 RSA public key encoding is
+ * According to RTC 4253 the RSA public key encoding is
*
- * According to RFC 8709 ED25519 public key encoding is
+ * According to RFC 8709 the ED25519 public key encoding is
*
* NOTE:
@@ -200,7 +217,7 @@ private byte[] hashMessage(byte[] aMessage) throws SshSignatureException {
private byte[] hashMessage(InputStream anInputStream) throws SshSignatureException, IOException {
MessageDigest tMessageDigest = hashAlgorithm.createMessageDigestInstance();
- byte[] tBuffer = new byte[BUFFER_SIZE];
+ byte[] tBuffer = new byte[bufferSize];
int tReadBytes = 0;
int i;
while ((i = anInputStream.read(tBuffer, 0, tBuffer.length)) >= 0) {
@@ -208,11 +225,11 @@ private byte[] hashMessage(InputStream anInputStream) throws SshSignatureExcepti
tReadBytes += i;
}
+ byte[] tHash = tMessageDigest.digest();
LOGGER.debug("hashed {} bytes using {} ({})",
tReadBytes,
tMessageDigest.getAlgorithm(),
tMessageDigest.getProvider());
- byte[] tHash = tMessageDigest.digest();
LOGGER.debug("hashed message: {}", HexUtil.bytesToHex(tHash));
return tHash;
}
@@ -277,15 +294,26 @@ private byte[] generateDataToSign(String aNamespace, byte[] aHashedMessage) {
// =================================================================================================================
- public static
+ *
+ * @author profhenry
+ */
+public class SshPublicKeyEncoderTest {
+
+ @Test
+ void testEncodeDsaPublicKey() throws Exception {
+ PublicKey tPublicKey = SshKeyUtil.readDsaKeyPair().getPublic();
+
+ SshPublicKeyEncoder tPublicKeyEncoder = new SshPublicKeyEncoder();
+ byte[] tSshEncodedPublicKey = tPublicKeyEncoder.encodePublicKey(tPublicKey);
+
+ assertThat(tSshEncodedPublicKey).asBase64Encoded()
+ .isEqualTo(
+ "AAAAB3NzaC1kc3MAAACBAP8BC5pi3uxvOtlb1ikWNeLUubiaT0l7aMOhAbOFmtivswcg0r+rZiX/UOjhxCATRiaTXV42byLnf0cKzQrZ5QEnm8uTf1aK0gdT/bdQaZwWmkMB2LdHQ4xEt3slsDLYoXwwzV+stpRcFQ1VyUlJQV754HqHZT8RbkUBJHIsIqslAAAAFQCKvLgJmuxvJy6DvdRB5Fm/VZR5PwAAAIEA1y5UQItEth5WSS6166Aujc+7x9jgaztoC4Uo0iskMM5D0oVrJSyVwCAOcNPEeya4zNnJgkD16Wco3XOryBcgjWScEoTgict2J8rnaUWDkyOMptfbiF+oU39INh3m+2tvIfsgX81bAQJD0STYiF9J3G/PQYjvRIxhybtHJHGcuaUAAACBANTDVZW7LC9PbzQ1e2SxMOznf76/WV8RfwfKALh0DpJvyaEuOQkgwO/AzSNoN1mJgkn7mjXKgsJLYB/49d1arv8n/nG+7oLGdKYlKlXOkJ2bW+yxnFwLvBTBniCbdyylPP1iNbw2SArns44xxBszjTedAZcpJQiv+ThZI3Bzg5dM");
+ }
+
+ @Test
+ void testEncodeRsaPublicKey() throws Exception {
+ PublicKey tPublicKey = SshKeyUtil.readRsaKeyPair().getPublic();
+
+ SshPublicKeyEncoder tPublicKeyEncoder = new SshPublicKeyEncoder();
+ byte[] tSshEncodedPublicKey = tPublicKeyEncoder.encodePublicKey(tPublicKey);
+
+ assertThat(tSshEncodedPublicKey).asBase64Encoded()
+ .isEqualTo(
+ "AAAAB3NzaC1yc2EAAAADAQABAAABgQDg9Lf347GyGfq+SZjWnLEFY61tz3czkkpeU71piNtCD9M18vsonIKmLRwUC4dKBE+UJQf7F79Mx/Z6XqgNCTP9tAVj1YMKtIIXbl6F4hkMWZr1XTjq78jCk3yWx7BA9CYaTxK185l3WbcZovrx9iTrVafi6+cXhxAC4HYHQYjB/1YubPhWIJ4mtqo7e22xP84Kdr/aYmSJbx0vaUjRJQaFJkYVi2Sb8GYAagd5YQ5aODU6CuY/ycp18UMQ56G/uSR19O+OGXrHbF2GEZTko6ESOAbu7EjquU1fOL3xeh/3GYNtYjeztQFCXGz2iXrKNk+wMjHGvrg8w11NdgpT983UMwA8bV8kctz4qaH/89HhR49pkLSxOc9AMzjSL8N4bueId598KnfutEzT0N+Ghwsi+1fDrohCTRx2x+PyLYe5syehjn/IxhnQlKEvtRitjUqCn32mAufx2BbCl8rykPTUxBE/QAUYPlA/Surv8j9yE8tEpDIdEBc78kpwBxH60p8=");
+ }
+
+ @Test
+ @EnabledForJreRange(min = JRE.JAVA_17)
+ void testEncodeEd25519PublicKey() throws Exception {
+ PublicKey tPublicKey = SshKeyUtil.readEd25519KeyPair().getPublic();
+
+ SshPublicKeyEncoder tPublicKeyEncoder = new SshPublicKeyEncoder();
+ byte[] tSshEncodedPublicKey = tPublicKeyEncoder.encodePublicKey(tPublicKey);
+
+ assertThat(tSshEncodedPublicKey).asBase64Encoded()
+ .isEqualTo("AAAAC3NzaC1lZDI1NTE5AAAAIDuDUZReuNqkLI5pqXRzx6+LtMtLMji2HPoDccHOE4XF");
+ }
+}
diff --git a/sshsig-core/src/test/java/de/profhenry/sshsig/core/SshSignatureGeneratorTest.java b/sshsig-core/src/test/java/de/profhenry/sshsig/core/SshSignatureGeneratorTest.java
index 12335d7..d3e9bd7 100644
--- a/sshsig-core/src/test/java/de/profhenry/sshsig/core/SshSignatureGeneratorTest.java
+++ b/sshsig-core/src/test/java/de/profhenry/sshsig/core/SshSignatureGeneratorTest.java
@@ -118,7 +118,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -179,7 +179,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -259,7 +259,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -327,7 +327,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -395,7 +395,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -450,7 +450,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
diff --git a/sshsig-i2pcrypto/SshPublicKeyEncoderTest (ip2crypto)(JDK17).launch b/sshsig-i2pcrypto/SshPublicKeyEncoderTest (ip2crypto)(JDK17).launch
new file mode 100644
index 0000000..bc4305d
--- /dev/null
+++ b/sshsig-i2pcrypto/SshPublicKeyEncoderTest (ip2crypto)(JDK17).launch
@@ -0,0 +1,24 @@
+
+
+ *
+ * @author profhenry
+ */
+public class SshPublicKeyEncoderTest {
+
+ @BeforeAll
+ static void setup() {
+ Security.addProvider(new EdDSASecurityProvider());
+ }
+
+ @Test
+ void testEncodeEd25519PublicKey() throws Exception {
+ PublicKey tPublicKey = SshKeyUtil.readEd25519KeyPair().getPublic();
+
+ SshPublicKeyEncoder tPublicKeyEncoder = new SshPublicKeyEncoder();
+ byte[] tSshEncodedPublicKey = tPublicKeyEncoder.encodePublicKey(tPublicKey);
+
+ assertThat(tSshEncodedPublicKey).asBase64Encoded()
+ .isEqualTo("AAAAC3NzaC1lZDI1NTE5AAAAIDuDUZReuNqkLI5pqXRzx6+LtMtLMji2HPoDccHOE4XF");
+ }
+}
diff --git a/sshsig-i2pcrypto/src/test/java/de/profhenry/sshsig/i2pcrypto/SshSignatureGeneratorTest.java b/sshsig-i2pcrypto/src/test/java/de/profhenry/sshsig/i2pcrypto/SshSignatureGeneratorTest.java
index 78ac381..144525f 100644
--- a/sshsig-i2pcrypto/src/test/java/de/profhenry/sshsig/i2pcrypto/SshSignatureGeneratorTest.java
+++ b/sshsig-i2pcrypto/src/test/java/de/profhenry/sshsig/i2pcrypto/SshSignatureGeneratorTest.java
@@ -110,7 +110,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -165,7 +165,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
diff --git a/sshsig-mina/src/test/java/de/profhenry/sshsig/mina/SshSignatureGeneratorTest.java b/sshsig-mina/src/test/java/de/profhenry/sshsig/mina/SshSignatureGeneratorTest.java
index b036f88..77b3371 100644
--- a/sshsig-mina/src/test/java/de/profhenry/sshsig/mina/SshSignatureGeneratorTest.java
+++ b/sshsig-mina/src/test/java/de/profhenry/sshsig/mina/SshSignatureGeneratorTest.java
@@ -75,7 +75,8 @@ class Sha256 {
@BeforeEach
void setup() throws Exception {
- sshSignatureGenerator = SshSignatureGenerator.create(new ApacheMinaSshAgentEngine(sshAgent))
+ sshSignatureGenerator = SshSignatureGenerator.create()
+ .withSigningBackend(new ApacheMinaSshAgentEngine(sshAgent))
.withHashAlgorithm(HashAlgorithm.SHA_256);
}
@@ -129,7 +130,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -142,7 +143,8 @@ class Sha512 {
@BeforeEach
void setup() throws Exception {
- sshSignatureGenerator = SshSignatureGenerator.create(new ApacheMinaSshAgentEngine(sshAgent));
+ sshSignatureGenerator =
+ SshSignatureGenerator.create().withSigningBackend(new ApacheMinaSshAgentEngine(sshAgent));
}
// @formatter:off
@@ -195,7 +197,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -224,7 +226,8 @@ class Sha256 {
@BeforeEach
void setup() throws Exception {
- sshSignatureGenerator = SshSignatureGenerator.create(new ApacheMinaSshAgentEngine(sshAgent))
+ sshSignatureGenerator = SshSignatureGenerator.create()
+ .withSigningBackend(new ApacheMinaSshAgentEngine(sshAgent))
.withHashAlgorithm(HashAlgorithm.SHA_256);
}
@@ -285,7 +288,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -298,7 +301,8 @@ class Sha512 {
@BeforeEach
void setup() throws Exception {
- sshSignatureGenerator = SshSignatureGenerator.create(new ApacheMinaSshAgentEngine(sshAgent));
+ sshSignatureGenerator =
+ SshSignatureGenerator.create().withSigningBackend(new ApacheMinaSshAgentEngine(sshAgent));
}
// @formatter:off
@@ -358,7 +362,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -387,7 +391,8 @@ class Sha256 {
@BeforeEach
void setup() throws Exception {
- sshSignatureGenerator = SshSignatureGenerator.create(new ApacheMinaSshAgentEngine(sshAgent))
+ sshSignatureGenerator = SshSignatureGenerator.create()
+ .withSigningBackend(new ApacheMinaSshAgentEngine(sshAgent))
.withHashAlgorithm(HashAlgorithm.SHA_256);
}
@@ -435,7 +440,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
@@ -448,7 +453,8 @@ class Sha512 {
@BeforeEach
void setup() throws Exception {
- sshSignatureGenerator = SshSignatureGenerator.create(new ApacheMinaSshAgentEngine(sshAgent));
+ sshSignatureGenerator =
+ SshSignatureGenerator.create().withSigningBackend(new ApacheMinaSshAgentEngine(sshAgent));
}
// @formatter:off
@@ -495,7 +501,7 @@ void testVerifyWithSshKeyGen() throws SshSignatureException, IOException, Interr
+ "."
+ sshSignatureGenerator.getHashAlgorithm()
+ ".sig";
- tSignature.write(Paths.get(tSignatureFileName));
+ tSignature.writeAsPem(Paths.get(tSignatureFileName));
verifyUsingSshKeygen(MESSAGE, NAMESPACE, tSignatureFileName);
}
+ * The second column of a SSH public key file contains this blob (base64 encoded).
+ *
*
*
*
* Since we are compiling against a JDK8 which has no support for ED25519 we are not able to provide a proper
- * encoding method :-/. For the case the public key has the X.509 format we can extract the 32 bytes from the X.509
- * encoding.
+ * encoding method :-/.
+ * For the case the public key has the X.509 format we can extract the required 32 bytes from the X.509 encoding.
*
* @param aPublicKey the ED25519 public key
* @return the encoded ED25519 key
diff --git a/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshSignature.java b/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshSignature.java
index c6d9989..c019618 100644
--- a/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshSignature.java
+++ b/sshsig-core/src/main/java/de/profhenry/sshsig/core/SshSignature.java
@@ -17,26 +17,21 @@
import java.io.BufferedWriter;
import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Base64;
-import java.util.function.Consumer;
/**
* @author profhenry
*/
public class SshSignature {
- private static final String HEADER = "-----BEGIN SSH SIGNATURE-----";
+ private static final String PEM_LABEL = "SSH SIGNATURE";
- private static final String FOOTER = "-----END SSH SIGNATURE-----";
-
- private static final int MAX_LINE_LENGTH = 70;
+ // SSHSIG protocol states that content SHOULD wrap after 76 chars
+ // However the actual OpenSSH implementation wraps after 70 chars
+ private static final int LINE_LENGTH = 70;
private final byte[] signatureData;
@@ -51,43 +46,24 @@ public SignatureAlgorithm getSignatureAlgorithm() {
return signatureAlgorithm;
}
- private void write(Writer aWriter) {
- PrintWriter tPrintWriter = new PrintWriter(aWriter);
-
- tPrintWriter.println(HEADER);
- String tEncoded = Base64.getEncoder().encodeToString(signatureData);
- splitAfterFixedNumberOfChars(tEncoded, MAX_LINE_LENGTH, tPrintWriter::println);
- tPrintWriter.println(FOOTER);
- tPrintWriter.flush();
+ public byte[] getSignatureData() {
+ return signatureData;
}
- public String dumb() {
+ public String toPem() {
StringWriter tWriter = new StringWriter();
- write(tWriter);
+ writeAsPem(tWriter);
return tWriter.toString();
}
- public void write(OutputStream anOutputStream) {
- OutputStreamWriter tWriter = new OutputStreamWriter(anOutputStream);
- write(tWriter);
- }
-
- public void write(Path aPath) throws IOException {
- BufferedWriter tWriter = Files.newBufferedWriter(aPath);
- write(tWriter);
- }
-
- public void writeRawData(Path aPath) throws IOException {
- Files.write(aPath, signatureData);
- }
-
- public byte[] getSignatureData() {
- return signatureData;
+ public void writeAsPem(Writer aWriter) {
+ PemWriter tPemWriter = new PemWriter(aWriter, LINE_LENGTH);
+ tPemWriter.writeData(PEM_LABEL, signatureData);
}
- private static void splitAfterFixedNumberOfChars(String aString, int aSize, Consumer