All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.litsec.opensaml.xmlsec.ExtendedDecrypter Maven / Gradle / Ivy

There is a newer version: 1.4.5
Show newest version
/*
 * Copyright 2016-2018 Litsec AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.litsec.opensaml.xmlsec;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.util.Collection;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;

import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.encryption.EncryptionMethod;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.encryption.XMLCipherInput;
import org.apache.xml.security.encryption.XMLEncryptionException;
import org.apache.xml.security.utils.EncryptionConstants;
import org.opensaml.security.credential.Credential;
import org.opensaml.xmlsec.DecryptionParameters;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
import org.opensaml.xmlsec.encryption.EncryptedKey;
import org.opensaml.xmlsec.encryption.support.Decrypter;
import org.opensaml.xmlsec.encryption.support.DecryptionException;
import org.opensaml.xmlsec.encryption.support.EncryptedKeyResolver;
import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

import com.google.common.base.Strings;

import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
import se.swedenconnect.opensaml.xmlsec.encryption.support.Pkcs11Decrypter;

/**
 * An extension of OpenSAML:s {@link Decrypter} implementation that handles the problem that when using the SunPKCS11
 * crypto provider the OAEPPadding does not work. This implementation supplies an workaround for this problem.
 * 

* See this post on * Stack overflow. *

*

* Deprecated: Use {@link Pkcs11Decrypter} instead. *

* * @author Martin Lindström ([email protected]) */ @Deprecated public class ExtendedDecrypter extends Decrypter { /** Class logger. */ private final Logger log = LoggerFactory.getLogger(ExtendedDecrypter.class); /** Test mode allows us to test the functionality with a soft key. */ private boolean testMode = false; /** Key length of decryption keys. */ private int keyLength = -1; /** Resolver for key encryption keys. */ private KeyInfoCredentialResolver _kekResolver; /** * Constructor. * * @param params * decryption parameters to use */ public ExtendedDecrypter(DecryptionParameters params) { super(params); this._kekResolver = params.getKEKKeyInfoCredentialResolver(); } /** * Constructor. * * @param newResolver * resolver for data encryption keys. * @param newKEKResolver * resolver for key encryption keys. * @param newEncKeyResolver * resolver for EncryptedKey elements */ public ExtendedDecrypter(KeyInfoCredentialResolver newResolver, KeyInfoCredentialResolver newKEKResolver, EncryptedKeyResolver newEncKeyResolver) { super(newResolver, newKEKResolver, newEncKeyResolver); this._kekResolver = newKEKResolver; } /** * Constructor. * * @param newResolver * resolver for data encryption keys. * @param newKEKResolver * resolver for key encryption keys. * @param newEncKeyResolver * resolver for EncryptedKey elements * @param whitelistAlgos * collection of whitelisted algorithm URIs * @param blacklistAlgos * collection of blacklisted algorithm URIs */ public ExtendedDecrypter(KeyInfoCredentialResolver newResolver, KeyInfoCredentialResolver newKEKResolver, EncryptedKeyResolver newEncKeyResolver, Collection whitelistAlgos, Collection blacklistAlgos) { super(newResolver, newKEKResolver, newEncKeyResolver, whitelistAlgos, blacklistAlgos); this._kekResolver = newKEKResolver; } /** * Init method for setting key size ... */ public void init() { if (this._kekResolver != null) { CriteriaSet cs = new CriteriaSet(); try { Credential cred = this._kekResolver.resolveSingle(cs); if (cred != null) { PublicKey pubKey = cred.getPublicKey(); if (pubKey != null) { this.keyLength = getKeyLength(pubKey); } } } catch (ResolverException e) { } if (this.keyLength <= 0) { log.error("Failed to resolve any certificates for key decryption"); } } } /** * Overrides the {@link Decrypter#decryptKey(EncryptedKey, String, Key)} so that we may handle the unsupported * features of the SunPKCS11 provider. */ @Override public Key decryptKey(EncryptedKey encryptedKey, String algorithm, Key kek) throws DecryptionException { if (!testMode && kek != null && !"sun.security.pkcs11.P11Key$P11PrivateKey".equals(kek.getClass().getName())) { return super.decryptKey(encryptedKey, algorithm, kek); } if (!AlgorithmSupport.isRSAOAEP(encryptedKey.getEncryptionMethod().getAlgorithm())) { return super.decryptKey(encryptedKey, algorithm, kek); } if (Strings.isNullOrEmpty(algorithm)) { log.error("Algorithm of encrypted key not supplied, key decryption cannot proceed."); throw new DecryptionException("Algorithm of encrypted key not supplied, key decryption cannot proceed."); } this.validateAlgorithms(encryptedKey); try { this.checkAndMarshall(encryptedKey); } catch (DecryptionException e) { log.error("Error marshalling EncryptedKey for decryption", e); throw e; } this.preProcessEncryptedKey(encryptedKey, algorithm, kek); XMLCipher xmlCipher; try { if (getJCAProviderName() != null) { xmlCipher = XMLCipher.getProviderInstance(getJCAProviderName()); } else { xmlCipher = XMLCipher.getInstance(); } xmlCipher.init(XMLCipher.UNWRAP_MODE, kek); } catch (XMLEncryptionException e) { log.error("Error initialzing cipher instance on key decryption", e); throw new DecryptionException("Error initialzing cipher instance on key decryption", e); } org.apache.xml.security.encryption.EncryptedKey encKey; try { Element targetElement = encryptedKey.getDOM(); encKey = xmlCipher.loadEncryptedKey(targetElement.getOwnerDocument(), targetElement); } catch (XMLEncryptionException e) { log.error("Error when loading library native encrypted key representation", e); throw new DecryptionException("Error when loading library native encrypted key representation", e); } try { Key key = this.customizedDecryptKey(encKey, algorithm, kek); if (key == null) { throw new DecryptionException("Key could not be decrypted"); } return key; } catch (XMLEncryptionException e) { log.error("Error decrypting encrypted key", e); throw new DecryptionException("Error decrypting encrypted key", e); } catch (Exception e) { throw new DecryptionException("Probable runtime exception on decryption:" + e.getMessage(), e); } } /** * Performs the actual key decryption. * * @param encryptedKey * the encrypted key * @param algorithm * the algorithm * @param kek * the private key * @return a secret key * @throws XMLEncryptionException * for errors */ @SuppressWarnings("restriction") private Key customizedDecryptKey(org.apache.xml.security.encryption.EncryptedKey encryptedKey, String algorithm, Key kek) throws XMLEncryptionException { // Obtain the encrypted octets byte[] encryptedBytes = (new XMLCipherInput(encryptedKey)).getBytes(); try { String provider = this.getJCAProviderName(); Cipher c = provider != null ? Cipher.getInstance("RSA/ECB/NoPadding", provider) : Cipher.getInstance("RSA/ECB/NoPadding"); c.init(Cipher.DECRYPT_MODE, kek); byte[] paddedPlainText = c.doFinal(encryptedBytes); //int keyLength = this.getKeySize(kek); /* Ensure leading zeros not stripped */ if (paddedPlainText.length < this.keyLength / 8) { byte[] tmp = new byte[this.keyLength / 8]; System.arraycopy(paddedPlainText, 0, tmp, tmp.length - paddedPlainText.length, paddedPlainText.length); paddedPlainText = tmp; } EncryptionMethod encMethod = encryptedKey.getEncryptionMethod(); OAEPParameterSpec oaepParameters = constructOAEPParameters(encMethod.getAlgorithm(), encMethod.getDigestAlgorithm(), encMethod.getMGFAlgorithm(), encMethod.getOAEPparams()); sun.security.rsa.RSAPadding padding = sun.security.rsa.RSAPadding.getInstance( sun.security.rsa.RSAPadding.PAD_OAEP_MGF1, keyLength / 8, new SecureRandom(), oaepParameters); byte[] secretKeyBytes = padding.unpad(paddedPlainText); String jceKeyAlgorithm = JCEMapper.getJCEKeyAlgorithmFromURI(algorithm); return new SecretKeySpec(secretKeyBytes, jceKeyAlgorithm); } catch (NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) { throw new XMLEncryptionException(e); } } /** * Construct an OAEPParameterSpec object from the given parameters */ private OAEPParameterSpec constructOAEPParameters( String encryptionAlgorithm, String digestAlgorithm, String mgfAlgorithm, byte[] oaepParams) { String jceDigestAlgorithm = "SHA-1"; if (digestAlgorithm != null) { jceDigestAlgorithm = JCEMapper.translateURItoJCEID(digestAlgorithm); } PSource.PSpecified pSource = PSource.PSpecified.DEFAULT; if (oaepParams != null) { pSource = new PSource.PSpecified(oaepParams); } MGF1ParameterSpec mgfParameterSpec = new MGF1ParameterSpec("SHA-1"); if (XMLCipher.RSA_OAEP_11.equals(encryptionAlgorithm)) { if (EncryptionConstants.MGF1_SHA256.equals(mgfAlgorithm)) { mgfParameterSpec = new MGF1ParameterSpec("SHA-256"); } else if (EncryptionConstants.MGF1_SHA384.equals(mgfAlgorithm)) { mgfParameterSpec = new MGF1ParameterSpec("SHA-384"); } else if (EncryptionConstants.MGF1_SHA512.equals(mgfAlgorithm)) { mgfParameterSpec = new MGF1ParameterSpec("SHA-512"); } } return new OAEPParameterSpec(jceDigestAlgorithm, "MGF1", mgfParameterSpec, pSource); } /** * Should we run this class in test mode? * * @param testMode * test mode flag */ public void setTestMode(boolean testMode) { this.testMode = testMode; } private static int getKeyLength(final PublicKey pk) { int len = -1; if (pk instanceof RSAPublicKey) { final RSAPublicKey rsapub = (RSAPublicKey) pk; len = rsapub.getModulus().bitLength(); } else if (pk instanceof ECPublicKey) { final ECPublicKey ecpriv = (ECPublicKey) pk; final java.security.spec.ECParameterSpec spec = ecpriv.getParams(); if (spec != null) { len = spec.getOrder().bitLength(); // does this really return something we expect? } else { // We support the key, but we don't know the key length len = 0; } } else if (pk instanceof DSAPublicKey) { final DSAPublicKey dsapub = (DSAPublicKey) pk; if (dsapub.getParams() != null) { len = dsapub.getParams().getP().bitLength(); } else { len = dsapub.getY().bitLength(); } } return len; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy