aboutsummaryrefslogtreecommitdiff
path: root/src/main/model/x501/Name.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/model/x501/Name.java')
-rw-r--r--src/main/model/x501/Name.java107
1 files changed, 104 insertions, 3 deletions
diff --git a/src/main/model/x501/Name.java b/src/main/model/x501/Name.java
index 19cde56..7477005 100644
--- a/src/main/model/x501/Name.java
+++ b/src/main/model/x501/Name.java
@@ -1,8 +1,6 @@
package model.x501;
-import model.asn1.ASN1Object;
-import model.asn1.Encodable;
-import model.asn1.Tag;
+import model.asn1.*;
import model.asn1.exceptions.ParseException;
import model.asn1.parsing.BytesReader;
@@ -53,6 +51,109 @@ public class Name extends ASN1Object {
}
/**
+ * EFFECTS: Parse OID after last KV and clear context if input is '='. Otherwise add to context.
+ * Throws {@link ParseException} if input is '+' or ',', or if the oid cannot be recognized.
+ * MODIFIES: context
+ */
+ private static ObjectIdentifier handleKey(char c, List<Character> context) throws ParseException {
+ if (c == '=') {
+ if (context.isEmpty()) {
+ throw new ParseException("Unterminated key");
+ }
+ final ObjectIdentifier oid = new ObjectIdentifier(ObjectIdentifier.TAG, null,
+ ObjectIdentifier.getKnown(
+ context.stream().map(Object::toString).collect(Collectors.joining(""))));
+ context.clear();
+ return oid;
+ } else if (c == '+' || c == ',') {
+ throw new ParseException("Unterminated key part: " + context);
+ } else {
+ context.add(c);
+ return null;
+ }
+ }
+
+ /**
+ * EFFECTS: Parse KV after '='. Clear context.
+ * Throws {@link ParseException} if context is empty.
+ * MODIFIES: context
+ * REQUIRES: curKey to be a valid OID
+ */
+ private static AttributeTypeAndValue flushKV(ObjectIdentifier curKey, List<Character> context)
+ throws ParseException {
+ if (context.isEmpty()) {
+ throw new ParseException("Unterminated value");
+ }
+ final AttributeTypeAndValue tv = new AttributeTypeAndValue(ASN1Object.TAG_SEQUENCE, null, curKey,
+ new PrintableString(PrintableString.TAG, null,
+ context.stream().map(Object::toString).collect(Collectors.joining(""))));
+ context.clear();
+ return tv;
+ }
+
+ /**
+ * EFFECTS: Handle value after =, optionally flush to rdns after ',', or add to curListKT if after '+'. Clears
+ * context if flushed, otherwise add to context. Returns whether switch to the state of reading key.
+ * Throws {@link ParseException} if c is '=' or context is empty.
+ * MODIFIES: context, curListKT, rdns
+ * REQUIRES: curKey to be a valid OID
+ */
+ private static boolean handleValue(char c, List<Character> context, List<AttributeTypeAndValue> curListKT,
+ ObjectIdentifier curKey, List<RelativeDistinguishedName> rdns)
+ throws ParseException {
+ if (c == ',') {
+ if (context.isEmpty()) {
+ throw new ParseException("Unterminated value");
+ }
+ curListKT.add(flushKV(curKey, context));
+ rdns.add(new RelativeDistinguishedName(ASN1Object.TAG_SET, null,
+ curListKT.toArray(AttributeTypeAndValue[]::new)));
+ curListKT.clear();
+ return true;
+ } else if (c == '+') {
+ curListKT.add(flushKV(curKey, context));
+ return true;
+ } else if (c == '=') {
+ throw new ParseException("Unterminated value part: " + context);
+ } else {
+ context.add(c);
+ return false;
+ }
+ }
+
+ /**
+ * EFFECTS: Parse the given DN string into structural X.509 RDN Sequence.
+ * Character literals = + , must be escaped.
+ * Values will always be PrintableString.
+ * Throws {@link ParseException} if invalid.
+ */
+ public static Name parseString(String dn) throws ParseException {
+ char state = 0; // 0 - Key, 1 - Value; MSB: Escaped
+ List<RelativeDistinguishedName> rdns = new ArrayList<>();
+ List<AttributeTypeAndValue> curListKT = new ArrayList<>();
+ ObjectIdentifier curKey = null;
+ List<Character> context = new ArrayList<>();
+ for (char c : (dn + ",").toCharArray()) {
+ if ((state >> 7) == 1) {
+ context.add(c);
+ state &= 127;
+ continue;
+ } else if (c == '\\') {
+ state |= 128;
+ continue;
+ }
+ if (state == 0) {
+ if ((curKey = handleKey(c, context)) != null) {
+ state = 1;
+ }
+ } else if (handleValue(c, context, curListKT, curKey, rdns)) {
+ state = 0;
+ }
+ }
+ return new Name(ASN1Object.TAG_SEQUENCE, null, rdns.toArray(RelativeDistinguishedName[]::new));
+ }
+
+ /**
* EFFECTS: Encode the SEQUENCE OF into DER, keep order. RDNs will be encoded one-by-one.
*/
@Override