Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/java/security/PKCS12Attribute.java
41152 views
1
/*
2
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package java.security;
27
28
import java.io.IOException;
29
import java.math.BigInteger;
30
import java.util.Arrays;
31
import java.util.regex.Pattern;
32
import sun.security.util.*;
33
34
/**
35
* An attribute associated with a PKCS12 keystore entry.
36
* The attribute name is an ASN.1 Object Identifier and the attribute
37
* value is a set of ASN.1 types.
38
*
39
* @since 1.8
40
*/
41
public final class PKCS12Attribute implements KeyStore.Entry.Attribute {
42
43
private static final Pattern COLON_SEPARATED_HEX_PAIRS =
44
Pattern.compile("^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+$");
45
private String name;
46
private String value;
47
private final byte[] encoded;
48
private int hashValue = -1;
49
50
/**
51
* Constructs a PKCS12 attribute from its name and value.
52
* The name is an ASN.1 Object Identifier represented as a list of
53
* dot-separated integers.
54
* A string value is represented as the string itself.
55
* A binary value is represented as a string of colon-separated
56
* pairs of hexadecimal digits.
57
* Multi-valued attributes are represented as a comma-separated
58
* list of values, enclosed in square brackets. See
59
* {@link Arrays#toString(java.lang.Object[])}.
60
* <p>
61
* A string value will be DER-encoded as an ASN.1 UTF8String and a
62
* binary value will be DER-encoded as an ASN.1 Octet String.
63
*
64
* @param name the attribute's identifier
65
* @param value the attribute's value
66
*
67
* @throws NullPointerException if {@code name} or {@code value}
68
* is {@code null}
69
* @throws IllegalArgumentException if {@code name} or
70
* {@code value} is incorrectly formatted
71
*/
72
public PKCS12Attribute(String name, String value) {
73
if (name == null || value == null) {
74
throw new NullPointerException();
75
}
76
// Validate name
77
ObjectIdentifier type;
78
try {
79
type = ObjectIdentifier.of(name);
80
} catch (IOException e) {
81
throw new IllegalArgumentException("Incorrect format: name", e);
82
}
83
this.name = name;
84
85
// Validate value
86
int length = value.length();
87
String[] values;
88
if (length > 1 &&
89
value.charAt(0) == '[' && value.charAt(length - 1) == ']') {
90
values = value.substring(1, length - 1).split(", ");
91
} else {
92
values = new String[]{ value };
93
}
94
this.value = value;
95
96
try {
97
this.encoded = encode(type, values);
98
} catch (IOException e) {
99
throw new IllegalArgumentException("Incorrect format: value", e);
100
}
101
}
102
103
/**
104
* Constructs a PKCS12 attribute from its ASN.1 DER encoding.
105
* The DER encoding is specified by the following ASN.1 definition:
106
* <pre>
107
*
108
* Attribute ::= SEQUENCE {
109
* type AttributeType,
110
* values SET OF AttributeValue
111
* }
112
* AttributeType ::= OBJECT IDENTIFIER
113
* AttributeValue ::= ANY defined by type
114
*
115
* </pre>
116
*
117
* @param encoded the attribute's ASN.1 DER encoding. It is cloned
118
* to prevent subsequent modification.
119
*
120
* @throws NullPointerException if {@code encoded} is
121
* {@code null}
122
* @throws IllegalArgumentException if {@code encoded} is
123
* incorrectly formatted
124
*/
125
public PKCS12Attribute(byte[] encoded) {
126
if (encoded == null) {
127
throw new NullPointerException();
128
}
129
this.encoded = encoded.clone();
130
131
try {
132
parse(encoded);
133
} catch (IOException e) {
134
throw new IllegalArgumentException("Incorrect format: encoded", e);
135
}
136
}
137
138
/**
139
* Returns the attribute's ASN.1 Object Identifier represented as a
140
* list of dot-separated integers.
141
*
142
* @return the attribute's identifier
143
*/
144
@Override
145
public String getName() {
146
return name;
147
}
148
149
/**
150
* Returns the attribute's ASN.1 DER-encoded value as a string.
151
* An ASN.1 DER-encoded value is returned in one of the following
152
* {@code String} formats:
153
* <ul>
154
* <li> the DER encoding of a basic ASN.1 type that has a natural
155
* string representation is returned as the string itself.
156
* Such types are currently limited to BOOLEAN, INTEGER,
157
* OBJECT IDENTIFIER, UTCTime, GeneralizedTime and the
158
* following six ASN.1 string types: UTF8String,
159
* PrintableString, T61String, IA5String, BMPString and
160
* GeneralString.
161
* <li> the DER encoding of any other ASN.1 type is not decoded but
162
* returned as a binary string of colon-separated pairs of
163
* hexadecimal digits.
164
* </ul>
165
* Multi-valued attributes are represented as a comma-separated
166
* list of values, enclosed in square brackets. See
167
* {@link Arrays#toString(java.lang.Object[])}.
168
*
169
* @return the attribute value's string encoding
170
*/
171
@Override
172
public String getValue() {
173
return value;
174
}
175
176
/**
177
* Returns the attribute's ASN.1 DER encoding.
178
*
179
* @return a clone of the attribute's DER encoding
180
*/
181
public byte[] getEncoded() {
182
return encoded.clone();
183
}
184
185
/**
186
* Compares this {@code PKCS12Attribute} and a specified object for
187
* equality.
188
*
189
* @param obj the comparison object
190
*
191
* @return true if {@code obj} is a {@code PKCS12Attribute} and
192
* their DER encodings are equal.
193
*/
194
@Override
195
public boolean equals(Object obj) {
196
if (this == obj) {
197
return true;
198
}
199
if (!(obj instanceof PKCS12Attribute)) {
200
return false;
201
}
202
return Arrays.equals(encoded, ((PKCS12Attribute) obj).encoded);
203
}
204
205
/**
206
* Returns the hashcode for this {@code PKCS12Attribute}.
207
* The hash code is computed from its DER encoding.
208
*
209
* @return the hash code
210
*/
211
@Override
212
public int hashCode() {
213
int h = hashValue;
214
if (h == -1) {
215
hashValue = h = Arrays.hashCode(encoded);
216
}
217
return h;
218
}
219
220
/**
221
* Returns a string representation of this {@code PKCS12Attribute}.
222
*
223
* @return a name/value pair separated by an 'equals' symbol
224
*/
225
@Override
226
public String toString() {
227
return (name + "=" + value);
228
}
229
230
private byte[] encode(ObjectIdentifier type, String[] values)
231
throws IOException {
232
DerOutputStream attribute = new DerOutputStream();
233
attribute.putOID(type);
234
DerOutputStream attrContent = new DerOutputStream();
235
for (String value : values) {
236
if (COLON_SEPARATED_HEX_PAIRS.matcher(value).matches()) {
237
byte[] bytes =
238
new BigInteger(value.replace(":", ""), 16).toByteArray();
239
if (bytes[0] == 0) {
240
bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
241
}
242
attrContent.putOctetString(bytes);
243
} else {
244
attrContent.putUTF8String(value);
245
}
246
}
247
attribute.write(DerValue.tag_Set, attrContent);
248
DerOutputStream attributeValue = new DerOutputStream();
249
attributeValue.write(DerValue.tag_Sequence, attribute);
250
251
return attributeValue.toByteArray();
252
}
253
254
private void parse(byte[] encoded) throws IOException {
255
DerInputStream attributeValue = new DerInputStream(encoded);
256
DerValue[] attrSeq = attributeValue.getSequence(2);
257
if (attrSeq.length != 2) {
258
throw new IOException("Invalid length for PKCS12Attribute");
259
}
260
ObjectIdentifier type = attrSeq[0].getOID();
261
DerInputStream attrContent =
262
new DerInputStream(attrSeq[1].toByteArray());
263
DerValue[] attrValueSet = attrContent.getSet(1);
264
String[] values = new String[attrValueSet.length];
265
String printableString;
266
for (int i = 0; i < attrValueSet.length; i++) {
267
if (attrValueSet[i].tag == DerValue.tag_OctetString) {
268
values[i] = Debug.toString(attrValueSet[i].getOctetString());
269
} else if ((printableString = attrValueSet[i].getAsString())
270
!= null) {
271
values[i] = printableString;
272
} else if (attrValueSet[i].tag == DerValue.tag_ObjectId) {
273
values[i] = attrValueSet[i].getOID().toString();
274
} else if (attrValueSet[i].tag == DerValue.tag_GeneralizedTime) {
275
values[i] = attrValueSet[i].getGeneralizedTime().toString();
276
} else if (attrValueSet[i].tag == DerValue.tag_UtcTime) {
277
values[i] = attrValueSet[i].getUTCTime().toString();
278
} else if (attrValueSet[i].tag == DerValue.tag_Integer) {
279
values[i] = attrValueSet[i].getBigInteger().toString();
280
} else if (attrValueSet[i].tag == DerValue.tag_Boolean) {
281
values[i] = String.valueOf(attrValueSet[i].getBoolean());
282
} else {
283
values[i] = Debug.toString(attrValueSet[i].getDataBytes());
284
}
285
}
286
287
this.name = type.toString();
288
this.value = values.length == 1 ? values[0] : Arrays.toString(values);
289
}
290
}
291
292