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,115 @@ 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+ ByteArrayOutputStream bos = new ByteArrayOutputStream ();
241+
242+ algID .encode (bos );
243+ byte [] pbmac1AlgIDBytes = bos .toByteArray ();
244+
245+ AlgorithmIdentifier kdfAlg = pbmac1Params .getKeyDerivationFunc ();
246+ AlgorithmIdentifier macAlg = pbmac1Params .getMessageAuthScheme ();
247+
248+ ASN1Value kdfParams = kdfAlg .getParameters ();
249+
250+ //Extract PBKDF2 parameters
251+ PBKDF2Params pbkdf2Params ;
252+
253+ if (kdfParams instanceof SEQUENCE ) {
254+ // Need to decode SEQUENCE bytes to PBKDF2Params
255+ ByteArrayOutputStream bosForDecode = new ByteArrayOutputStream ();
256+ kdfParams .encode (bosForDecode );
257+ pbkdf2Params = (PBKDF2Params ) PBKDF2Params .getTemplate ().decode (
258+ new ByteArrayInputStream (bosForDecode .toByteArray ()));
259+
260+ } else if (kdfParams instanceof ANY ) {
261+ // Create template that knows PBKDF2 structure
262+ pbkdf2Params = (PBKDF2Params ) ((ANY ) kdfParams ).decodeWith (PBKDF2Params .getTemplate ());
263+ } else {
264+ throw new Exception ("Unexpected PBKDF2 parameter type: " + kdfParams .getClass ().getName ());
265+ }
266+
267+ byte [] kdfSalt = pbkdf2Params .getSalt ();
268+ int kdfIterations = pbkdf2Params .getIterations ();
269+
270+ // Get HMAC OID from MAC AlgorithmIdentifier
271+ OBJECT_IDENTIFIER macOID = macAlg .getOID ();
272+
273+ HMACAlgorithm hmacAlgorithm ;
274+ if (macOID .equals (HMACAlgorithm .SHA256 .toOID ())) {
275+ hmacAlgorithm = HMACAlgorithm .SHA256 ;
276+ } else if (macOID .equals (HMACAlgorithm .SHA384 .toOID ())) {
277+ hmacAlgorithm = HMACAlgorithm .SHA384 ;
278+ } else if (macOID .equals (HMACAlgorithm .SHA512 .toOID ())) {
279+ hmacAlgorithm = HMACAlgorithm .SHA512 ;
280+ } else {
281+ throw new NoSuchAlgorithmException ("Unsupported HMAC algorithm for PBMAC1: " + macOID .toString ());
282+ }
283+
284+ // Call native JSS code to perform PBKDF2 + HMAC
285+ // This uses certified NSS crypto (PK11_PBEKeyGen + PK11_DigestOp)
286+ // The OID will be mapped to the appropriate NSS HMAC mechanism
287+
288+
289+ char [] passwordChars = password .getCharCopy ();
290+ byte [] passwordBytes = Password .charToByte (passwordChars );
291+
292+ try {
293+ byte [] macValue = nativeComputePBMAC1 (
294+ token ,
295+ passwordBytes ,
296+ data ,
297+ pbmac1AlgIDBytes ,
298+ hmacAlgorithm
299+ );
300+
301+ this .mac = new DigestInfo (algID , new OCTET_STRING (macValue ));
302+ this .macSalt = new OCTET_STRING (kdfSalt );
303+ this .macIterationCount = new INTEGER (kdfIterations );
304+ } finally {
305+ Password .wipeBytes (passwordBytes );
306+ if (passwordChars != null ) {
307+ Password .wipeChars (passwordChars );
308+ }
309+ }
310+ }
311+
312+ /**
313+ * Native method to compute PBMAC1 MAC using NSS.
314+ *
315+ * @param password Password bytes
316+ * @param salt PBKDF2 salt
317+ * @param iterations PBKDF2 iteration count
318+ * @param data Data to MAC
319+ * @param hmacOID HMAC algorithm OID (e.g., hmacWithSHA256)
320+ * @return HMAC value
321+ */
322+ private native byte [] nativeComputePBMAC1 (
323+ CryptoToken token ,
324+ byte [] password ,
325+ byte [] data ,
326+ byte [] pbmac1AlgID ,
327+ HMACAlgorithm hmacAlgorithm
328+ ) throws Exception ;
329+
199330 ///////////////////////////////////////////////////////////////////////
200331 // DER encoding
201332 ///////////////////////////////////////////////////////////////////////
0 commit comments