/** * Decode an ASN.1 BIT STRING. * * @param buf the buffer containing the DER-encoded BIT STRING. * @return the bits in the BIT STRING */ public static BitSet decodeBitString(ByteBuffer buf) { DerId id = DerId.decode(buf); if (!id.matches(DerId.TagClass.UNIVERSAL, DerId.EncodingType.PRIMITIVE, ASN1_BIT_STRING_TAG_NUM) && !id.matches( DerId.TagClass.UNIVERSAL, DerId.EncodingType.CONSTRUCTED, ASN1_BIT_STRING_TAG_NUM)) { throw new IllegalArgumentException("Expected BIT STRING identifier, received " + id); } int len = DerUtils.decodeLength(buf); if (buf.remaining() < len) { throw new IllegalArgumentException("Insufficient content for BIT STRING"); } // If primitive encoding, then the initial octet is just used for padding if (id.getEncodingType() == DerId.EncodingType.PRIMITIVE) { buf.get(); len--; } int nbits = len * 8; BitSet bits = new BitSet(nbits); for (int i = 0; i < len; i++) { short next = (short) (0xff & buf.get()); int bitIndex = (i + 1) * 8 - 1; while (next != 0) { if ((next & 1) == 1) { bits.set(bitIndex); } bitIndex--; next >>>= 1; } } return bits; }
/** * Decode an ASN.1 SEQUENCE by reading the identifier and length octets. The remaining data in the * buffer is the SEQUENCE. * * @param buf the DER-encoded SEQUENCE * @return the length of the SEQUENCE */ public static int decodeSequence(ByteBuffer buf) { DerId id = DerId.decode(buf); if (!id.matches( DerId.TagClass.UNIVERSAL, DerId.EncodingType.CONSTRUCTED, ASN1_SEQUENCE_TAG_NUM)) { throw new IllegalArgumentException("Expected SEQUENCE identifier, received " + id); } int len = DerUtils.decodeLength(buf); if (buf.remaining() < len) { throw new IllegalArgumentException("Insufficient content for SEQUENCE"); } return len; }
/** * Decode an ASN.1 IA5String. * * @param buf the DER-encoded IA5String * @return the string */ public static String decodeIA5String(ByteBuffer buf) { DerId id = DerId.decode(buf); if (!id.matches( DerId.TagClass.UNIVERSAL, DerId.EncodingType.PRIMITIVE, ASN1_IA5STRING_TAG_NUM)) { throw new IllegalArgumentException("Expected IA5String identifier, received " + id); } int len = DerUtils.decodeLength(buf); if (buf.remaining() < len) { throw new IllegalArgumentException("Insufficient content for IA5String"); } byte[] dst = new byte[len]; buf.get(dst); return new String(dst); }
/** * Decode an ASN.1 GeneralizedTime. * * @param buf the DER-encoded GeneralizedTime * @return the data and time */ public static Date decodeGeneralizedTime(ByteBuffer buf) { // GeneralizedTime ::= [UNIVERSAL 24] IMPLICIT VisibleString DerId id = DerId.decode(buf); if (!id.matches( DerId.TagClass.UNIVERSAL, DerId.EncodingType.PRIMITIVE, ASN1_GENERALIZED_TIME_TAG_NUM)) { throw new IllegalArgumentException("Expected GeneralizedTime identifier, received " + id); } int len = DerUtils.decodeLength(buf); if (buf.remaining() < len) { throw new IllegalArgumentException("Insufficient content for GeneralizedTime"); } Date date = null; byte[] dst = new byte[len]; buf.get(dst); String iso8601DateString = new String(dst); Matcher matcher = GENERALIZED_TIME_PATTERN.matcher(iso8601DateString); if (matcher.matches()) { Calendar cal = Calendar.getInstance(); cal.clear(); // Process yyyyMMddHHmmss cal.set(Calendar.YEAR, Integer.parseInt(matcher.group(1))); cal.set( Calendar.MONTH, Integer.parseInt(matcher.group(2)) - 1); // Calendar.MONTH is zero based cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(matcher.group(3))); cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(matcher.group(4))); cal.set(Calendar.MINUTE, Integer.parseInt(matcher.group(5))); cal.set(Calendar.SECOND, Integer.parseInt(matcher.group(6))); // Process fractional seconds, if any String fracSecStr = matcher.group(7); if (fracSecStr != null) { cal.set(Calendar.MILLISECOND, (int) (Float.parseFloat(fracSecStr) * 1000)); } // Process time zone, if any String tzStr = matcher.group(8); if (tzStr != null) { cal.setTimeZone(TimeZone.getTimeZone("Z".equals(tzStr) ? "GMT" : "GMT" + tzStr)); } date = cal.getTime(); } else { throw new IllegalArgumentException("Malformed GeneralizedTime " + iso8601DateString); } return date; }
/** * Decode an ASN.1 OCTET STRING. * * @param buf the DER-encoded OCTET STRING * @return the octets */ public static short[] decodeOctetString(ByteBuffer buf) { DerId id = DerId.decode(buf); if (!id.matches(DerId.TagClass.UNIVERSAL, ASN1_OCTET_STRING_TAG_NUM)) { throw new IllegalArgumentException("Expected OCTET STRING identifier, received " + id); } int len = DerUtils.decodeLength(buf); if (buf.remaining() < len) { throw new IllegalArgumentException("Insufficient content for OCTET STRING"); } short[] dst = new short[len]; for (int i = 0; i < len; i++) { dst[i] = (short) (0xff & buf.get()); } return dst; }
/** * Decode an ASN.1 INTEGER. * * @param buf the buffer containing the DER-encoded INTEGER * @return the value of the INTEGER */ public static int decodeInteger(ByteBuffer buf) { DerId id = DerId.decode(buf); if (!id.matches(DerId.TagClass.UNIVERSAL, DerId.EncodingType.PRIMITIVE, ASN1_INTEGER_TAG_NUM)) { throw new IllegalArgumentException("Expected INTEGER identifier, received " + id); } int len = DerUtils.decodeLength(buf); if (buf.remaining() < len) { throw new IllegalArgumentException("Insufficient content for INTEGER"); } int value = 0; for (int i = 0; i < len; i++) { value = (value << 8) + (0xff & buf.get()); } return value; }