From 65ea6c17a0c1348aa9ef4e158102ddf173936882 Mon Sep 17 00:00:00 2001 From: Yuuta Liang Date: Thu, 23 Nov 2023 08:09:01 +0800 Subject: Add GUI Signed-off-by: Yuuta Liang --- src/main/model/x501/Name.java | 107 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 3 deletions(-) (limited to 'src/main/model/x501/Name.java') 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; @@ -52,6 +50,109 @@ public class Name extends ASN1Object { this.rdnSequence = list.toArray(new RelativeDistinguishedName[0]); } + /** + * 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 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 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 context, List curListKT, + ObjectIdentifier curKey, List 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 rdns = new ArrayList<>(); + List curListKT = new ArrayList<>(); + ObjectIdentifier curKey = null; + List 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. */ -- cgit v1.2.3