56
56
import java .security .spec .ECPoint ;
57
57
import java .security .spec .ECPublicKeySpec ;
58
58
import java .util .ArrayList ;
59
+ import java .util .Arrays ;
59
60
import java .util .Base64 ;
60
61
import java .util .Collection ;
61
62
@@ -64,17 +65,17 @@ final class XMLENC {
64
65
final static String ALGORITHM = "Algorithm" ;
65
66
66
67
// Returns elements to be added to the CDOC XML, based on recipient type
67
- private static Element toRecipient (Document cdoc , String name , X509Certificate cert , SecretKey dek , boolean includecert ) throws GeneralSecurityException {
68
+ private static Element toRecipient (Document cdoc , CDOC . Version v , String name , X509Certificate cert , SecretKey dek , boolean includecert ) throws GeneralSecurityException {
68
69
if (cert .getPublicKey () instanceof ECPublicKey ) {
69
- return toECRecipient (cdoc , name , cert , dek , includecert );
70
+ return toECRecipient (cdoc , v , name , cert , dek , includecert );
70
71
} else if (cert .getPublicKey () instanceof RSAPublicKey ) {
71
- return toRSARecipient (cdoc , name , cert , dek , includecert );
72
+ return toRSARecipient (cdoc , v , name , cert , dek , includecert );
72
73
} else {
73
74
throw new IllegalArgumentException ("Unknown public key algorithm: " + cert .getPublicKey ().getAlgorithm ());
74
75
}
75
76
}
76
77
77
- private static Element toRSARecipient (Document cdoc , String name , X509Certificate cert , SecretKey dek , boolean includecert ) throws GeneralSecurityException {
78
+ private static Element toRSARecipient (Document cdoc , CDOC . Version v , String name , X509Certificate cert , SecretKey dek , boolean includecert ) throws GeneralSecurityException {
78
79
Element enckey = cdoc .createElement ("xenc:EncryptedKey" );
79
80
enckey .setAttribute ("Recipient" , name );
80
81
@@ -108,14 +109,18 @@ private static Element toRSARecipient(Document cdoc, String name, X509Certificat
108
109
return enckey ;
109
110
}
110
111
111
- private static Element toECRecipient (Document cdoc , String name , X509Certificate cert , SecretKey dek , boolean includecert ) throws GeneralSecurityException {
112
+ private static Element toECRecipient (Document cdoc , CDOC . Version v , String name , X509Certificate cert , SecretKey dek , boolean includecert ) throws GeneralSecurityException {
112
113
ECPublicKey k = (ECPublicKey ) cert .getPublicKey ();
113
114
114
- // Generate temporary key.
115
+ // Generate ephemeral key.
115
116
KeyPairGenerator kpg = KeyPairGenerator .getInstance ("EC" );
116
117
kpg .initialize (k .getParams ());
117
118
KeyPair keyPair = kpg .generateKeyPair ();
118
119
120
+ SubjectPublicKeyInfo partyVkey = SubjectPublicKeyInfo .getInstance (k .getEncoded ());
121
+ SubjectPublicKeyInfo partyUkey = SubjectPublicKeyInfo .getInstance (keyPair .getPublic ().getEncoded ());
122
+
123
+ // Construct XML
119
124
Element enckey = cdoc .createElement ("xenc:EncryptedKey" );
120
125
enckey .setAttribute ("Recipient" , name );
121
126
@@ -132,28 +137,35 @@ private static Element toECRecipient(Document cdoc, String name, X509Certificate
132
137
//
133
138
Element kdm = cdoc .createElement ("xenc11:KeyDerivationMethod" );
134
139
kdm .setAttribute (ALGORITHM , "http://www.w3.org/2009/xmlenc11#ConcatKDF" );
135
- //
136
-
137
- // Get the OID
138
- SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo .getInstance (k .getEncoded ());
139
- SubjectPublicKeyInfo tempKeyInfo = SubjectPublicKeyInfo .getInstance (keyPair .getPublic ().getEncoded ());
140
140
141
- //System.out.println(subPubKeyInfo.getAlgorithm().getParameters());
142
141
143
- //
144
142
// String curveName = "urn:oid:" + ASN1ObjectIdentifier.getInstance(subPubKeyInfo.getAlgorithm().getParameters()).toString();
145
- String curveName = "urn:oid:" + subPubKeyInfo .getAlgorithm ().getParameters ().toString ();
143
+ String curveName = "urn:oid:" + partyVkey .getAlgorithm ().getParameters ().toString ();
146
144
//System.out.println(curveName);
147
145
148
146
// CRITICAL
149
- byte [] algid = "http://www.w3.org/2001/04/xmlenc#kw-aes256" .getBytes (StandardCharsets .US_ASCII );
150
- byte [] uinfo = tempKeyInfo .getPublicKeyData ().getBytes ();
151
- byte [] vinfo = getCN (cert ).getBytes (StandardCharsets .UTF_8 );
147
+ final byte [] algid ;
148
+ final byte [] uinfo ;
149
+ final byte [] vinfo ;
150
+
151
+ // This if assumes that EC is only possible with v11 and v20
152
+ // Formatting conforms to hexBinary with zero padding
153
+ if (v == CDOC .Version .CDOC_V1_1 ) {
154
+ algid = "ENCDOC-XML|1.1" .getBytes (StandardCharsets .US_ASCII );
155
+ uinfo = partyUkey .getPublicKeyData ().getBytes ();
156
+ vinfo = cert .getEncoded ();
157
+ } else if (v == CDOC .Version .CDOC_V2_0 ) {
158
+ algid = "http://www.w3.org/2001/04/xmlenc#kw-aes256" .getBytes (StandardCharsets .US_ASCII );
159
+ uinfo = partyUkey .getPublicKeyData ().getBytes ();
160
+ vinfo = partyVkey .getPublicKeyData ().getBytes ();
161
+ } else {
162
+ throw new IllegalStateException ("Invalid document version for EC recipient" );
163
+ }
152
164
153
165
Element ckdfp = cdoc .createElement ("xenc11:ConcatKDFParams" );
154
- ckdfp .setAttribute ("AlgorithmID" , Hex .toHexString (algid ));
155
- ckdfp .setAttribute ("PartyUInfo" , Hex .toHexString (uinfo ));
156
- ckdfp .setAttribute ("PartyVInfo" , Hex .toHexString (vinfo ));
166
+ ckdfp .setAttribute ("AlgorithmID" , Hex .toHexString (Legacy . concatenate ( new byte []{ 0x00 }, algid ) ));
167
+ ckdfp .setAttribute ("PartyUInfo" , Hex .toHexString (Legacy . concatenate ( new byte []{ 0x00 }, uinfo ) ));
168
+ ckdfp .setAttribute ("PartyVInfo" , Hex .toHexString (Legacy . concatenate ( new byte []{ 0x00 }, vinfo ) ));
157
169
Element dm = cdoc .createElement ("ds:DigestMethod" );
158
170
dm .setAttribute (ALGORITHM , "http://www.w3.org/2001/04/xmlenc#sha256" );
159
171
ckdfp .appendChild (dm );
@@ -167,7 +179,7 @@ private static Element toECRecipient(Document cdoc, String name, X509Certificate
167
179
eckvnc .setAttribute ("URI" , curveName );
168
180
eckv .appendChild (eckvnc );
169
181
Element ecpk = cdoc .createElement ("dsig11:PublicKey" );
170
- ecpk .setTextContent (Base64 .getEncoder ().encodeToString (tempKeyInfo .getPublicKeyData ().getBytes ()));
182
+ ecpk .setTextContent (Base64 .getEncoder ().encodeToString (partyUkey .getPublicKeyData ().getBytes ()));
171
183
eckv .appendChild (ecpk );
172
184
173
185
kv .appendChild (eckv );
@@ -192,14 +204,14 @@ private static Element toECRecipient(Document cdoc, String name, X509Certificate
192
204
KeyAgreement key_agreement = KeyAgreement .getInstance ("ECDH" );
193
205
key_agreement .init (keyPair .getPrivate ());
194
206
key_agreement .doPhase (cert .getPublicKey (), true );
207
+
195
208
// Use the shared secret to wrap the actual key
196
209
byte [] shared_secret = key_agreement .generateSecret ();
197
210
198
211
// Derive key wrap key with ckdf
199
212
ConcatenationKDFGenerator ckdf = new ConcatenationKDFGenerator (new SHA384Digest ()); // FIXME: parametrize
200
213
ckdf .init (new KDFParameters (shared_secret , Legacy .concatenate (algid , uinfo , vinfo )));
201
214
byte [] wrapkeybytes = new byte [32 ];
202
-
203
215
ckdf .generateBytes (wrapkeybytes , 0 , 32 );
204
216
205
217
SecretKeySpec wrapKey = new SecretKeySpec (wrapkeybytes , "AES" );
@@ -253,7 +265,7 @@ static Document makeRecipientsXML(CDOC.Version v, Collection<X509Certificate> re
253
265
} else {
254
266
// One for every recipient, dependent on algorithm
255
267
for (X509Certificate crt : recipients ) {
256
- Element enckey = toRecipient (cdoc , privacy ? "Undisclosed" : getCN (crt ), crt , dek , privacy );
268
+ Element enckey = toRecipient (cdoc , v , privacy ? "Undisclosed" : getCN (crt ), crt , dek , privacy );
257
269
keyinfo .appendChild (enckey );
258
270
}
259
271
}
@@ -284,6 +296,13 @@ static ArrayList<Recipient> parseRecipientsXML(Document d) throws IOException, C
284
296
byte a [] = Hex .decode (params .getAttributes ().getNamedItem ("AlgorithmID" ).getTextContent ());
285
297
byte u [] = Hex .decode (params .getAttributes ().getNamedItem ("PartyUInfo" ).getTextContent ());
286
298
byte v [] = Hex .decode (params .getAttributes ().getNamedItem ("PartyVInfo" ).getTextContent ());
299
+ // Support only full octets
300
+ if (a [0 ] != 0 || u [0 ] != 0 || v [0 ] != 0 ) {
301
+ throw new IOException ("Only full octets supported for AlgorithmID, PartyUInfo and PartyVInfo" );
302
+ }
303
+ a = Arrays .copyOfRange (a , 1 , a .length );
304
+ u = Arrays .copyOfRange (u , 1 , u .length );
305
+ v = Arrays .copyOfRange (v , 1 , v .length );
287
306
288
307
String kdf = params .getParentNode ().getAttributes ().getNamedItem ("Algorithm" ).getTextContent ();
289
308
if (!kdf .equals ("http://www.w3.org/2009/xmlenc11#ConcatKDF" ))
@@ -313,7 +332,7 @@ static ArrayList<Recipient> parseRecipientsXML(Document d) throws IOException, C
313
332
Recipient .ECDHESRecipient r = new Recipient .ECDHESRecipient (cert , name , pk , cgram , a , u , v );
314
333
result .add (r );
315
334
} else {
316
- throw new RuntimeException ("Unknown key encryption algorithm: " + algorithm );
335
+ throw new IOException ("Unknown key encryption algorithm: " + algorithm );
317
336
}
318
337
}
319
338
} catch (XPathExpressionException | GeneralSecurityException | NullPointerException e ) {
0 commit comments