Apache License 2.0

项目介绍

alexvoronov/gcdc-asn1 是一个托管在 GitHub 上的开源项目,主要使用 Java 编写,采用 Apache License 2.0 许可证,目前获得 52 个 Star、23 次 Fork。

仓库全名
alexvoronov/gcdc-asn1
开源许可证
Apache License 2.0
Star 数
52
Fork 数
23
未关闭 Issue
2

README 摘要

ASN.1 Datatypes Java annotations to augment Java classes with information from ASN.1 specifications. These annotations can later be used by encoders like asn1-uper. Table of Contents: Status Supported ASN.1 Features Design choices Examples Example 1: Without restrictions or extension markers Example 2: With Restrictions, no extension markers Example 3: With restrictions and extension markers Other ASN.1 tools…

完整内容见 GitHub 原始 README 或本页 。

常见问题

gcdc-asn1 用什么语言开发?

主要使用 Java 编写,采用 Apache License 2.0 许可证,目前有 52 个 Star。

gcdc-asn1 可以免费商用吗?

仓库采用 Apache License 2.0 许可证,可免费使用与修改,商业使用请遵守许可证条款(例如保留版权声明与许可声明)。

如何获取 gcdc-asn1 的源码?

可以直接克隆仓库:git clone https://github.com/alexvoronov/gcdc-asn1.git,默认分支为 master;安装与运行步骤见仓库 README。

gcdc-asn1 最近还在维护吗?

仓库最近一次更新为 2025年11月12日,当前有 2 个未关闭 Issue;Star 数为 52,可作为社区活跃度的参考。

关于

ASN.1 data types and UPER encoder/decoder

描述取自 GitHub 官方 API,中文内容由 GitRead 中文社区 编辑团队整理。

简要分析

热度分
150
价值分
43
活跃状态
活跃

语言占比

Java

README

ASN.1 Datatypes

Java annotations to augment Java classes with information from ASN.1 specifications. These annotations can later be used by encoders like asn1-uper.

Table of Contents:

Status

Datatypes are enough to handle camdenm. There is no compiler yet, so Java classes and annotations have to be created and added manually.

Supported ASN.1 Features

The following ASN.1 features are implemented:

ASN.1 Java
INTEGER (unconstrained) BigInteger
INTEGER (constrained) short, int, long, BigInteger (depending on constraint)
BOOLEAN boolean
ENUMERATED enum
SEQUENCE class
CHOICE class
BIT STRING (constrained to fixed length) class
BIT STRING (non-fixed length) List<Boolean>
OCTET STRING List<Byte>
IA5String, UTF8String, VisibleString String
SEQUENCE OF T List<T> (or SequenceOfT extends Asn1SequenceOf<T>)
SET OF T also List<T>

Design choices

Design goal was to make the library as clean and simple as possible for the end user, even if it will not be very performant. Inspiration was taken from GSON, where they use reflection to extract fields from classes.

ASN.1 has three major ways to construct complex datatypes: SEQUENCE, CHOICE and SEQUENCE OF. They correspond to struct , union and arrays of C language. (ASN.1 SET and SET OF are pretty much identical to SEQUENCE and SEQUENCE OF, at least for PER, so they are not considered separately).

A SEQUENCE is encoded as an ordinary Java class. Java Reflection API Class.getFields() can return all fields of a class. The documentation says that there is no guarantee on the order in which the fields are returned, however, since Java version 1.6 the fields are returned in the declaration order.

A CHOICE is encoded as an ordinary Java class as well, with exactly one field in the class being non-null, corresponding to the element present.

A SEQUENCE OF is encoded as List<T>. Java type erasure in generics results in the type being lost at run-time if there is no object, so decoding will not work for generics (encoding is fine). So the class have to be non-generic and extend List<T>. GSON allows generic classes by using extra parameters. Scala also solved this issue, using Manifests and implicit parameters. For this library, just making a non-generic wrapper class with a concrete type for List is good enough. To make instantiation of abstract List<T> easier, there is Asn1SequenceOF<T> (it was not possible to extend, for example, ArrayList<T> directly).

OCTET STRING is just a SEQUENCE OF byte. BIT STRING is a SEQUENCE OF boolean. ENUMERATED is an enum.

Integers in ASN.1 are unbounded by default, so BigInteger is used to represent them. If an integer is constrained, then long, int or short will be enough to represent it.

