Java.security.unrecoverablekeyexception failed pkcs12 integrity checking

Hello,thanks for viewing.

I set up a centos VM to host a tomcat server.

Then I bought the App Service Certificate, and clear the Certificate Status to Issued.

But when I download the certificate(.pfx file),and set it to my tomcat.Tomcat gets error log

"java.security.UnrecoverableKeyException: Failed PKCS12 integrity checking"

My tomcat server.xml looks like this.

<style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000; background-color: #ffffff} span.s1 {font-variant-ligatures: no-common-ligatures} </style>

<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true"   maxThreads="150" scheme="https" secure="true"  clientAuth="false" sslProtocol="TLS"

keystoreFile="/usr/xx-xx-xx-xx-xx-x-xxxxx-20200118.pfx"

keystoreType="PKCS12"

keystorePass="xxxx.verylongpwd"

<style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000; background-color: #ffffff} span.s1 {font-variant-ligatures: no-common-ligatures} </style>

truststoreType="PKCS12"

truststoreFile="/usr/xx-xx-xx-xx-xx-x-xxxxx-20200118.pfx"

truststorePass="xxxx.verylongpwd"

/>

Any hint to solve is very much appreciated.

<style type="text/css">p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000; background-color: #ffffff} span.s1 {font-variant-ligatures: no-common-ligatures} </style>

/* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.pkcs12; import java.io.*; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Key; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.KeyStoreSpi; import java.security.KeyStoreException; import java.security.UnrecoverableKeyException; import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; import java.math.*; import java.security.AlgorithmParameters; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import javax.crypto.SecretKeyFactory; import javax.crypto.SecretKey; import javax.crypto.Cipher; import javax.crypto.Mac; import javax.security.auth.x500.X500Principal; import sun.security.util.DerInputStream; import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.ObjectIdentifier; import sun.security.pkcs.ContentInfo; import sun.security.x509.AlgorithmId; import sun.security.pkcs.EncryptedPrivateKeyInfo; /** * This class provides the keystore implementation referred to as "PKCS12". * Implements the PKCS#12 PFX protected using the Password privacy mode. * The contents are protected using Password integrity mode. * * Currently we support following PBE algorithms: * - pbeWithSHAAnd3KeyTripleDESCBC to encrypt private keys * - pbeWithSHAAnd40BitRC2CBC to encrypt certificates * * Supported encryption of various implementations : * * Software and mode. Certificate encryption Private key encryption * --------------------------------------------------------------------- * MSIE4 (domestic 40 bit RC2. 40 bit RC2 * and xport versions) * PKCS#12 export. * * MSIE4, 5 (domestic 40 bit RC2, 40 bit RC2, * and export versions) 3 key triple DES 3 key triple DES * PKCS#12 import. * * MSIE5 40 bit RC2 3 key triple DES, * PKCS#12 export. with SHA1 (168 bits) * * Netscape Communicator 40 bit RC2 3 key triple DES, * (domestic and export with SHA1 (168 bits) * versions) PKCS#12 export * * Netscape Communicator 40 bit ciphers only All. * (export version) * PKCS#12 import. * * Netscape Communicator All. All. * (domestic or fortified * version) PKCS#12 import. * * OpenSSL PKCS#12 code. All. All. * --------------------------------------------------------------------- * * NOTE: Currently PKCS12 KeyStore does not support TrustedCertEntries. * PKCS#12 is mainly used to deliver private keys with their associated * certificate chain and aliases. In a PKCS12 keystore, entries are * identified by the alias, and a localKeyId is required to match the * private key with the certificate. * * @author Seema Malkani * @author Jeff Nisewanger * @author Jan Luehe * * @see KeyProtector * @see java.security.KeyStoreSpi * @see KeyTool * * */ publicfinalclass PKCS12KeyStore extendsKeyStoreSpi{ publicstaticfinalint VERSION_3 =3; privatestaticfinalint keyBag[]={1,2,840,113549,1,12, 10,1,2}; privatestaticfinalint certBag[]={1,2,840,113549,1,12, 10,1,3}; privatestaticfinalint pkcs9Name[]={1,2,840,113549,1,9, 20}; privatestaticfinalint pkcs9KeyId[]={1,2,840,113549,1,9, 21}; privatestaticfinalint pkcs9certType[]={1,2,840,113549,1,9, 22,1}; privatestaticfinalint pbeWithSHAAnd40BitRC2CBC[]= {1,2,840,113549,1,12,1,6}; privatestaticfinalint pbeWithSHAAnd3KeyTripleDESCBC[]= {1,2,840,113549,1,12,1,3}; privatestaticObjectIdentifier PKCS8ShroudedKeyBag_OID; privatestaticObjectIdentifierCertBag_OID; privatestaticObjectIdentifier PKCS9FriendlyName_OID; privatestaticObjectIdentifier PKCS9LocalKeyId_OID; privatestaticObjectIdentifier PKCS9CertType_OID; privatestaticObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID; privatestaticObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID; privateint counter =0; privatestaticfinalint iterationCount =1024; privatestaticfinalint SALT_LEN =20; // private key count // Note: This is a workaround to allow null localKeyID attribute // in pkcs12 with one private key entry and associated cert-chain privateint privateKeyCount =0; // the source of randomness privateSecureRandom random; static{ try{ PKCS8ShroudedKeyBag_OID =newObjectIdentifier(keyBag); CertBag_OID=newObjectIdentifier(certBag); PKCS9FriendlyName_OID =newObjectIdentifier(pkcs9Name); PKCS9LocalKeyId_OID =newObjectIdentifier(pkcs9KeyId); PKCS9CertType_OID =newObjectIdentifier(pkcs9certType); pbeWithSHAAnd40BitRC2CBC_OID = newObjectIdentifier(pbeWithSHAAnd40BitRC2CBC); pbeWithSHAAnd3KeyTripleDESCBC_OID = newObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC); }catch(IOException ioe){ // should not happen } } // Private keys and their supporting certificate chains privatestaticclassKeyEntry{ Date date;// the creation date of this entry byte[] protectedPrivKey; Certificate chain[]; byte[] keyId; String alias; }; // A certificate with its PKCS #9 attributes privatestaticclassCertEntry{ final X509Certificate cert; finalbyte[] keyId; finalString alias; CertEntry(X509Certificate cert,byte[] keyId,String alias){ this.cert = cert; this.keyId = keyId; this.alias = alias; } } /** * Private keys and certificates are stored in a hashtable. * Hash entries are keyed by alias names. */ privateHashtable<String,KeyEntry> entries = newHashtable<String,KeyEntry>(); privateArrayList<KeyEntry> keyList =newArrayList<KeyEntry>(); privateLinkedHashMap<X500Principal, X509Certificate> certsMap = newLinkedHashMap<X500Principal, X509Certificate>(); privateArrayList<CertEntry> certEntries =newArrayList<CertEntry>(); /** * Returns the key associated with the given alias, using the given * password to recover it. * * @param alias the alias name * @param password the password for recovering the key * * @return the requested key, or null if the given alias does not exist * or does not identify a <i>key entry</i>. * * @exception NoSuchAlgorithmException if the algorithm for recovering the * key cannot be found * @exception UnrecoverableKeyException if the key cannot be recovered * (e.g., the given password is wrong). */ publicKey engineGetKey(String alias,char[] password) throwsNoSuchAlgorithmException,UnrecoverableKeyException { KeyEntry entry = entries.get(alias.toLowerCase()); Key key =null; if(entry ==null){ returnnull; } // get the encoded private key byte[] encrBytes = entry.protectedPrivKey; byte[] encryptedKey; AlgorithmParameters algParams; ObjectIdentifier algOid; try{ // get the encrypted private key EncryptedPrivateKeyInfo encrInfo = newEncryptedPrivateKeyInfo(encrBytes); encryptedKey = encrInfo.getEncryptedData(); // parse Algorithm parameters DerValue val =newDerValue(encrInfo.getAlgorithm().encode()); DerInputStream in = val.toDerInputStream(); algOid = in.getOID(); algParams = parseAlgParameters(in); }catch(IOException ioe){ UnrecoverableKeyException uke = newUnrecoverableKeyException("Private key not stored as " +"PKCS#8 EncryptedPrivateKeyInfo: "+ ioe); uke.initCause(ioe); throw uke; } try{ byte[] privateKeyInfo; while(true){ try{ // Use JCE SecretKey skey = getPBEKey(password); Cipher cipher =Cipher.getInstance(algOid.toString()); cipher.init(Cipher.DECRYPT_MODE, skey, algParams); privateKeyInfo = cipher.doFinal(encryptedKey); break; }catch(Exception e){ if(password.length ==0){ // Retry using an empty password // without a NULL terminator. password =newchar[1]; continue; } throw e; } } PKCS8EncodedKeySpec kspec =new PKCS8EncodedKeySpec(privateKeyInfo); /* * Parse the key algorithm and then use a JCA key factory * to create the private key. */ DerValue val =newDerValue(privateKeyInfo); DerInputStream in = val.toDerInputStream(); int i = in.getInteger(); DerValue[] value = in.getSequence(2); AlgorithmId algId =newAlgorithmId(value[0].getOID()); String algName = algId.getName(); KeyFactory kfac =KeyFactory.getInstance(algName); key = kfac.generatePrivate(kspec); }catch(Exception e){ UnrecoverableKeyException uke = newUnrecoverableKeyException("Get Key failed: "+ e.getMessage()); uke.initCause(e); throw uke; } return key; } /** * Returns the certificate chain associated with the given alias. * * @param alias the alias name * * @return the certificate chain (ordered with the user's certificate first * and the root certificate authority last), or null if the given alias * does not exist or does not contain a certificate chain (i.e., the given * alias identifies either a <i>trusted certificate entry</i> or a * <i>key entry</i> without a certificate chain). */ publicCertificate[] engineGetCertificateChain(String alias){ KeyEntry entry = entries.get(alias.toLowerCase()); if(entry !=null){ if(entry.chain ==null){ returnnull; }else{ return entry.chain.clone(); } }else{ returnnull; } } /** * Returns the certificate associated with the given alias. * * <p>If the given alias name identifies a * <i>trusted certificate entry</i>, the certificate associated with that * entry is returned. If the given alias name identifies a * <i>key entry</i>, the first element of the certificate chain of that * entry is returned, or null if that entry does not have a certificate * chain. * * @param alias the alias name * * @return the certificate, or null if the given alias does not exist or * does not contain a certificate. */ publicCertificate engineGetCertificate(String alias){ KeyEntry entry = entries.get(alias.toLowerCase()); if(entry !=null){ if(entry.chain ==null){ returnnull; }else{ return entry.chain[0]; } }else{ returnnull; } } /** * Returns the creation date of the entry identified by the given alias. * * @param alias the alias name * * @return the creation date of this entry, or null if the given alias does * not exist */ publicDate engineGetCreationDate(String alias){ KeyEntry entry = entries.get(alias.toLowerCase()); if(entry !=null){ returnnewDate(entry.date.getTime()); }else{ returnnull; } } /** * Assigns the given key to the given alias, protecting it with the given * password. * * <p>If the given key is of type <code>java.security.PrivateKey</code>, * it must be accompanied by a certificate chain certifying the * corresponding public key. * * <p>If the given alias already exists, the keystore information * associated with it is overridden by the given key (and possibly * certificate chain). * * @param alias the alias name * @param key the key to be associated with the alias * @param password the password to protect the key * @param chain the certificate chain for the corresponding public * key (only required if the given key is of type * <code>java.security.PrivateKey</code>). * * @exception KeyStoreException if the given key cannot be protected, or * this operation fails for some other reason */ publicsynchronizedvoid engineSetKeyEntry(String alias,Key key, char[] password,Certificate[] chain) throwsKeyStoreException { try{ KeyEntry entry =newKeyEntry(); entry.date =newDate(); if(key instanceofPrivateKey){ if((key.getFormat().equals("PKCS#8"))|| (key.getFormat().equals("PKCS8"))){ // Encrypt the private key entry.protectedPrivKey = encryptPrivateKey(key.getEncoded(), password); }else{ thrownewKeyStoreException("Private key is not encoded"+ "as PKCS#8"); } }else{ thrownewKeyStoreException("Key is not a PrivateKey"); } // clone the chain if(chain !=null){ // validate cert-chain if((chain.length >1)&&(!validateChain(chain))) thrownewKeyStoreException("Certificate chain is "+ "not validate"); entry.chain = chain.clone(); } // set the keyId to current date entry.keyId =("Time "+(entry.date).getTime()).getBytes("UTF8"); // set the alias entry.alias = alias.toLowerCase(); // add the entry entries.put(alias.toLowerCase(), entry); }catch(Exception nsae){ KeyStoreException ke =newKeyStoreException("Key protection "+ " algorithm not found: "+ nsae); ke.initCause(nsae); throw ke; } } /** * Assigns the given key (that has already been protected) to the given * alias. * * <p>If the protected key is of type * <code>java.security.PrivateKey</code>, it must be accompanied by a * certificate chain certifying the corresponding public key. If the * underlying keystore implementation is of type <code>jks</code>, * <code>key</code> must be encoded as an * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard. * * <p>If the given alias already exists, the keystore information * associated with it is overridden by the given key (and possibly * certificate chain). * * @param alias the alias name * @param key the key (in protected format) to be associated with the alias * @param chain the certificate chain for the corresponding public * key (only useful if the protected key is of type * <code>java.security.PrivateKey</code>). * * @exception KeyStoreException if this operation fails. */ publicsynchronizedvoid engineSetKeyEntry(String alias,byte[] key, Certificate[] chain) throwsKeyStoreException { // key must be encoded as EncryptedPrivateKeyInfo // as defined in PKCS#8 try{ newEncryptedPrivateKeyInfo(key); }catch(IOException ioe){ KeyStoreException ke =newKeyStoreException("Private key is not" +" stored as PKCS#8 EncryptedPrivateKeyInfo: "+ ioe); ke.initCause(ioe); throw ke; } KeyEntry entry =newKeyEntry(); entry.date =newDate(); try{ // set the keyId to current date entry.keyId =("Time "+(entry.date).getTime()).getBytes("UTF8"); }catch(UnsupportedEncodingException ex){ // Won't happen } // set the alias entry.alias = alias.toLowerCase(); entry.protectedPrivKey = key.clone(); if(chain !=null){ entry.chain = chain.clone(); } // add the entry entries.put(alias.toLowerCase(), entry); } /* * Generate random salt */ privatebyte[] getSalt() { // Generate a random salt. byte[] salt =newbyte[SALT_LEN]; if(random ==null){ random =newSecureRandom(); } random.nextBytes(salt); return salt; } /* * Generate PBE Algorithm Parameters */ privateAlgorithmParameters getAlgorithmParameters(String algorithm) throwsIOException { AlgorithmParameters algParams =null; // create PBE parameters from salt and iteration count PBEParameterSpec paramSpec = newPBEParameterSpec(getSalt(), iterationCount); try{ algParams =AlgorithmParameters.getInstance(algorithm); algParams.init(paramSpec); }catch(Exception e){ IOException ioe = newIOException("getAlgorithmParameters failed: "+ e.getMessage()); ioe.initCause(e); throw ioe; } return algParams; } /* * parse Algorithm Parameters */ privateAlgorithmParameters parseAlgParameters(DerInputStream in) throwsIOException { AlgorithmParameters algParams =null; try{ DerValue params; if(in.available()==0){ params =null; }else{ params = in.getDerValue(); if(params.tag ==DerValue.tag_Null){ params =null; } } if(params !=null){ algParams =AlgorithmParameters.getInstance("PBE"); algParams.init(params.toByteArray()); } }catch(Exception e){ IOException ioe = newIOException("parseAlgParameters failed: "+ e.getMessage()); ioe.initCause(e); throw ioe; } return algParams; } /* * Generate PBE key */ privateSecretKey getPBEKey(char[] password)throwsIOException { SecretKey skey =null; try{ PBEKeySpec keySpec =newPBEKeySpec(password); SecretKeyFactory skFac =SecretKeyFactory.getInstance("PBE"); skey = skFac.generateSecret(keySpec); }catch(Exception e){ IOException ioe =newIOException("getSecretKey failed: "+ e.getMessage()); ioe.initCause(e); throw ioe; } return skey; } /* * Encrypt private key using Password-based encryption (PBE) * as defined in PKCS#5. * * NOTE: Currently pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is * used to derive the key and IV. * * @return encrypted private key encoded as EncryptedPrivateKeyInfo */ privatebyte[] encryptPrivateKey(byte[] data,char[] password) throwsIOException,NoSuchAlgorithmException,UnrecoverableKeyException { byte[] key =null; try{ // create AlgorithmParameters AlgorithmParameters algParams = getAlgorithmParameters("PBEWithSHA1AndDESede"); // Use JCE SecretKey skey = getPBEKey(password); Cipher cipher =Cipher.getInstance("PBEWithSHA1AndDESede"); cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); byte[] encryptedKey = cipher.doFinal(data); // wrap encrypted private key in EncryptedPrivateKeyInfo // as defined in PKCS#8 AlgorithmId algid = newAlgorithmId(pbeWithSHAAnd3KeyTripleDESCBC_OID, algParams); EncryptedPrivateKeyInfo encrInfo = newEncryptedPrivateKeyInfo(algid, encryptedKey); key = encrInfo.getEncoded(); }catch(Exception e){ UnrecoverableKeyException uke = newUnrecoverableKeyException("Encrypt Private Key failed: " + e.getMessage()); uke.initCause(e); throw uke; } return key; } /** * Assigns the given certificate to the given alias. * * <p>If the given alias already exists in this keystore and identifies a * <i>trusted certificate entry</i>, the certificate associated with it is * overridden by the given certificate. * * @param alias the alias name * @param cert the certificate * * @exception KeyStoreException if the given alias already exists and does * identify a <i>key entry</i>, or on an attempt to create a * <i>trusted cert entry</i> which is currently not supported. */ publicsynchronizedvoid engineSetCertificateEntry(String alias, Certificate cert)throwsKeyStoreException { KeyEntry entry = entries.get(alias.toLowerCase()); if(entry !=null){ thrownewKeyStoreException("Cannot overwrite own certificate"); }else thrownewKeyStoreException("TrustedCertEntry not supported"); } /** * Deletes the entry identified by the given alias from this keystore. * * @param alias the alias name * * @exception KeyStoreException if the entry cannot be removed. */ publicsynchronizedvoid engineDeleteEntry(String alias) throwsKeyStoreException { entries.remove(alias.toLowerCase()); } /** * Lists all the alias names of this keystore. * * @return enumeration of the alias names */ publicEnumeration<String> engineAliases(){ return entries.keys(); } /** * Checks if the given alias exists in this keystore. * * @param alias the alias name * * @return true if the alias exists, false otherwise */ publicboolean engineContainsAlias(String alias){ return entries.containsKey(alias.toLowerCase()); } /** * Retrieves the number of entries in this keystore. * * @return the number of entries in this keystore */ publicint engineSize(){ return entries.size(); } /** * Returns true if the entry identified by the given alias is a * <i>key entry</i>, and false otherwise. * * @return true if the entry identified by the given alias is a * <i>key entry</i>, false otherwise. */ publicboolean engineIsKeyEntry(String alias){ KeyEntry entry = entries.get(alias.toLowerCase()); if(entry !=null){ returntrue; }else{ returnfalse; } } /** * Returns true if the entry identified by the given alias is a * <i>trusted certificate entry</i>, and false otherwise. * * @return true if the entry identified by the given alias is a * <i>trusted certificate entry</i>, false otherwise. */ publicboolean engineIsCertificateEntry(String alias){ // TrustedCertEntry is not supported returnfalse; } /** * Returns the (alias) name of the first keystore entry whose certificate * matches the given certificate. * * <p>This method attempts to match the given certificate with each * keystore entry. If the entry being considered * is a <i>trusted certificate entry</i>, the given certificate is * compared to that entry's certificate. If the entry being considered is * a <i>key entry</i>, the given certificate is compared to the first * element of that entry's certificate chain (if a chain exists). * * @param cert the certificate to match with. * * @return the (alias) name of the first entry with matching certificate, * or null if no such entry exists in this keystore. */ publicString engineGetCertificateAlias(Certificate cert){ Certificate certElem =null; for(Enumeration<String> e = entries.keys(); e.hasMoreElements();){ String alias = e.nextElement(); KeyEntry entry = entries.get(alias); if(entry.chain !=null){ certElem = entry.chain[0]; } if(certElem.equals(cert)){ return alias; } } returnnull; } /** * Stores this keystore to the given output stream, and protects its * integrity with the given password. * * @param stream the output stream to which this keystore is written. * @param password the password to generate the keystore integrity check * * @exception IOException if there was an I/O problem with data * @exception NoSuchAlgorithmException if the appropriate data integrity * algorithm could not be found * @exception CertificateException if any of the certificates included in * the keystore data could not be stored */ publicsynchronizedvoid engineStore(OutputStream stream,char[] password) throwsIOException,NoSuchAlgorithmException,CertificateException { // password is mandatory when storing if(password ==null){ thrownewIllegalArgumentException("password can't be null"); } // -- Create PFX DerOutputStream pfx =newDerOutputStream(); // PFX version (always write the latest version) DerOutputStream version =newDerOutputStream(); version.putInteger(VERSION_3); byte[] pfxVersion = version.toByteArray(); pfx.write(pfxVersion); // -- Create AuthSafe DerOutputStream authSafe =newDerOutputStream(); // -- Create ContentInfos DerOutputStream authSafeContentInfo =newDerOutputStream(); // -- create safeContent Data ContentInfo byte[] safeContentData = createSafeContent(); ContentInfo dataContentInfo =newContentInfo(safeContentData); dataContentInfo.encode(authSafeContentInfo); // -- create EncryptedContentInfo byte[] encrData = createEncryptedData(password); ContentInfo encrContentInfo = newContentInfo(ContentInfo.ENCRYPTED_DATA_OID, newDerValue(encrData)); encrContentInfo.encode(authSafeContentInfo); // wrap as SequenceOf ContentInfos DerOutputStream cInfo =newDerOutputStream(); cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo); byte[] authenticatedSafe = cInfo.toByteArray(); // Create Encapsulated ContentInfo ContentInfo contentInfo =newContentInfo(authenticatedSafe); contentInfo.encode(authSafe); byte[] authSafeData = authSafe.toByteArray(); pfx.write(authSafeData); // -- MAC byte[] macData = calculateMac(password, authenticatedSafe); pfx.write(macData); // write PFX to output stream DerOutputStream pfxout =newDerOutputStream(); pfxout.write(DerValue.tag_Sequence, pfx); byte[] pfxData = pfxout.toByteArray(); stream.write(pfxData); stream.flush(); } /* * Generate Hash. */ privatebyte[] generateHash(byte[] data)throwsIOException { byte[] digest =null; try{ MessageDigest md =MessageDigest.getInstance("SHA1"); md.update(data); digest = md.digest(); }catch(Exception e){ IOException ioe =newIOException("generateHash failed: "+ e); ioe.initCause(e); throw ioe; } return digest; } /* * Calculate MAC using HMAC algorithm (required for password integrity) * * Hash-based MAC algorithm combines secret key with message digest to * create a message authentication code (MAC) */ privatebyte[] calculateMac(char[] passwd,byte[] data) throwsIOException { byte[] mData =null; String algName ="SHA1"; try{ // Generate a random salt. byte[] salt = getSalt(); // generate MAC (MAC key is generated within JCE) Mac m =Mac.getInstance("HmacPBESHA1"); PBEParameterSpec params = newPBEParameterSpec(salt, iterationCount); SecretKey key = getPBEKey(passwd); m.init(key, params); m.update(data); byte[] macResult = m.doFinal(); // encode as MacData MacData macData =newMacData(algName, macResult, salt, iterationCount); DerOutputStream bytes =newDerOutputStream(); bytes.write(macData.getEncoded()); mData = bytes.toByteArray(); }catch(Exception e){ IOException ioe =newIOException("calculateMac failed: "+ e); ioe.initCause(e); throw ioe; } return mData; } /* * Validate Certificate Chain */ privateboolean validateChain(Certificate[] certChain) { for(int i =0; i < certChain.length-1; i++){ X500Principal issuerDN = ((X509Certificate)certChain[i]).getIssuerX500Principal(); X500Principal subjectDN = ((X509Certificate)certChain[i+1]).getSubjectX500Principal(); if(!(issuerDN.equals(subjectDN))) returnfalse; } returntrue; } /* * Create PKCS#12 Attributes, friendlyName and localKeyId. * * Although attributes are optional, they could be required. * For e.g. localKeyId attribute is required to match the * private key with the associated end-entity certificate. * * PKCS8ShroudedKeyBags include unique localKeyID and friendlyName. * CertBags may or may not include attributes depending on the type * of Certificate. In end-entity certificates, localKeyID should be * unique, and the corresponding private key should have the same * localKeyID. For trusted CA certs in the cert-chain, localKeyID * attribute is not required, hence most vendors don't include it. * NSS/Netscape require it to be unique or null, where as IE/OpenSSL * ignore it. * * Here is a list of pkcs12 attribute values in CertBags. * * PKCS12 Attribute NSS/Netscape IE OpenSSL J2SE * -------------------------------------------------------------- * LocalKeyId * (In EE cert only, * NULL in CA certs) true true true true * * friendlyName unique same/ same/ unique * unique unique/ * null * * Note: OpenSSL adds friendlyName for end-entity cert only, and * removes the localKeyID and friendlyName for CA certs. * If the CertBag did not have a friendlyName, most vendors will * add it, and assign it to the DN of the cert. */ privatebyte[] getBagAttributes(String alias,byte[] keyId) throwsIOException{ byte[] localKeyID =null; byte[] friendlyName =null; // return null if both attributes are null if((alias ==null)&&(keyId ==null)){ returnnull; } // SafeBag Attributes DerOutputStream bagAttrs =newDerOutputStream(); // Encode the friendlyname oid. if(alias !=null){ DerOutputStream bagAttr1 =newDerOutputStream(); bagAttr1.putOID(PKCS9FriendlyName_OID); DerOutputStream bagAttrContent1 =newDerOutputStream(); DerOutputStream bagAttrValue1 =newDerOutputStream(); bagAttrContent1.putBMPString(alias); bagAttr1.write(DerValue.tag_Set, bagAttrContent1); bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1); friendlyName = bagAttrValue1.toByteArray(); } // Encode the localkeyId oid. if(keyId !=null){ DerOutputStream bagAttr2 =newDerOutputStream(); bagAttr2.putOID(PKCS9LocalKeyId_OID); DerOutputStream bagAttrContent2 =newDerOutputStream(); DerOutputStream bagAttrValue2 =newDerOutputStream(); bagAttrContent2.putOctetString(keyId); bagAttr2.write(DerValue.tag_Set, bagAttrContent2); bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2); localKeyID = bagAttrValue2.toByteArray(); } DerOutputStream attrs =newDerOutputStream(); if(friendlyName !=null){ attrs.write(friendlyName); } if(localKeyID !=null){ attrs.write(localKeyID); } bagAttrs.write(DerValue.tag_Set, attrs); return bagAttrs.toByteArray(); } /* * Create EncryptedData content type, that contains EncryptedContentInfo. * Includes certificates in individual SafeBags of type CertBag. * Each CertBag may include pkcs12 attributes * (see comments in getBagAttributes) */ privatebyte[] createEncryptedData(char[] password) throwsCertificateException,IOException { DerOutputStream out =newDerOutputStream(); for(Enumeration<String> e = entries.keys(); e.hasMoreElements();){ String alias = e.nextElement(); KeyEntry entry = entries.get(alias); // certificate chain int chainLen; if(entry.chain ==null){ chainLen =0; }else{ chainLen = entry.chain.length; } for(int i =0; i < chainLen; i++){ // create SafeBag of Type CertBag DerOutputStream safeBag =newDerOutputStream(); safeBag.putOID(CertBag_OID); // create a CertBag DerOutputStream certBag =newDerOutputStream(); certBag.putOID(PKCS9CertType_OID); // write encoded certs in a context-specific tag DerOutputStream certValue =newDerOutputStream(); X509Certificate cert =(X509Certificate)entry.chain[i]; certValue.putOctetString(cert.getEncoded()); certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,(byte)0), certValue); // wrap CertBag in a Sequence DerOutputStream certout =newDerOutputStream(); certout.write(DerValue.tag_Sequence, certBag); byte[] certBagValue = certout.toByteArray(); // Wrap the CertBag encoding in a context-specific tag. DerOutputStream bagValue =newDerOutputStream(); bagValue.write(certBagValue); // write SafeBag Value safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,(byte)0), bagValue); // write SafeBag Attributes // All Certs should have a unique friendlyName. // This change is made to meet NSS requirements. byte[] bagAttrs =null; if(i ==0){ // Only End-Entity Cert should have a localKeyId. bagAttrs = getBagAttributes(entry.alias, entry.keyId); }else{ // Trusted root CA certs and Intermediate CA certs do not // need to have a localKeyId, and hence localKeyId is null // This change is made to meet NSS/Netscape requirements. // NSS pkcs12 library requires trusted CA certs in the // certificate chain to have unique or null localKeyID. // However, IE/OpenSSL do not impose this restriction. bagAttrs = getBagAttributes( cert.getSubjectX500Principal().getName(),null); } if(bagAttrs !=null){ safeBag.write(bagAttrs); } // wrap as Sequence out.write(DerValue.tag_Sequence, safeBag); }// for cert-chain } // wrap as SequenceOf SafeBag DerOutputStream safeBagValue =newDerOutputStream(); safeBagValue.write(DerValue.tag_SequenceOf, out); byte[] safeBagData = safeBagValue.toByteArray(); // encrypt the content (EncryptedContentInfo) byte[] encrContentInfo = encryptContent(safeBagData, password); // -- SEQUENCE of EncryptedData DerOutputStream encrData =newDerOutputStream(); DerOutputStream encrDataContent =newDerOutputStream(); encrData.putInteger(0); encrData.write(encrContentInfo); encrDataContent.write(DerValue.tag_Sequence, encrData); return encrDataContent.toByteArray(); } /* * Create SafeContent Data content type. * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag. * Each PKCS8ShroudedKeyBag includes pkcs12 attributes * (see comments in getBagAttributes) */ privatebyte[] createSafeContent() throwsCertificateException,IOException{ DerOutputStream out =newDerOutputStream(); for(Enumeration<String> e = entries.keys(); e.hasMoreElements();){ String alias = e.nextElement(); KeyEntry entry = entries.get(alias); // Create SafeBag of type pkcs8ShroudedKeyBag DerOutputStream safeBag =newDerOutputStream(); safeBag.putOID(PKCS8ShroudedKeyBag_OID); // get the encrypted private key byte[] encrBytes = entry.protectedPrivKey; EncryptedPrivateKeyInfo encrInfo =null; try{ encrInfo =newEncryptedPrivateKeyInfo(encrBytes); }catch(IOException ioe){ thrownewIOException("Private key not stored as " +"PKCS#8 EncryptedPrivateKeyInfo"+ ioe.getMessage()); } // Wrap the EncryptedPrivateKeyInfo in a context-specific tag. DerOutputStream bagValue =newDerOutputStream(); bagValue.write(encrInfo.getEncoded()); safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,(byte)0), bagValue); // write SafeBag Attributes byte[] bagAttrs = getBagAttributes(alias, entry.keyId); safeBag.write(bagAttrs); // wrap as Sequence out.write(DerValue.tag_Sequence, safeBag); } // wrap as Sequence DerOutputStream safeBagValue =newDerOutputStream(); safeBagValue.write(DerValue.tag_Sequence, out); return safeBagValue.toByteArray(); } /* * Encrypt the contents using Password-based (PBE) encryption * as defined in PKCS #5. * * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used * to derive the key and IV. * * @return encrypted contents encoded as EncryptedContentInfo */ privatebyte[] encryptContent(byte[] data,char[] password) throwsIOException{ byte[] encryptedData =null; // create AlgorithmParameters AlgorithmParameters algParams = getAlgorithmParameters("PBEWithSHA1AndRC2_40"); DerOutputStream bytes =newDerOutputStream(); AlgorithmId algId = newAlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams); algId.encode(bytes); byte[] encodedAlgId = bytes.toByteArray(); try{ // Use JCE SecretKey skey = getPBEKey(password); Cipher cipher =Cipher.getInstance("PBEWithSHA1AndRC2_40"); cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); encryptedData = cipher.doFinal(data); }catch(Exception e){ IOException ioe =newIOException("Failed to encrypt"+ " safe contents entry: "+ e); ioe.initCause(e); throw ioe; } // create EncryptedContentInfo DerOutputStream bytes2 =newDerOutputStream(); bytes2.putOID(ContentInfo.DATA_OID); bytes2.write(encodedAlgId); // Wrap encrypted data in a context-specific tag. DerOutputStream tmpout2 =newDerOutputStream(); tmpout2.putOctetString(encryptedData); bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false,(byte)0), tmpout2); // wrap EncryptedContentInfo in a Sequence DerOutputStream out =newDerOutputStream(); out.write(DerValue.tag_Sequence, bytes2); return out.toByteArray(); } /** * Loads the keystore from the given input stream. * * <p>If a password is given, it is used to check the integrity of the * keystore data. Otherwise, the integrity of the keystore is not checked. * * @param stream the input stream from which the keystore is loaded * @param password the (optional) password used to check the integrity of * the keystore. * * @exception IOException if there is an I/O or format problem with the * keystore data * @exception NoSuchAlgorithmException if the algorithm used to check * the integrity of the keystore cannot be found * @exception CertificateException if any of the certificates in the * keystore could not be loaded */ publicsynchronizedvoid engineLoad(InputStream stream,char[] password) throwsIOException,NoSuchAlgorithmException,CertificateException { DataInputStream dis; CertificateFactory cf =null; ByteArrayInputStream bais =null; byte[] encoded =null; if(stream ==null) return; // reset the counter counter =0; DerValue val =newDerValue(stream); DerInputStream s = val.toDerInputStream(); int version = s.getInteger(); if(version != VERSION_3){ thrownewIOException("PKCS12 keystore not in version 3 format"); } entries.clear(); /* * Read the authSafe. */ byte[] authSafeData; ContentInfo authSafe =newContentInfo(s); ObjectIdentifier contentType = authSafe.getContentType(); if(contentType.equals(ContentInfo.DATA_OID)){ authSafeData = authSafe.getData(); }else/* signed data */{ thrownewIOException("public key protected PKCS12 not supported"); } DerInputStream as =newDerInputStream(authSafeData); DerValue[] safeContentsArray = as.getSequence(2); int count = safeContentsArray.length; // reset the count at the start privateKeyCount =0; /* * Spin over the ContentInfos. */ for(int i =0; i < count; i++){ byte[] safeContentsData; ContentInfo safeContents; DerInputStream sci; byte[] eAlgId =null; sci =newDerInputStream(safeContentsArray[i].toByteArray()); safeContents =newContentInfo(sci); contentType = safeContents.getContentType(); safeContentsData =null; if(contentType.equals(ContentInfo.DATA_OID)){ safeContentsData = safeContents.getData(); }elseif(contentType.equals(ContentInfo.ENCRYPTED_DATA_OID)){ if(password ==null){ continue; } DerInputStream edi = safeContents.getContent().toDerInputStream(); int edVersion = edi.getInteger(); DerValue[] seq = edi.getSequence(2); ObjectIdentifier edContentType = seq[0].getOID(); eAlgId = seq[1].toByteArray(); if(!seq[2].isContextSpecific((byte)0)){ thrownewIOException("encrypted content not present!"); } byte newTag =DerValue.tag_OctetString; if(seq[2].isConstructed()) newTag |=0x20; seq[2].resetTag(newTag); safeContentsData = seq[2].getOctetString(); // parse Algorithm parameters DerInputStream in = seq[1].toDerInputStream(); ObjectIdentifier algOid = in.getOID(); AlgorithmParameters algParams = parseAlgParameters(in); while(true){ try{ // Use JCE SecretKey skey = getPBEKey(password); Cipher cipher =Cipher.getInstance(algOid.toString()); cipher.init(Cipher.DECRYPT_MODE, skey, algParams); safeContentsData = cipher.doFinal(safeContentsData); break; }catch(Exception e){ if(password.length ==0){ // Retry using an empty password // without a NULL terminator. password =newchar[1]; continue; } thrownewIOException( "failed to decrypt safe contents entry: "+ e, e); } } }else{ thrownewIOException("public key protected PKCS12"+ " not supported"); } DerInputStream sc =newDerInputStream(safeContentsData); loadSafeContents(sc, password); } // The MacData is optional. if(password !=null&& s.available()>0){ MacData macData =newMacData(s); try{ String algName = macData.getDigestAlgName().toUpperCase(); if(algName.equals("SHA")|| algName.equals("SHA1")|| algName.equals("SHA-1")){ algName ="SHA1"; } // generate MAC (MAC key is created within JCE) Mac m =Mac.getInstance("HmacPBE"+ algName); PBEParameterSpec params = newPBEParameterSpec(macData.getSalt(), macData.getIterations()); SecretKey key = getPBEKey(password); m.init(key, params); m.update(authSafeData); byte[] macResult = m.doFinal(); if(!Arrays.equals(macData.getDigest(), macResult)){ thrownewSecurityException("Failed PKCS12"+ " integrity checking"); } }catch(Exception e){ IOException ioe = newIOException("Integrity check failed: "+ e); ioe.initCause(e); throw ioe; } } /* * Match up private keys with certificate chains. */ KeyEntry[] list = keyList.toArray(newKeyEntry[keyList.size()]); for(int m =0; m < list.length; m++){ KeyEntry entry = list[m]; if(entry.keyId !=null){ ArrayList<X509Certificate> chain = newArrayList<X509Certificate>(); X509Certificate cert = findMatchedCertificate(entry); while(cert !=null){ chain.add(cert); X500Principal issuerDN = cert.getIssuerX500Principal(); if(issuerDN.equals(cert.getSubjectX500Principal())){ break; } cert = certsMap.get(issuerDN); } /* Update existing KeyEntry in entries table */ if(chain.size()>0) entry.chain = chain.toArray(newCertificate[chain.size()]); } } certEntries.clear(); certsMap.clear(); keyList.clear(); } /** * Locates a matched CertEntry from certEntries, and returns its cert. * @param entry the KeyEntry to match * @return a certificate, null if not found */ private X509Certificate findMatchedCertificate(KeyEntry entry){ CertEntry keyIdMatch =null; CertEntry aliasMatch =null; for(CertEntry ce: certEntries){ if(Arrays.equals(entry.keyId, ce.keyId)){ keyIdMatch = ce; if(entry.alias.equalsIgnoreCase(ce.alias)){ // Full match! return ce.cert; } }elseif(entry.alias.equalsIgnoreCase(ce.alias)){ aliasMatch = ce; } } // keyId match first, for compatibility if(keyIdMatch !=null)return keyIdMatch.cert; elseif(aliasMatch !=null)return aliasMatch.cert; elsereturnnull; } privatevoid loadSafeContents(DerInputStream stream,char[] password) throwsIOException,NoSuchAlgorithmException,CertificateException { DerValue[] safeBags = stream.getSequence(2); int count = safeBags.length; /* * Spin over the SafeBags. */ for(int i =0; i < count; i++){ ObjectIdentifier bagId; DerInputStream sbi; DerValue bagValue; Object bagItem =null; sbi = safeBags[i].toDerInputStream(); bagId = sbi.getOID(); bagValue = sbi.getDerValue(); if(!bagValue.isContextSpecific((byte)0)){ thrownewIOException("unsupported PKCS12 bag value type " + bagValue.tag); } bagValue = bagValue.data.getDerValue(); if(bagId.equals(PKCS8ShroudedKeyBag_OID)){ KeyEntry kEntry =newKeyEntry(); kEntry.protectedPrivKey = bagValue.toByteArray(); bagItem = kEntry; privateKeyCount++; }elseif(bagId.equals(CertBag_OID)){ DerInputStream cs =newDerInputStream(bagValue.toByteArray()); DerValue[] certValues = cs.getSequence(2); ObjectIdentifier certId = certValues[0].getOID(); if(!certValues[1].isContextSpecific((byte)0)){ thrownewIOException("unsupported PKCS12 cert value type " + certValues[1].tag); } DerValue certValue = certValues[1].data.getDerValue(); CertificateFactory cf =CertificateFactory.getInstance("X509"); X509Certificate cert; cert =(X509Certificate)cf.generateCertificate (newByteArrayInputStream(certValue.getOctetString())); bagItem = cert; }else{ // log error message for "unsupported PKCS12 bag type" } DerValue[] attrSet; try{ attrSet = sbi.getSet(2); }catch(IOException e){ // entry does not have attributes // Note: CA certs can have no attributes // OpenSSL generates pkcs12 with no attr for CA certs. attrSet =null; } String alias =null; byte[] keyId =null; if(attrSet !=null){ for(int j =0; j < attrSet.length; j++){ DerInputStream as = newDerInputStream(attrSet[j].toByteArray()); DerValue[] attrSeq = as.getSequence(2); ObjectIdentifier attrId = attrSeq[0].getOID(); DerInputStream vs = newDerInputStream(attrSeq[1].toByteArray()); DerValue[] valSet; try{ valSet = vs.getSet(1); }catch(IOException e){ thrownewIOException("Attribute "+ attrId + " should have a value "+ e.getMessage()); } if(attrId.equals(PKCS9FriendlyName_OID)){ alias = valSet[0].getBMPString(); }elseif(attrId.equals(PKCS9LocalKeyId_OID)){ keyId = valSet[0].getOctetString(); }else{ // log error message for "unknown attr" } } } /* * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId) * are optional PKCS12 bagAttributes. But entries in the keyStore * are identified by their alias. Hence we need to have an * Unfriendlyname in the alias, if alias is null. The keyId * attribute is required to match the private key with the * certificate. If we get a bagItem of type KeyEntry with a * null keyId, we should skip it entirely. */ if(bagItem instanceofKeyEntry){ KeyEntry entry =(KeyEntry)bagItem; if(keyId ==null){ // Insert a localKeyID for the privateKey // Note: This is a workaround to allow null localKeyID // attribute in pkcs12 with one private key entry and // associated cert-chain if(privateKeyCount ==1){ keyId ="01".getBytes("UTF8"); }else{ continue; } } entry.keyId = keyId; // restore date if it exists String keyIdStr =newString(keyId,"UTF8"); Date date =null; if(keyIdStr.startsWith("Time ")){ try{ date =newDate( Long.parseLong(keyIdStr.substring(5))); }catch(Exception e){ date =null; } } if(date ==null){ date =newDate(); } entry.date = date; keyList.add(entry); if(alias ==null) alias = getUnfriendlyName(); entry.alias = alias; entries.put(alias.toLowerCase(), entry); }elseif(bagItem instanceof X509Certificate){ X509Certificate cert =(X509Certificate)bagItem; // Insert a localKeyID for the corresponding cert // Note: This is a workaround to allow null localKeyID // attribute in pkcs12 with one private key entry and // associated cert-chain if((keyId ==null)&&(privateKeyCount ==1)){ // insert localKeyID only for EE cert or self-signed cert if(i ==0){ keyId ="01".getBytes("UTF8"); } } certEntries.add(newCertEntry(cert, keyId, alias)); X500Principal subjectDN = cert.getSubjectX500Principal(); if(subjectDN !=null){ if(!certsMap.containsKey(subjectDN)){ certsMap.put(subjectDN, cert); } } } } } privateString getUnfriendlyName(){ counter++; return(String.valueOf(counter)); } }

Toplist

Latest post

TAGs