/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 org.wildfly.security.asn1;
import org.junit.Test;
import org.wildfly.security.util.ByteStringBuilder;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import static org.junit.Assert.*;
/**
* Tests for DER encoding. The expected results for these test cases were generated using
* Bouncy Castle's and Sun's DER libraries.
*
* @author <a href="mailto:fjuma@redhat.com">Farah Juma</a>
*/
public class DEREncoderTest {
@Test
public void testEncodeEmptyOctetString() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeOctetString("");
assertArrayEquals(new byte[] {4, 0}, target.toArray());
target = new ByteStringBuilder();
encoder = new DEREncoder(target);
encoder.encodeOctetString(new ByteStringBuilder());
assertArrayEquals(new byte[] {4, 0}, target.toArray());
target = new ByteStringBuilder();
encoder = new DEREncoder(target);
encoder.encodeOctetString(new byte[0]);
assertArrayEquals(new byte[] {4, 0}, target.toArray());
}
@Test
public void testEncodeOctetString() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeOctetString(new byte[] {1, 35, 69, 103, -119, -85, -51, -17});
byte[] expected = new byte[] {4, 8, 1, 35, 69, 103, -119, -85, -51, -17};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeEmptyIA5String() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeIA5String(new byte[0]);
assertArrayEquals(new byte[] {22, 0}, target.toArray());
target = new ByteStringBuilder();
encoder = new DEREncoder(target);
encoder.encodeIA5String(new ByteStringBuilder());
assertArrayEquals(new byte[] {22, 0}, target.toArray());
target = new ByteStringBuilder();
encoder = new DEREncoder(target);
encoder.encodeIA5String("");
assertArrayEquals(new byte[] {22, 0}, target.toArray());
}
@Test
public void testEncodeIA5String() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeIA5String("test1@rsa.com");
byte[] expected = new byte[] {22, 13, 116, 101, 115, 116, 49, 64, 114, 115, 97, 46, 99, 111, 109};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeEmptyBitString() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeBitString(new byte[] {}, 0);
byte[] expected = new byte[] {3, 1, 0};
assertArrayEquals(expected, target.toArray());
target = new ByteStringBuilder();
encoder = new DEREncoder(target);
encoder.encodeBitString("");
assertArrayEquals(new byte[] {3, 1, 0}, target.toArray());
}
@Test
public void testEncodeBitString() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeBitString(new byte[] {110, 93, -64}, 6);
byte[] expected = new byte[] {3, 4, 6, 110, 93, -64};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeBitStringUsingBinaryString() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeBitString("011011100101110111");
byte[] expected = new byte[] {(byte)0x03, (byte)0x04, (byte)0x06, (byte)0x6e, (byte)0x5d, (byte)0xc0};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeObjectIdentifier() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeObjectIdentifier("2.25.196556539987194312349856245628873852187.1.128");
byte[] expected = {6, 23, 105, -126, -89, -33, -76, -24, -97, -72, -72, -57, -75, -94, -46, -64, -128, -86, -82, -41, -118, 27, 1, -127, 0};
assertArrayEquals(expected, target.toArray());
}
@Test(expected=ASN1Exception.class)
public void testEncodeObjectIdentifierTooFewComponents() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeObjectIdentifier("1");
}
@Test(expected=ASN1Exception.class)
public void testEncodeObjectIdentifierInvalidFirstComponent() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeObjectIdentifier("5.10");
}
@Test(expected=ASN1Exception.class)
public void testEncodeObjectIdentifierInvalidSecondComponent() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeObjectIdentifier("0.50");
}
@Test
public void testEncodeNull() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeNull();
byte[] expected = {5, 0};
assertArrayEquals(expected, target.toArray());
}
@Test(expected=IllegalStateException.class)
public void testEncodeEndSequenceBeforeStart() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.endSequence();
}
@Test(expected=IllegalStateException.class)
public void testEncodeEndSetBeforeStart() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.endSequence();
}
@Test(expected=IllegalStateException.class)
public void testEncodeEndExplicitBeforeStart() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSequence();
encoder.encodeIA5String("server1@test.com");
encoder.endExplicit();
encoder.endSequence();
}
@Test
public void testEncodeSimpleSequence() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSequence();
encoder.encodeIA5String("this is a test");
encoder.encodeOctetString(new byte[] {1, 35, 69, 103, -119, -85, -51, -17});
encoder.encodeIA5String("test1@rsa.com");
encoder.endSequence();
byte[] expected = new byte[] {48, 41, 22, 14, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 22, 13, 116, 101, 115, 116, 49, 64, 114, 115, 97, 46, 99, 111, 109};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeComplexSequence() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSequence();
encoder.encodeIA5String("test string");
encoder.encodeOctetString(new byte[]{1, 35, 69, 103, -119, -85, -51, -17});
encoder.startSequence();
encoder.encodeIA5String("this is a test");
encoder.encodeOctetString(new byte[] {1, 35, 69, 103, -119, -85, -51, -17});
encoder.startSequence();
encoder.encodeIA5String("testing");
encoder.encodeIA5String("again");
encoder.endSequence();
encoder.encodeIA5String("test1@rsa.com");
encoder.endSequence();
encoder.encodeIA5String("the end");
encoder.endSequence();
byte[] expected = new byte[] {48, 93, 22, 11, 116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 103, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 48, 59, 22, 14, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 48, 16, 22, 7, 116, 101, 115, 116, 105, 110, 103, 22, 5, 97, 103, 97, 105, 110, 22, 13, 116, 101, 115, 116, 49, 64, 114, 115, 97, 46, 99, 111, 109, 22, 7, 116, 104, 101, 32, 101, 110, 100};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeSimpleSet() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSet();
encoder.encodeNull();
encoder.encodeOctetString(new byte[] {1, 35, 69, 103, -119, -85, -51, -17});
encoder.encodeObjectIdentifier("1.2.45684.5447897894");
encoder.encodeOctetString(new byte[] {1, 35, 69});
encoder.endSet();
byte[] expected = new byte[] {49, 28, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 4, 3, 1, 35, 69, 5, 0, 6, 9, 42, -126, -28, 116, -108, -91, -31, -90, 38};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeComplexSet() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSet();
encoder.encodeObjectIdentifier("1.2.123.1234");
encoder.encodeOctetString(new byte[]{1, 35, 69, 103, -119, -85, -51, -17});
encoder.startSet();
encoder.encodeObjectIdentifier("2.1.58.5456.36");
encoder.encodeOctetString(new byte[] {1, 35, 69});
encoder.startSet();
encoder.encodeNull();
encoder.encodeIA5String("this is a test");
encoder.endSet();
encoder.encodeNull();
encoder.endSet();
encoder.encodeIA5String("test1@rsa.com");
encoder.endSet();
byte[] expected = new byte[] { 49, 67, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 6, 4, 42, 123, -119, 82, 49, 34, 4, 3, 1, 35, 69, 5, 0, 6, 5, 81, 58, -86, 80, 36, 49, 18, 5, 0, 22, 14, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116, 22, 13, 116, 101, 115, 116, 49, 64, 114, 115, 97, 46, 99, 111, 109 };
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeSimpleSetOf() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSetOf();
encoder.encodeObjectIdentifier("1.2.459.14587.225466");
encoder.encodeObjectIdentifier("1.2.123.1234");
encoder.encodeObjectIdentifier("2.1.58.5456.36.456");
encoder.endSetOf();
byte[] expected = new byte[] {49, 25, 6, 4, 42, 123, -119, 82, 6, 7, 81, 58, -86, 80, 36, -125, 72, 6, 8, 42, -125, 75, -15, 123, -115, -31, 58};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeComplexSetOf() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSetOf();
encoder.startSetOf();
encoder.encodeIA5String("test1@rsa.com");
encoder.encodeIA5String("abc@rsa.com");
encoder.endSetOf();
encoder.startSetOf();
encoder.encodeIA5String("this is a string that's longer");
encoder.encodeIA5String("this is a string");
encoder.endSetOf();
encoder.endSetOf();
byte[] expected = new byte[] {49, 82, 49, 28, 22, 11, 97, 98, 99, 64, 114, 115, 97, 46, 99, 111, 109, 22, 13, 116, 101, 115, 116, 49, 64, 114, 115, 97, 46, 99, 111, 109, 49, 50, 22, 16, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 116, 114, 105, 110, 103, 22, 30, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 116, 114, 105, 110, 103, 32, 116, 104, 97, 116, 39, 115, 32, 108, 111, 110, 103, 101, 114};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeExplicit() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startExplicit(2);
encoder.startSequence();
encoder.encodeIA5String("this is a test");
encoder.encodeOctetString(new byte[] {1, 35, 69, 103, -119, -85, -51, -17});
encoder.encodeIA5String("test1@rsa.com");
encoder.endSequence();
encoder.endExplicit();
byte[] expected = new byte[] {-94, 43, 48, 41, 22, 14, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 22, 13, 116, 101, 115, 116, 49, 64, 114, 115, 97, 46, 99, 111, 109};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeImplicit() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeImplicit(2);
encoder.encodeIA5String("server1.example.com");
byte[] expected = new byte[] {-126, 19, 115, 101, 114, 118, 101, 114, 49, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testWriteEncoded() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSet();
encoder.encodeNull();
encoder.writeEncoded(new byte[] {4, 8, 1, 35, 69, 103, -119, -85, -51, -17});
encoder.encodeObjectIdentifier("1.2.45684.5447897894");
encoder.encodeOctetString(new byte[] {1, 35, 69});
encoder.endSet();
byte[] expected = new byte[] {49, 28, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 4, 3, 1, 35, 69, 5, 0, 6, 9, 42, -126, -28, 116, -108, -91, -31, -90, 38};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testImplicitWriteEncoded() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeImplicit(3);
encoder.writeEncoded(new byte[] {48, 41, 22, 14, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 22, 13, 116, 101, 115, 116, 49, 64, 114, 115, 97, 46, 99, 111, 109});
byte[] expected = new byte[] {-93,41,22,14,116,104,105,115,32,105,115,32,97,32,116,101,115,116,4,8,1,35,69,103,-119,-85,-51,-17,22,13,116,101,115,116,49,64,114,115,97,46,99,111,109};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testFlush() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSequence();
encoder.encodeIA5String("test string");
encoder.encodeOctetString(new byte[]{1, 35, 69, 103, -119, -85, -51, -17});
encoder.startSet();
encoder.encodeIA5String("this is a test");
encoder.encodeOctetString(new byte[] {1, 35, 69, 103, -119, -85, -51, -17});
encoder.startSequence();
encoder.encodeIA5String("testing");
encoder.encodeIA5String("again");
encoder.endSequence();
encoder.encodeIA5String("test1@rsa.com");
// Flush will end the unfinished set and sequence
encoder.flush();
byte[] expected = new byte[] {48, 84, 22, 11, 116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 103, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 49, 59, 4, 8, 1, 35, 69, 103, -119, -85, -51, -17, 48, 16, 22, 7, 116, 101, 115, 116, 105, 110, 103, 22, 5, 97, 103, 97, 105, 110, 22, 14, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 116, 101, 115, 116, 22, 13, 116, 101, 115, 116, 49, 64, 114, 115, 97, 46, 99, 111, 109};
assertArrayEquals(expected, target.toArray());
}
@Test
public void testEncodeInteger() throws Exception {
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.encodeInteger(123);
DERDecoder decoder = new DERDecoder(target.toArray());
assertEquals(123, decoder.decodeInteger().intValue());
}
@Test
public void testEncodeDSAKey() throws Exception {
KeyPair keyPair = KeyPairGenerator.getInstance("DSA").generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
DSAPublicKeySpec keySpec = keyFactory.getKeySpec(publicKey, DSAPublicKeySpec.class);
// dsa public key and params
BigInteger y = keySpec.getY();
BigInteger p = keySpec.getP();
BigInteger q = keySpec.getQ();
BigInteger g = keySpec.getG();
ByteStringBuilder target = new ByteStringBuilder();
DEREncoder encoder = new DEREncoder(target);
encoder.startSequence();
encoder.startSequence();
encoder.encodeObjectIdentifier(ASN1.OID_DSA);
encoder.startSequence();
encoder.encodeInteger(p);
encoder.encodeInteger(q);
encoder.encodeInteger(g);
encoder.endSequence();
encoder.endSequence();
encoder.encodeBitString(y);
encoder.endSequence();
byte[] der = target.toArray();
DERDecoder decoder = new DERDecoder(der);
decoder.startSequence();
decoder.startSequence();
assertEquals(ASN1.OID_DSA, decoder.decodeObjectIdentifier());
decoder.startSequence();
BigInteger decodedP = decoder.decodeInteger();
assertEquals(p, decodedP);
BigInteger decodedQ = decoder.decodeInteger();
assertEquals(q, decodedQ);
BigInteger decodedG = decoder.decodeInteger();
assertEquals(g, decodedG);
BigInteger decodedY = decoder.decodeBitStringAsInteger();
assertEquals(y, decodedY);
DSAPublicKeySpec dsaPublicKeySpec = new DSAPublicKeySpec(decodedY, decodedP, decodedQ, decodedG);
PublicKey keyFromSpec = keyFactory.generatePublic(dsaPublicKeySpec);
PublicKey keyFromDer = keyFactory.generatePublic(new X509EncodedKeySpec(der));
assertNotNull(keyFromSpec);
PrivateKey privateKey = keyPair.getPrivate();
byte[] toSign = "signed_content".getBytes();
Signature signature = Signature.getInstance(ASN1.OID_SHA1_WITH_DSA);
signature.initSign(privateKey);
signature.update(toSign);
byte[] contentSignature = signature.sign();
signature = Signature.getInstance(ASN1.OID_SHA1_WITH_DSA);
signature.initVerify(keyFromSpec);
signature.update(toSign);
assertTrue(signature.verify(contentSignature));
signature = Signature.getInstance(ASN1.OID_SHA1_WITH_DSA);
signature.initVerify(keyFromDer);
signature.update(toSign);
assertTrue(signature.verify(contentSignature));
}
}