All strings, including IA5String, UTF8String and VisibleString are represented by Java String, the difference is only in annotations.

OPTIONAL is implemented as a nullable field with an annotation. Using built-in Java 8 Optional<T> was not possible due to type erasure, and creating non-generic wrapper classes every time OPTIONAL is used is too much of a burden, so a simple nullable field is used. The name for annotation is @Asn1Optional, to not clash with built-in Optional.

DEFAULT is treated in ASN.1 almost exactly as OPTIONAL, so the annotation from OPTIONAL is used for DEFAULT as well. The only difference is that DEFAULT have a static initializer in the Java class.

ASN.1 restrictions are implemented as Java Annotations. Integers have @IntRange, sequences have @SizeRange and @FixedSize, strings have alphabets etc.

ASN.1 Extensions are marked by annotations too. A sequence that has an extension marker have annotation @HasExtensionMarker, and all elements that come after that marker in ASN.1 are marked with @IsExtension in the Java class. @IntRange() and @SizeRange() also support an optional argument hasExtension=true, which is set to false by default.

Examples

Here’s how three examples from the Annex A of the UPER standard (ITU X.691) would be encoded in Java.

Example 1: Without restrictions or extension markers

See this code in action as a part of asn1-uper test suite in UperEncoderExample1BasicTest.java.

ASN.1:

PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET { 
  name Name,
  title [0] VisibleString, 
  number EmployeeNumber, 
  dateOfHire [1] Date, 
  nameOfSpouse [2] Name,
  children [3] IMPLICIT
  SEQUENCE OF ChildInformation DEFAULT {} }

ChildInformation ::= SET { 
  name Name,
  dateOfBirth [0] Date} 

Name ::= [APPLICATION 1] IMPLICIT SEQUENCE { 
  givenName VisibleString,
  initial VisibleString,
  familyName VisibleString }

EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER

Date ::= [APPLICATION 3] IMPLICIT VisibleString -- YYYYMMDD

Corresponding Java code:

@Sequence
public static class PersonenelRecord {
  Name name;
  EmployeeNumber number;
  @RestrictedString(CharacterRestriction.VisibleString)
  String title;
  Date dateOfHire;
  Name nameOfSpouse;
  @Asn1Optional SequenceOfChildInformation sequenceOfChildInformation = new SequenceOfChildInformation();

  public PersonenelRecord() {
  this(new Name(), new EmployeeNumber(), "", new Date(), new Name(), new SequenceOfChildInformation());
  }

  public PersonenelRecord(
    Name name,
    EmployeeNumber number,
    String title,
    Date dateOfHire,
    Name nameOfSpouse,
    SequenceOfChildInformation sequenceOfChildInformation
    ) {
  this.name = name;
  this.number = number;
  this.title = title;
  this.dateOfHire = dateOfHire;
  this.nameOfSpouse = nameOfSpouse;
  this.sequenceOfChildInformation = sequenceOfChildInformation;
  }
}

@Sequence
public static class Name {
  @RestrictedString(CharacterRestriction.VisibleString)
  String givenName;
  @RestrictedString(CharacterRestriction.VisibleString)
  String initial;
  @RestrictedString(CharacterRestriction.VisibleString)
  String familyName;

  public Name() { this("", "", ""); }
  public Name(String givenName, String initial, String familyName) {
  this.givenName = givenName;
  this.initial = initial;
  this.familyName = familyName;
  }
}

public static class EmployeeNumber extends Asn1BigInteger {
  public EmployeeNumber() { this(0); }
  public EmployeeNumber(long value) { this(BigInteger.valueOf(value)); }
  public EmployeeNumber(BigInteger value) { super(value); }
}

@RestrictedString(CharacterRestriction.VisibleString)
public static class Date extends Asn1String {
  public Date() { this(""); }
  public Date(String value) { super(value); }
}

@Sequence
public static class ChildInformation {
  Name name;
  Date dateOfBirth;

  public ChildInformation() { this(new Name(), new Date()); }
  public ChildInformation(Name name, Date dateOfBirth) {
  this.name = name;
  this.dateOfBirth = dateOfBirth;
  }
}

public static class SequenceOfChildInformation extends Asn1SequenceOf<ChildInformation> {
  public SequenceOfChildInformation() { super(); }
  public SequenceOfChildInformation(Collection<ChildInformation> coll) { super(coll); }
}

