@@ -24,6 +24,8 @@ This file is part of the iText (R) project.
24
24
25
25
import com .itextpdf .bouncycastleconnector .BouncyCastleFactoryCreator ;
26
26
import com .itextpdf .commons .bouncycastle .IBouncyCastleFactory ;
27
+ import com .itextpdf .commons .utils .MessageFormatUtil ;
28
+ import com .itextpdf .io .util .EnumUtil ;
27
29
import com .itextpdf .kernel .crypto .OID ;
28
30
29
31
import java .security .cert .X509Certificate ;
@@ -36,14 +38,24 @@ This file is part of the iText (R) project.
36
38
public class KeyUsageExtension extends CertificateExtension {
37
39
38
40
private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator .getFactory ();
41
+ public static final String EXPECTED_VALUE =
42
+ "Key usage expected: ({0})" ;
43
+ public static final String ACTUAL_VALUE = "\n but found {0}" ;
44
+ public static final String MISSING_VALUE = "\n but nothing found." ;
39
45
40
46
private final int keyUsage ;
41
47
private final boolean resultOnMissingExtension ;
48
+ private String messagePreAmble ;
49
+ private String message ;
42
50
43
51
/**
44
52
* Create new {@link KeyUsageExtension} instance using provided {@code int} flag.
45
53
*
46
54
* @param keyUsage {@code int} flag which represents bit values for key usage value
55
+ * bit strings are stored with the big-endian byte order and padding on the end,
56
+ * the big endian notation causes a shift in actual integer values for
57
+ * bits 1-8 becoming 0-7 and bit 1
58
+ * the 7 bits padding makes for bit 0 to become bit 7 of the first byte
47
59
*/
48
60
public KeyUsageExtension (int keyUsage ) {
49
61
this (keyUsage , false );
@@ -52,14 +64,20 @@ public KeyUsageExtension(int keyUsage) {
52
64
/**
53
65
* Create new {@link KeyUsageExtension} instance using provided {@code int} flag.
54
66
*
55
- * @param keyUsage {@code int} flag which represents bit values for key usage value
67
+ * @param keyUsage {@code int} flag which represents bit values for key usage value
68
+ * bit strings are stored with the big-endian byte order and padding on the end,
69
+ * the big endian notation causes a shift in actual integer values for bits 1-8
70
+ * becoming 0-7 and bit 1
71
+ * the 7 bits padding makes for bit 0 to become bit 7 of the first byte
56
72
* @param resultOnMissingExtension parameter which represents return value for
57
73
* {@link #existsInCertificate(X509Certificate)} method in case of the extension not being present in a certificate
58
74
*/
59
75
public KeyUsageExtension (int keyUsage , boolean resultOnMissingExtension ) {
60
76
super (OID .X509Extensions .KEY_USAGE , FACTORY .createKeyUsage (keyUsage ).toASN1Primitive ());
61
77
this .keyUsage = keyUsage ;
62
78
this .resultOnMissingExtension = resultOnMissingExtension ;
79
+ messagePreAmble =MessageFormatUtil .format (EXPECTED_VALUE , convertKeyUsageMaskToString (keyUsage ));
80
+ message = messagePreAmble ;
63
81
}
64
82
65
83
/**
@@ -74,7 +92,7 @@ public KeyUsageExtension(List<KeyUsage> keyUsages) {
74
92
/**
75
93
* Create new {@link KeyUsageExtension} instance using provided key usage enum list.
76
94
*
77
- * @param keyUsages key usages {@link List} which represents key usage values
95
+ * @param keyUsages key usages {@link List} which represents key usage values
78
96
* @param resultOnMissingExtension parameter which represents return value for
79
97
* {@link #existsInCertificate(X509Certificate)} method in case of the extension not being present in a certificate
80
98
*/
@@ -115,44 +133,62 @@ public boolean existsInCertificate(X509Certificate certificate) {
115
133
boolean [] providedKeyUsageFlags = certificate .getKeyUsage ();
116
134
if (providedKeyUsageFlags == null ) {
117
135
// By default, we want to return true if extension is not specified. Configurable.
136
+ message = messagePreAmble + MISSING_VALUE ;
118
137
return resultOnMissingExtension ;
119
138
}
120
- for (int i = 0 ; i < providedKeyUsageFlags .length ; ++i ) {
121
- int power = providedKeyUsageFlags .length - i - 2 ;
122
- if (power < 0 ) {
123
- // Bits are encoded backwards, for the last bit power is -1 and in this case we need to go over byte
124
- power = 16 + power ;
125
- }
126
- if ((keyUsage & (1 << power )) != 0 && !providedKeyUsageFlags [i ]) {
127
- return false ;
139
+ int bitmap = 0 ;
140
+ // bit strings are stored with the big-endian byte order and padding on the end,
141
+ // the big endian notation causes a shift in actual integer values for bits 1-8 becoming 0-7 and bit 1
142
+ // the 7 bits padding makes for bit 0 to become bit 7 of the first byte
143
+ for (int i = 0 ; i < providedKeyUsageFlags .length - 1 ; ++i ) {
144
+ if (providedKeyUsageFlags [i ]) {
145
+ bitmap += 1 << (8 -i -1 );
128
146
}
129
147
}
148
+ if (providedKeyUsageFlags [8 ]) {
149
+ bitmap += 0x8000 ;
150
+ }
151
+ if ((bitmap & keyUsage ) != keyUsage ) {
152
+ message = new StringBuilder (messagePreAmble ).append (
153
+ MessageFormatUtil .format (ACTUAL_VALUE , convertKeyUsageMaskToString (bitmap )))
154
+ .toString ();
155
+ return false ;
156
+ }
130
157
return true ;
131
158
}
159
+ @ Override
160
+ public String getMessage () {
161
+ return message ;
162
+ }
132
163
133
- private static int convertKeyUsageSetToInt (List <KeyUsage > keyUsages ) {
134
- KeyUsage [] possibleKeyUsage = new KeyUsage [] {
135
- KeyUsage .DIGITAL_SIGNATURE ,
136
- KeyUsage .NON_REPUDIATION ,
137
- KeyUsage .KEY_ENCIPHERMENT ,
138
- KeyUsage .DATA_ENCIPHERMENT ,
139
- KeyUsage .KEY_AGREEMENT ,
140
- KeyUsage .KEY_CERT_SIGN ,
141
- KeyUsage .CRL_SIGN ,
142
- KeyUsage .ENCIPHER_ONLY ,
143
- KeyUsage .DECIPHER_ONLY
144
- };
145
- int result = 0 ;
146
- for (int i = 0 ; i < possibleKeyUsage .length ; ++i ) {
147
- if (keyUsages .contains (possibleKeyUsage [i ])) {
148
- int power = possibleKeyUsage .length - i - 2 ;
149
- if (power < 0 ) {
150
- // Bits are encoded backwards, for the last bit power is -1 and in this case we need to go over byte
151
- power = 16 + power ;
152
- }
153
- result |= (1 << power );
164
+ private static String convertKeyUsageMaskToString (int keyUsageMask ) {
165
+ StringBuilder result = new StringBuilder ();
166
+ String separator = "" ;
167
+ // bit strings are stored with the big-endian byte order and padding on the end,
168
+ // the big endian notation causes a shift in actual integer values for bits 1-8 becoming 0-7 and bit 1
169
+ // the 7 bits padding makes for bit 0 to become bit 7 of the first byte
170
+ for (KeyUsage usage : EnumUtil .getAllValuesOfEnum (KeyUsage .class )) {
171
+ if (((1 << (8 -usage .ordinal ()-1 )) & keyUsageMask ) > 0 ||
172
+ (usage == KeyUsage .DECIPHER_ONLY && (keyUsageMask & 0x8000 ) == 0x8000 )) {
173
+ result .append (separator )
174
+ .append (usage );
175
+ separator = ", " ;
176
+ }
177
+ }
178
+ return result .toString ();
179
+ }
180
+ private static int convertKeyUsageSetToInt (Iterable <KeyUsage > keyUsages ) {
181
+ int keyUsageMask = 0 ;
182
+ // bit strings are stored with the big-endian byte order and padding on the end,
183
+ // the big endian notation causes a shift in actual integer values for bits 1-8 becoming 0-7 and bit 1
184
+ // the 7 bits padding makes for bit 0 to become bit 7 of the first byte
185
+ for (KeyUsage usage : keyUsages ) {
186
+ if (usage == KeyUsage .DECIPHER_ONLY ) {
187
+ keyUsageMask += 0x8000 ;
188
+ continue ;
154
189
}
190
+ keyUsageMask += 1 << ( 8 - usage .ordinal () - 1 );
155
191
}
156
- return result ;
192
+ return keyUsageMask ;
157
193
}
158
194
}
0 commit comments