Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/security/PKCS12Attribute.java
38829 views
/*1* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.security;2627import java.io.IOException;28import java.math.BigInteger;29import java.util.Arrays;30import java.util.regex.Pattern;31import sun.security.util.*;3233/**34* An attribute associated with a PKCS12 keystore entry.35* The attribute name is an ASN.1 Object Identifier and the attribute36* value is a set of ASN.1 types.37*38* @since 1.839*/40public final class PKCS12Attribute implements KeyStore.Entry.Attribute {4142private static final Pattern COLON_SEPARATED_HEX_PAIRS =43Pattern.compile("^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+$");44private String name;45private String value;46private byte[] encoded;47private int hashValue = -1;4849/**50* Constructs a PKCS12 attribute from its name and value.51* The name is an ASN.1 Object Identifier represented as a list of52* dot-separated integers.53* A string value is represented as the string itself.54* A binary value is represented as a string of colon-separated55* pairs of hexadecimal digits.56* Multi-valued attributes are represented as a comma-separated57* list of values, enclosed in square brackets. See58* {@link Arrays#toString(java.lang.Object[])}.59* <p>60* A string value will be DER-encoded as an ASN.1 UTF8String and a61* binary value will be DER-encoded as an ASN.1 Octet String.62*63* @param name the attribute's identifier64* @param value the attribute's value65*66* @exception NullPointerException if {@code name} or {@code value}67* is {@code null}68* @exception IllegalArgumentException if {@code name} or69* {@code value} is incorrectly formatted70*/71public PKCS12Attribute(String name, String value) {72if (name == null || value == null) {73throw new NullPointerException();74}75// Validate name76ObjectIdentifier type;77try {78type = new ObjectIdentifier(name);79} catch (IOException e) {80throw new IllegalArgumentException("Incorrect format: name", e);81}82this.name = name;8384// Validate value85int length = value.length();86String[] values;87if (value.charAt(0) == '[' && value.charAt(length - 1) == ']') {88values = value.substring(1, length - 1).split(", ");89} else {90values = new String[]{ value };91}92this.value = value;9394try {95this.encoded = encode(type, values);96} catch (IOException e) {97throw new IllegalArgumentException("Incorrect format: value", e);98}99}100101/**102* Constructs a PKCS12 attribute from its ASN.1 DER encoding.103* The DER encoding is specified by the following ASN.1 definition:104* <pre>105*106* Attribute ::= SEQUENCE {107* type AttributeType,108* values SET OF AttributeValue109* }110* AttributeType ::= OBJECT IDENTIFIER111* AttributeValue ::= ANY defined by type112*113* </pre>114*115* @param encoded the attribute's ASN.1 DER encoding. It is cloned116* to prevent subsequent modificaion.117*118* @exception NullPointerException if {@code encoded} is119* {@code null}120* @exception IllegalArgumentException if {@code encoded} is121* incorrectly formatted122*/123public PKCS12Attribute(byte[] encoded) {124if (encoded == null) {125throw new NullPointerException();126}127this.encoded = encoded.clone();128129try {130parse(encoded);131} catch (IOException e) {132throw new IllegalArgumentException("Incorrect format: encoded", e);133}134}135136/**137* Returns the attribute's ASN.1 Object Identifier represented as a138* list of dot-separated integers.139*140* @return the attribute's identifier141*/142@Override143public String getName() {144return name;145}146147/**148* Returns the attribute's ASN.1 DER-encoded value as a string.149* An ASN.1 DER-encoded value is returned in one of the following150* {@code String} formats:151* <ul>152* <li> the DER encoding of a basic ASN.1 type that has a natural153* string representation is returned as the string itself.154* Such types are currently limited to BOOLEAN, INTEGER,155* OBJECT IDENTIFIER, UTCTime, GeneralizedTime and the156* following six ASN.1 string types: UTF8String,157* PrintableString, T61String, IA5String, BMPString and158* GeneralString.159* <li> the DER encoding of any other ASN.1 type is not decoded but160* returned as a binary string of colon-separated pairs of161* hexadecimal digits.162* </ul>163* Multi-valued attributes are represented as a comma-separated164* list of values, enclosed in square brackets. See165* {@link Arrays#toString(java.lang.Object[])}.166*167* @return the attribute value's string encoding168*/169@Override170public String getValue() {171return value;172}173174/**175* Returns the attribute's ASN.1 DER encoding.176*177* @return a clone of the attribute's DER encoding178*/179public byte[] getEncoded() {180return encoded.clone();181}182183/**184* Compares this {@code PKCS12Attribute} and a specified object for185* equality.186*187* @param obj the comparison object188*189* @return true if {@code obj} is a {@code PKCS12Attribute} and190* their DER encodings are equal.191*/192@Override193public boolean equals(Object obj) {194if (this == obj) {195return true;196}197if (!(obj instanceof PKCS12Attribute)) {198return false;199}200return Arrays.equals(encoded, ((PKCS12Attribute) obj).getEncoded());201}202203/**204* Returns the hashcode for this {@code PKCS12Attribute}.205* The hash code is computed from its DER encoding.206*207* @return the hash code208*/209@Override210public int hashCode() {211if (hashValue == -1) {212Arrays.hashCode(encoded);213}214return hashValue;215}216217/**218* Returns a string representation of this {@code PKCS12Attribute}.219*220* @return a name/value pair separated by an 'equals' symbol221*/222@Override223public String toString() {224return (name + "=" + value);225}226227private byte[] encode(ObjectIdentifier type, String[] values)228throws IOException {229DerOutputStream attribute = new DerOutputStream();230attribute.putOID(type);231DerOutputStream attrContent = new DerOutputStream();232for (String value : values) {233if (COLON_SEPARATED_HEX_PAIRS.matcher(value).matches()) {234byte[] bytes =235new BigInteger(value.replace(":", ""), 16).toByteArray();236if (bytes[0] == 0) {237bytes = Arrays.copyOfRange(bytes, 1, bytes.length);238}239attrContent.putOctetString(bytes);240} else {241attrContent.putUTF8String(value);242}243}244attribute.write(DerValue.tag_Set, attrContent);245DerOutputStream attributeValue = new DerOutputStream();246attributeValue.write(DerValue.tag_Sequence, attribute);247248return attributeValue.toByteArray();249}250251private void parse(byte[] encoded) throws IOException {252DerInputStream attributeValue = new DerInputStream(encoded);253DerValue[] attrSeq = attributeValue.getSequence(2);254if (attrSeq.length != 2) {255throw new IOException("Invalid length for PKCS12Attribute");256}257ObjectIdentifier type = attrSeq[0].getOID();258DerInputStream attrContent =259new DerInputStream(attrSeq[1].toByteArray());260DerValue[] attrValueSet = attrContent.getSet(1);261String[] values = new String[attrValueSet.length];262String printableString;263for (int i = 0; i < attrValueSet.length; i++) {264if (attrValueSet[i].tag == DerValue.tag_OctetString) {265values[i] = Debug.toString(attrValueSet[i].getOctetString());266} else if ((printableString = attrValueSet[i].getAsString())267!= null) {268values[i] = printableString;269} else if (attrValueSet[i].tag == DerValue.tag_ObjectId) {270values[i] = attrValueSet[i].getOID().toString();271} else if (attrValueSet[i].tag == DerValue.tag_GeneralizedTime) {272values[i] = attrValueSet[i].getGeneralizedTime().toString();273} else if (attrValueSet[i].tag == DerValue.tag_UtcTime) {274values[i] = attrValueSet[i].getUTCTime().toString();275} else if (attrValueSet[i].tag == DerValue.tag_Integer) {276values[i] = attrValueSet[i].getBigInteger().toString();277} else if (attrValueSet[i].tag == DerValue.tag_Boolean) {278values[i] = String.valueOf(attrValueSet[i].getBoolean());279} else {280values[i] = Debug.toString(attrValueSet[i].getDataBytes());281}282}283284this.name = type.toString();285this.value = values.length == 1 ? values[0] : Arrays.toString(values);286}287}288289290