And the use of those classes:

PersonenelRecord record = new PersonenelRecord(
  new Name(
  "John",
  "P",
  "Smith"
  ),
  new EmployeeNumber(51),
  "Director",
  new Date("19710917"),
  new Name(
  "Mary",
  "T",
  "Smith"),
  new SequenceOfChildInformation(Arrays.asList(
  new ChildInformation(
    new Name(
    "Ralph",
    "T",
    "Smith"
    ),
    new Date("19571111")
  ),
  new ChildInformation(
    new Name(
    "Susan",
    "B",
    "Jones"
    ),
    new Date("19590717")
  )
  ))
);

Example 2: With Restrictions, no extension markers

See code in UperEncoderExample2RestrictionTest.java

ASN.1:

PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET {
  name Name,
  title [0] VisibleString,
  number EmployeeNumber,
  dateOfHire [1] Date,
  nameOfSpouse [2] Name,
  children [3] IMPLICIT
  SEQUENCE OF ChildInformation DEFAULT {} }

ChildInformation ::= SET {
  name Name,
  dateOfBirth [0] Date}

Name ::= [APPLICATION 1] IMPLICIT SEQUENCE {
  givenName NameString,
  initial NameString (SIZE(1)),
  familyName NameString }

EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER

Date ::= [APPLICATION 3] IMPLICIT VisibleString (FROM("0".."9") ^ SIZE(8)) -- YYYYMMDD

NameString ::= VisibleString (FROM("a".."z" | "A".."Z" | "-.") ^ SIZE(1..64))

Java classes:

@Sequence
public static class PersonenelRecord {
  Name name;
  EmployeeNumber number;
  @RestrictedString(CharacterRestriction.VisibleString)
  String title;
  Date dateOfHire;
  Name nameOfSpouse;
  @Asn1Optional SequenceOfChildInformation sequenceOfChildInformation = new SequenceOfChildInformation();

  public PersonenelRecord() {
    this(new Name(), new EmployeeNumber(), "", new Date(), new Name(), new SequenceOfChildInformation());
  }

  public PersonenelRecord(
      Name name,
      EmployeeNumber number,
      String title,
      Date dateOfHire,
      Name nameOfSpouse,
      SequenceOfChildInformation sequenceOfChildInformation
      ) {
    this.name = name;
    this.number = number;
    this.title = title;
    this.dateOfHire = dateOfHire;
    this.nameOfSpouse = nameOfSpouse;
    this.sequenceOfChildInformation = sequenceOfChildInformation;
  }
}

@Sequence
public static class Name {
  NameString givenName;
  @FixedSize(1)
  NameString initial;
  NameString familyName;

  public Name() { this(new NameString(), new NameString(), new NameString()); }
  public Name(NameString givenName, NameString initial, NameString familyName) {
    this.givenName = givenName;
    this.initial = initial;
    this.familyName = familyName;
  }
}

//"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-."
@RestrictedString(value = CharacterRestriction.VisibleString, alphabet = NameString.NameStringAlphabet.class)
@SizeRange(minValue = 1, maxValue = 64)
public static class NameString extends Asn1String {
  public NameString() { this(""); }
  public NameString(String value) { super(value); }

  public static class NameStringAlphabet extends Alphabet {
    private final static String chars =
        new AlphabetBuilder().withRange('a', 'z').withRange('A','Z').withChars("-.").chars();
    public NameStringAlphabet() {
      super(chars);
    }
  }
}

public static class EmployeeNumber extends Asn1BigInteger {
  public EmployeeNumber() { this(0); }
  public EmployeeNumber(long value) { this(BigInteger.valueOf(value)); }
  public EmployeeNumber(BigInteger value) { super(value); }
}

@RestrictedString(value = CharacterRestriction.VisibleString, alphabet = Date.DateAlphabet.class)
@FixedSize(8)
public static class Date extends Asn1String {
  public Date() { this(""); }
  public Date(String value) { super(value); }
  public static class DateAlphabet extends Alphabet {
    private final static String chars = new AlphabetBuilder().withRange('0', '9').chars();
    public DateAlphabet() {
      super(chars);
    }
  }
}

@Sequence
public static class ChildInformation {
  Name name;
  Date dateOfBirth;

  public ChildInformation() { this(new Name(), new Date()); }
  public ChildInformation(Name name, Date dateOfBirth) {
    this.name = name;
    this.dateOfBirth = dateOfBirth;
  }
}

