From c161fe26f6681dd00916059d1c740eeca6f05de2 Mon Sep 17 00:00:00 2001 From: charlesrgould Date: Wed, 4 Oct 2017 05:06:37 -0400 Subject: [PATCH] Extracted ASN.1/DER encoding to method (#368) --- .../schmizz/sshj/signature/SignatureDSA.java | 36 ++++++---- .../sshj/signature/SignatureECDSA.java | 24 +++---- .../sshj/signature/SignatureDSATest.java | 68 +++++++++++++++++++ ...ationTest.java => SignatureECDSATest.java} | 2 +- 4 files changed, 102 insertions(+), 28 deletions(-) create mode 100644 src/test/java/net/schmizz/sshj/signature/SignatureDSATest.java rename src/test/java/net/schmizz/sshj/signature/{VerificationTest.java => SignatureECDSATest.java} (99%) diff --git a/src/main/java/net/schmizz/sshj/signature/SignatureDSA.java b/src/main/java/net/schmizz/sshj/signature/SignatureDSA.java index 9b11b6ca7..3406e609c 100644 --- a/src/main/java/net/schmizz/sshj/signature/SignatureDSA.java +++ b/src/main/java/net/schmizz/sshj/signature/SignatureDSA.java @@ -82,21 +82,10 @@ public byte[] encode(byte[] sig) { } @Override - public boolean verify(byte[] incomingSig) { - byte[] extractSig = extractSig(incomingSig, "ssh-dss"); + public boolean verify(byte[] sig) { try { - // ASN.1 - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ASN1OutputStream asn1OutputStream = new ASN1OutputStream(os); - ASN1EncodableVector vector = new ASN1EncodableVector(); - BigInteger bigInteger = new BigInteger(1, Arrays.copyOfRange(extractSig, 0, 20)); - vector.add(new ASN1Integer(bigInteger)); - BigInteger bigInteger2 = new BigInteger(1, Arrays.copyOfRange(extractSig, 20, 40)); - vector.add(new ASN1Integer(bigInteger2)); - asn1OutputStream.writeObject(new DERSequence(vector)); - asn1OutputStream.close(); - byte[] finalSig = os.toByteArray(); - return signature.verify(finalSig); + byte[] sigBlob = extractSig(sig, "ssh-dss"); + return signature.verify(asnEncode(sigBlob)); } catch (SignatureException e) { throw new SSHRuntimeException(e); } catch (IOException e) { @@ -104,4 +93,23 @@ public boolean verify(byte[] incomingSig) { } } + /** + * Encodes the signature as a DER sequence (ASN.1 format). + */ + private byte[] asnEncode(byte[] sigBlob) throws IOException { + byte[] r = new BigInteger(1, Arrays.copyOfRange(sigBlob, 0, 20)).toByteArray(); + byte[] s = new BigInteger(1, Arrays.copyOfRange(sigBlob, 20, 40)).toByteArray(); + + ASN1EncodableVector vector = new ASN1EncodableVector(); + vector.add(new ASN1Integer(r)); + vector.add(new ASN1Integer(s)); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ASN1OutputStream asnOS = new ASN1OutputStream(baos); + + asnOS.writeObject(new DERSequence(vector)); + asnOS.flush(); + + return baos.toByteArray(); + } } diff --git a/src/main/java/net/schmizz/sshj/signature/SignatureECDSA.java b/src/main/java/net/schmizz/sshj/signature/SignatureECDSA.java index 1a857a340..b063e393b 100644 --- a/src/main/java/net/schmizz/sshj/signature/SignatureECDSA.java +++ b/src/main/java/net/schmizz/sshj/signature/SignatureECDSA.java @@ -98,7 +98,7 @@ public byte[] encode(byte[] sig) { System.arraycopy(sig, 4, r, 0, rLen); System.arraycopy(sig, 6 + rLen, s, 0, sLen); - Buffer buf = new Buffer.PlainBuffer(); + Buffer.PlainBuffer buf = new Buffer.PlainBuffer(); buf.putMPInt(new BigInteger(r)); buf.putMPInt(new BigInteger(s)); @@ -107,18 +107,9 @@ public byte[] encode(byte[] sig) { @Override public boolean verify(byte[] sig) { - byte[] r; - byte[] s; try { - Buffer sigbuf = new Buffer.PlainBuffer(extractSig(sig, keyTypeName)); - r = sigbuf.readBytes(); - s = sigbuf.readBytes(); - } catch (Exception e) { - throw new SSHRuntimeException(e); - } - - try { - return signature.verify(asnEncode(r, s)); + byte[] sigBlob = extractSig(sig, keyTypeName); + return signature.verify(asnEncode(sigBlob)); } catch (SignatureException e) { throw new SSHRuntimeException(e); } catch (IOException e) { @@ -126,7 +117,14 @@ public boolean verify(byte[] sig) { } } - private byte[] asnEncode(byte[] r, byte[] s) throws IOException { + /** + * Encodes the signature as a DER sequence (ASN.1 format). + */ + private byte[] asnEncode(byte[] sigBlob) throws IOException { + Buffer.PlainBuffer sigbuf = new Buffer.PlainBuffer(sigBlob); + byte[] r = sigbuf.readBytes(); + byte[] s = sigbuf.readBytes(); + ASN1EncodableVector vector = new ASN1EncodableVector(); vector.add(new ASN1Integer(r)); vector.add(new ASN1Integer(s)); diff --git a/src/test/java/net/schmizz/sshj/signature/SignatureDSATest.java b/src/test/java/net/schmizz/sshj/signature/SignatureDSATest.java new file mode 100644 index 000000000..339e13a97 --- /dev/null +++ b/src/test/java/net/schmizz/sshj/signature/SignatureDSATest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C)2009 - SSHJ Contributors + * + * Licensed 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 net.schmizz.sshj.signature; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; +import java.util.Arrays; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import net.schmizz.sshj.common.Buffer; +import net.schmizz.sshj.common.IOUtils; + +public class SignatureDSATest { + + private KeyFactory keyFactory; + + @Before + public void setUp() throws NoSuchAlgorithmException { + keyFactory = KeyFactory.getInstance("DSA"); + } + + @Test + public void testSignAndVerify() throws Exception { + BigInteger x = new BigInteger(new byte[] { 58, 19, -71, -30, 89, -111, 75, 98, 110, 38, -56, -23, 68, 74, -40, 17, -30, 37, 50, 35 }); + BigInteger y = new BigInteger(new byte[] { 32, -91, -39, 54, 19, 14, 26, 113, -109, -92, -45, 83, -86, 23, -103, 108, 102, 86, 110, 78, -45, -41, -37, 38, -94, -92, -124, -36, -93, 92, 127, 113, 97, -119, -10, -73, -41, -45, 98, -104, -54, -9, -92, 66, 15, 31, 68, -32, 32, -121, -51, 68, 29, 100, 59, 60, 109, 111, -81, 80, 7, 127, 116, -107, 88, -114, -114, -69, 41, -15, 59, 81, 70, 9, -113, 36, 119, 28, 16, -127, -65, 32, -19, 109, -27, 24, -48, -80, 84, 47, 119, 25, 57, -118, -66, -22, -105, -11, 112, 16, -91, -127, 62, 23, 89, -17, -43, -105, -4, -43, 60, 42, -81, -95, -27, -8, 98, -37, 120, 80, -76, 93, -24, -104, -117, 38, -56, -68 }); + BigInteger p = new BigInteger(new byte[] { 0, -3, 127, 83, -127, 29, 117, 18, 41, 82, -33, 74, -100, 46, -20, -28, -25, -10, 17, -73, 82, 60, -17, 68, 0, -61, 30, 63, -128, -74, 81, 38, 105, 69, 93, 64, 34, 81, -5, 89, 61, -115, 88, -6, -65, -59, -11, -70, 48, -10, -53, -101, 85, 108, -41, -127, 59, -128, 29, 52, 111, -14, 102, 96, -73, 107, -103, 80, -91, -92, -97, -97, -24, 4, 123, 16, 34, -62, 79, -69, -87, -41, -2, -73, -58, 27, -8, 59, 87, -25, -58, -88, -90, 21, 15, 4, -5, -125, -10, -45, -59, 30, -61, 2, 53, 84, 19, 90, 22, -111, 50, -10, 117, -13, -82, 43, 97, -41, 42, -17, -14, 34, 3, 25, -99, -47, 72, 1, -57 }); + BigInteger q = new BigInteger(new byte[] { 0, -105, 96, 80, -113, 21, 35, 11, -52, -78, -110, -71, -126, -94, -21, -124, 11, -16, 88, 28, -11 }); + BigInteger g = new BigInteger(new byte[] { 0, -9, -31, -96, -123, -42, -101, 61, -34, -53, -68, -85, 92, 54, -72, 87, -71, 121, -108, -81, -69, -6, 58, -22, -126, -7, 87, 76, 11, 61, 7, -126, 103, 81, 89, 87, -114, -70, -44, 89, 79, -26, 113, 7, 16, -127, -128, -76, 73, 22, 113, 35, -24, 76, 40, 22, 19, -73, -49, 9, 50, -116, -56, -90, -31, 60, 22, 122, -117, 84, 124, -115, 40, -32, -93, -82, 30, 43, -77, -90, 117, -111, 110, -93, 127, 11, -6, 33, 53, 98, -15, -5, 98, 122, 1, 36, 59, -52, -92, -15, -66, -88, 81, -112, -119, -88, -125, -33, -31, 90, -27, -97, 6, -110, -117, 102, 94, -128, 123, 85, 37, 100, 1, 76, 59, -2, -49, 73, 42 }); + + byte[] data = "The Magic Words are Squeamish Ossifrage".getBytes(IOUtils.UTF8); + + // A previously signed and verified signature using the data and DSA key parameters above. + byte[] dataSig = new byte[] { 0, 0, 0, 7, 115, 115, 104, 45, 100, 115, 115, 0, 0, 0, 40, 40, -71, 33, 105, -89, -107, 8, 26, -13, -90, 73, -103, 105, 112, 7, -59, -66, 46, 85, -27, 20, 82, 22, -113, -75, -86, -121, -42, -73, 78, 66, 93, -34, 39, -50, -93, 27, -5, 37, -92 }; + + SignatureDSA signatureForSigning = new SignatureDSA(); + signatureForSigning.initSign(keyFactory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g))); + signatureForSigning.update(data); + byte[] sigBlob = signatureForSigning.encode(signatureForSigning.sign()); + byte[] sigFull = new Buffer.PlainBuffer().putString("ssh-dss").putBytes(sigBlob).getCompactData(); + + SignatureDSA signatureForVerifying = new SignatureDSA(); + signatureForVerifying.initVerify(keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g))); + signatureForVerifying.update(data); + Assert.assertTrue("Failed to verify signature: " + Arrays.toString(sigFull), signatureForVerifying.verify(sigFull)); + signatureForVerifying.update(data); + Assert.assertTrue("Failed to verify signature: " + Arrays.toString(dataSig), signatureForVerifying.verify(dataSig)); + } + +} diff --git a/src/test/java/net/schmizz/sshj/signature/VerificationTest.java b/src/test/java/net/schmizz/sshj/signature/SignatureECDSATest.java similarity index 99% rename from src/test/java/net/schmizz/sshj/signature/VerificationTest.java rename to src/test/java/net/schmizz/sshj/signature/SignatureECDSATest.java index 79f3f2b6a..2ed767c9a 100644 --- a/src/test/java/net/schmizz/sshj/signature/VerificationTest.java +++ b/src/test/java/net/schmizz/sshj/signature/SignatureECDSATest.java @@ -22,7 +22,7 @@ import java.security.PublicKey; -public class VerificationTest { +public class SignatureECDSATest { @Test public void testECDSA256Verifies() throws BufferException {