88import java .io .IOException ;
99import java .io .InputStream ;
1010import java .io .OutputStream ;
11+ import java .io .ByteArrayInputStream ;
12+ import java .io .ByteArrayOutputStream ;
1113import java .security .DigestException ;
1214import java .security .InvalidAlgorithmParameterException ;
1315import java .security .InvalidKeyException ;
2224import org .mozilla .jss .asn1 .OCTET_STRING ;
2325import org .mozilla .jss .asn1 .SEQUENCE ;
2426import org .mozilla .jss .asn1 .Tag ;
27+ import org .mozilla .jss .asn1 .OBJECT_IDENTIFIER ;
28+ import org .mozilla .jss .asn1 .ANY ;
2529import org .mozilla .jss .crypto .CryptoToken ;
2630import org .mozilla .jss .crypto .DigestAlgorithm ;
2731import org .mozilla .jss .crypto .HMACAlgorithm ;
3236import org .mozilla .jss .crypto .PBEKeyGenParams ;
3337import org .mozilla .jss .crypto .SymmetricKey ;
3438import org .mozilla .jss .crypto .TokenException ;
39+ import org .mozilla .jss .crypto .PBEAlgorithm ;
40+ import org .mozilla .jss .pkix .primitive .PBMAC1Params ;
3541import org .mozilla .jss .pkcs7 .DigestInfo ;
3642import org .mozilla .jss .pkix .primitive .AlgorithmIdentifier ;
43+ import org .mozilla .jss .pkix .primitive .PBKDF2Params ;
3744import org .mozilla .jss .util .Password ;
3845
3946public class MacData implements ASN1Value {
@@ -137,13 +144,28 @@ public MacData( Password password, byte[] macSalt,
137144 rand .nextBytes (macSalt );
138145 }
139146
140- PBEKeyGenParams params = new PBEKeyGenParams ( password , macSalt , iterations );
147+ // Handle null algID - default to SHA1 for backward compatibility
141148
142149 try {
143- // generate key from password and salt
144- if (algID == null ) {
150+ if (algID == null ) {
145151 algID = new AlgorithmIdentifier (DigestAlgorithm .SHA1 .toOID ());
146152 }
153+
154+ // Check if this is PBMAC1 - route to new implementation
155+ if (algID .getOID ().equals (PBEAlgorithm .PBE_PKCS5_PBMAC1 .toOID ())) {
156+ computePBMAC1 (token ,password , macSalt , iterations , toBeMACed , algID );
157+ return ; // Early return - skip legacy code below
158+ }
159+ } catch (NoSuchAlgorithmException e ) {
160+ throw new TokenException ("Algorithm OID error: " + e .getMessage (), e );
161+ } catch (Exception e ) {
162+ throw new TokenException ("Failed to compute PBMAC1: " + e .getMessage (), e );
163+ }
164+
165+ PBEKeyGenParams params = new PBEKeyGenParams (password , macSalt , iterations );
166+
167+ try {
168+ // generate key from password and salt
147169 KeyGenerator kg = null ;
148170 JSSMessageDigest digest = null ;
149171 if (DigestAlgorithm .SHA1 .toOID ().equals (algID .getOID ())){
@@ -176,7 +198,7 @@ public MacData( Password password, byte[] macSalt,
176198 // put everything into a DigestInfo
177199 this .mac = new DigestInfo (algID , new OCTET_STRING (digestBytes ));
178200 this .macSalt = new OCTET_STRING (macSalt );
179- this .macIterationCount = new INTEGER (iterations );
201+ this .macIterationCount = new INTEGER (1 );
180202
181203 } catch (NoSuchAlgorithmException e ) {
182204 throw new RuntimeException ("HMAC algorithm not found on internal " +
@@ -196,6 +218,112 @@ public MacData( Password password, byte[] macSalt,
196218 }
197219 }
198220
221+ private void computePBMAC1 (CryptoToken token , Password password , byte [] salt , int iterations ,
222+ byte [] data , AlgorithmIdentifier algID )
223+ throws Exception
224+ {
225+ // Parse PBMAC1 parameters to extract KDF and MAC algorithms
226+
227+ PBMAC1Params pbmac1Params ;
228+ ASN1Value params = algID .getParameters ();
229+
230+ if (params instanceof PBMAC1Params ) {
231+ // Already decoded (create/write path)
232+ pbmac1Params = (PBMAC1Params ) params ;
233+ } else if (params instanceof ANY ) {
234+ // Needs decoding (read from file path)
235+ pbmac1Params = (PBMAC1Params ) ((ANY ) params ).decodeWith (PBMAC1Params .getTemplate ());
236+ } else {
237+ throw new Exception ("Unexpected PBMAC1 parameter type: " + params .getClass ().getName ());
238+ }
239+
240+ AlgorithmIdentifier kdfAlg = pbmac1Params .getKeyDerivationFunc ();
241+ AlgorithmIdentifier macAlg = pbmac1Params .getMessageAuthScheme ();
242+
243+ ASN1Value kdfParams = kdfAlg .getParameters ();
244+
245+ //Extract PBKDF2 parameters
246+ PBKDF2Params pbkdf2Params ;
247+
248+ if (kdfParams instanceof SEQUENCE ) {
249+ // Need to decode SEQUENCE bytes to PBKDF2Params
250+ ByteArrayOutputStream bos = new ByteArrayOutputStream ();
251+ kdfParams .encode (bos );
252+ pbkdf2Params = (PBKDF2Params ) PBKDF2Params .getTemplate ().decode (
253+ new ByteArrayInputStream (bos .toByteArray ()));
254+
255+ } else if (kdfParams instanceof ANY ) {
256+ // Create template that knows PBKDF2 structure
257+ pbkdf2Params = (PBKDF2Params ) ((ANY ) kdfParams ).decodeWith (PBKDF2Params .getTemplate ());
258+ } else {
259+ throw new Exception ("Unexpected PBKDF2 parameter type: " + kdfParams .getClass ().getName ());
260+ }
261+
262+ byte [] kdfSalt = pbkdf2Params .getSalt ();
263+ int kdfIterations = pbkdf2Params .getIterations ();
264+
265+ // Get HMAC OID from MAC AlgorithmIdentifier
266+ OBJECT_IDENTIFIER macOID = macAlg .getOID ();
267+
268+ HMACAlgorithm hmacAlgorithm ;
269+ if (macOID .equals (HMACAlgorithm .SHA256 .toOID ())) {
270+ hmacAlgorithm = HMACAlgorithm .SHA256 ;
271+ } else if (macOID .equals (HMACAlgorithm .SHA384 .toOID ())) {
272+ hmacAlgorithm = HMACAlgorithm .SHA384 ;
273+ } else if (macOID .equals (HMACAlgorithm .SHA512 .toOID ())) {
274+ hmacAlgorithm = HMACAlgorithm .SHA512 ;
275+ } else {
276+ throw new NoSuchAlgorithmException ("Unsupported HMAC algorithm for PBMAC1: " + macOID .toString ());
277+ }
278+
279+ // Call native JSS code to perform PBKDF2 + HMAC
280+ // This uses certified NSS crypto (PK11_PBEKeyGen + PK11_DigestOp)
281+ // The OID will be mapped to the appropriate NSS HMAC mechanism
282+
283+
284+ char [] passwordChars = password .getCharCopy ();
285+ byte [] passwordBytes = Password .charToByte (passwordChars );
286+
287+ try {
288+ byte [] macValue = nativeComputePBMAC1 (
289+ token ,
290+ passwordBytes ,
291+ kdfSalt ,
292+ kdfIterations ,
293+ data ,
294+ hmacAlgorithm
295+ );
296+
297+ this .mac = new DigestInfo (algID , new OCTET_STRING (macValue ));
298+ this .macSalt = new OCTET_STRING (kdfSalt );
299+ this .macIterationCount = new INTEGER (kdfIterations );
300+ } finally {
301+ Password .wipeBytes (passwordBytes );
302+ if (passwordChars != null ) {
303+ Password .wipeChars (passwordChars );
304+ }
305+ }
306+ }
307+
308+ /**
309+ * Native method to compute PBMAC1 MAC using NSS.
310+ *
311+ * @param password Password bytes
312+ * @param salt PBKDF2 salt
313+ * @param iterations PBKDF2 iteration count
314+ * @param data Data to MAC
315+ * @param hmacOID HMAC algorithm OID (e.g., hmacWithSHA256)
316+ * @return HMAC value
317+ */
318+ private native byte [] nativeComputePBMAC1 (
319+ CryptoToken token ,
320+ byte [] password ,
321+ byte [] salt ,
322+ int iterations ,
323+ byte [] data ,
324+ HMACAlgorithm hmacAlgorithm
325+ ) throws Exception ;
326+
199327 ///////////////////////////////////////////////////////////////////////
200328 // DER encoding
201329 ///////////////////////////////////////////////////////////////////////
0 commit comments