public static class SequenceOfChildInformation extends Asn1SequenceOf<ChildInformation> {
  public SequenceOfChildInformation() { super(); }
  public SequenceOfChildInformation(Collection<ChildInformation> coll) { super(coll); }
}

And example object instantiation:

PersonenelRecord record = new PersonenelRecord(
  new Name(
    new NameString("John"),
    new NameString("P"),
    new NameString("Smith")
  ),
  new EmployeeNumber(51),
  "Director",
  new Date("19710917"),
  new Name(
    new NameString("Mary"),
    new NameString("T"),
    new NameString("Smith")
  ),
  new SequenceOfChildInformation(Arrays.asList(
    new ChildInformation(
      new Name(
        new NameString("Ralph"),
        new NameString("T"),
        new NameString("Smith")
      ),
      new Date("19571111")
    ),
    new ChildInformation(
      new Name(
        new NameString("Susan"),
        new NameString("B"),
        new NameString("Jones")
      ),
      new Date("19590717")
    )
  ))
);

Example 3: With restrictions and extension markers

See code in UperEncoderExample3ExtensionTest.java.

ASN.1 schema:

PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET {
name Name,
title [0] VisibleString, 
number EmployeeNumber, 
dateOfHire [1] Date, 
nameOfSpouse [2] Name,
children [3] IMPLICIT
SEQUENCE (SIZE(2, ...)) OF ChildInformation OPTIONAL,
... }

ChildInformation ::= SET { 
name Name, 
dateOfBirth [0] Date,
...,
sex [1] IMPLICIT ENUMERATED {male(1), female(2),
unknown(3)} OPTIONAL 
}

Name ::= [APPLICATION 1] IMPLICIT SEQUENCE { 
  givenName NameString,
  initial NameString (SIZE(1)),
  familyName NameString,
  ... 
}

EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER (0..9999, ...)

Date ::= [APPLICATION 3] IMPLICIT VisibleString
(FROM("0".."9") ^ SIZE(8, ..., 9..20)) -- YYYYMMDD 

NameString ::= VisibleString
(FROM("a".."z" | "A".."Z" | "-.") ^ SIZE(1..64, ...))

Corresponding Java code:

@Sequence
@HasExtensionMarker
public static class PersonenelRecord {
  Name name;
  EmployeeNumber number;
  @RestrictedString(CharacterRestriction.VisibleString)
  String title;
  Date dateOfHire;
  Name nameOfSpouse;
  @Asn1Optional SequenceOfChildInformation sequenceOfChildInformation;

  public PersonenelRecord() {
    this(new Name(), new EmployeeNumber(), "", new Date(), new Name(), new SequenceOfChildInformation());
  }

  public PersonenelRecord(
      Name name,
      EmployeeNumber number,
      String title,
      Date dateOfHire,
      Name nameOfSpouse,
      SequenceOfChildInformation sequenceOfChildInformation
      ) {
    this.name = name;
    this.number = number;
    this.title = title;
    this.dateOfHire = dateOfHire;
    this.nameOfSpouse = nameOfSpouse;
    this.sequenceOfChildInformation = sequenceOfChildInformation;
  }
}

@Sequence
@HasExtensionMarker
public static class Name {
  NameString givenName;
  @FixedSize(1)
  NameString initial;
  NameString familyName;

  public Name() { this(new NameString(), new NameString(), new NameString()); }
  public Name(NameString givenName, NameString initial, NameString familyName) {
    this.givenName = givenName;
    this.initial = initial;
    this.familyName = familyName;
  }
}

//"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-."
@RestrictedString(value = CharacterRestriction.VisibleString, alphabet = NameString.NameStringAlphabet.class)
@SizeRange(minValue = 1, maxValue = 64, hasExtensionMarker = true)
public static class NameString extends Asn1String {
  public NameString() { this(""); }
  public NameString(String value) { super(value); }

  public static class NameStringAlphabet extends Alphabet {
    private final static String chars =
        new AlphabetBuilder().withRange('a', 'z').withRange('A','Z').withChars("-.").chars();
    public NameStringAlphabet() {
      super(chars);
    }
  }
}

@IntRange(minValue = 0, maxValue = 9999, hasExtensionMarker = true)
public static class EmployeeNumber extends Asn1Integer {
  public EmployeeNumber() { this(0); }
  public EmployeeNumber(long value) { super(value); }
}

@RestrictedString(value = CharacterRestriction.VisibleString, alphabet = Date.DateAlphabet.class)
@SizeRange(minValue = 8, maxValue = 8, hasExtensionMarker = true)
public static class Date extends Asn1String {
  public Date() { this(""); }
  public Date(String value) { super(value); }
  public static class DateAlphabet extends Alphabet {
    private final static String chars = new AlphabetBuilder().withRange('0', '9').chars();
    public DateAlphabet() {
      super(chars);
    }
  }
}

@Sequence
@HasExtensionMarker
public static class ChildInformation {
  Name name;
  Date dateOfBirth;

  @IsExtension
  @Asn1Optional
  Sex sex;

  public ChildInformation() { this(new Name(), new Date()); }
  public ChildInformation(Name name, Date dateOfBirth) {
    this(name, dateOfBirth, null);
  }
  public ChildInformation(Name name, Date dateOfBirth, Sex sex) {
    this.name = name;
    this.dateOfBirth = dateOfBirth;
    this.sex = sex;
  }
}

public static enum Sex {
  male(1),
  female(2),
  unknown(3);

  private final int value;
  public int value() { return value; }
  private Sex(int value) { this.value = value; }
}

@SizeRange(minValue=2, maxValue=2, hasExtensionMarker=true)
public static class SequenceOfChildInformation extends Asn1SequenceOf<ChildInformation> {
  public SequenceOfChildInformation() { super(); }
  public SequenceOfChildInformation(Collection<ChildInformation> coll) { super(coll); }
}

And example instantiation code:

PersonenelRecord record = new PersonenelRecord(
  new Name(
    new NameString("John"),
    new NameString("P"),
    new NameString("Smith")
  ),
  new EmployeeNumber(51),
  "Director",
  new Date("19710917"),
  new Name(
    new NameString("Mary"),
    new NameString("T"),
    new NameString("Smith")
  ),
  new SequenceOfChildInformation(Arrays.asList(
    new ChildInformation(
      new Name(
        new NameString("Ralph"),
        new NameString("T"),
        new NameString("Smith")
      ),
      new Date("19571111")
    ),
    new ChildInformation(
      new Name(
        new NameString("Susan"),
        new NameString("B"),
        new NameString("Jones")
      ),
      new Date("19590717"),
      Sex.female
    )
  ))
);

Other ASN.1 tools

ITU-T have a list of ASN.1 tools. IvmaiAsn project also have its own list.

Here is my (incomplete) list of the open-source tools:

Name License Runtime Compiler BER, DER? UPER?
asn1c BSD 2-clause C C ✓ ✓
asn1scc Dual: LGPL, commercial C, Ada F#, Antlr/Java ✓ ✓
snacc GPL C C, C++ ✓
III ASN.1 Mozilla C++ C++ ✓ ✓
libtasn1 LGPL ANSI C99 C ✓ (DER)
pyasn1 BSD 2-clause Python asn1ate (Python) ✓
dpkt BSD 3-Clause Python . ✓
libmich GPLv2 Python Python ✓ ✓
ans1tools MIT Python Python ✓ ✓
ASN1js BSD 3-clause JavaScript . ✓
asn1js MIT JavaScript . ✓
node-asn1 MIT JavaScript . ✓ (BER)
ASN1.js MIT JavaScript . ✓ (DER)
ASN1s GPL Java Java/Antlr ✓ (BER)
jASN1 LGPL Java Java ✓ (BER)
openASN.1 LGPL Java Java ✓ ✓
asn1forj GPL Java ? ✓
JAC GPL Java ? ✓ (BER, CER, DER)
JASN GPL Java ? ✓ (BER, DER)
Binary Notes Apache Java, .NET XSLT ✓ ✓
arc BSD 4-clause Java javacc/Java
Cryptix BSD 2-clause Java SableCC
Legion of The Bouncy Castle MIT, MIT X11 Java, C# . ✓ (DER, BER)
Apache Harmony Apache Java .

Acknowledgments

This implementation was partly developed within i-GAME project that has received funding from the European Union’s Seventh Framework Programme for research, technological development and demonstration under grant agreement no 612035.

Use of annotations and reflection was inspired by Gson.

License

This code is released under Apache 2.0 license.

评论

暂无评论