diff --git a/pom.xml b/pom.xml index 928619b..c319fa6 100644 --- a/pom.xml +++ b/pom.xml @@ -48,9 +48,7 @@ 0.9.5.5 1.0.2 1.0.9 - 1.68 - 1.68 - 1.68 + 1.81 3.1.0 @@ -67,17 +65,17 @@ org.nhind direct-policy - 8.0.0 + 8.1.0-SNAPSHOT org.nhind direct-common - 8.0.0 + 8.1.0-SNAPSHOT org.nhind direct-msg-monitor-model - 8.0.0 + 8.1.0-SNAPSHOT org.projectlombok @@ -112,18 +110,8 @@ org.bouncycastle - bcprov-jdk15on - ${bcprov-jdk15on.version} - - - org.bouncycastle - bcmail-jdk15on - ${bcmail-jdk15on.version} - - - org.bouncycastle - bcpkix-jdk15on - ${bcpkix-jdk15on.version} + bcpkix-jdk18on + ${bcpkix-jdk18on.version} dnsjava @@ -164,6 +152,20 @@ org.springframework.boot spring-boot-starter-test test + + + org.bouncycastle + bcprov-jdk15on + + + org.bouncycastle + bcmail-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + diff --git a/src/main/java/org/nhindirect/stagent/cryptography/EncryptionAlgorithm.java b/src/main/java/org/nhindirect/stagent/cryptography/EncryptionAlgorithm.java index 27a2d5b..26c0db4 100644 --- a/src/main/java/org/nhindirect/stagent/cryptography/EncryptionAlgorithm.java +++ b/src/main/java/org/nhindirect/stagent/cryptography/EncryptionAlgorithm.java @@ -23,6 +23,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY package org.nhindirect.stagent.cryptography; import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.cms.CMSEnvelopedGenerator; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator; @@ -45,8 +46,10 @@ public enum EncryptionAlgorithm DES_EDE3_CBC("DESEDE/CBC/PKCS5Padding", CMSEnvelopedGenerator.DES_EDE3_CBC), AES128_CBC("AES/CBC/PKCS5Padding", CMSEnvelopedGenerator.AES128_CBC), AES192_CBC("AES/CBC/PKCS5Padding", CMSEnvelopedGenerator.AES192_CBC), - AES256_CBC("AES/CBC/PKCS5Padding", CMSEnvelopedGenerator.AES256_CBC); - + AES256_CBC("AES/CBC/PKCS5Padding", CMSEnvelopedGenerator.AES256_CBC), + RSA_OAEP("RSA_OAEP", PKCSObjectIdentifiers.id_RSAES_OAEP.getId()), + RSA_PKCS1_V15("RSA_PKCS1_V15", PKCSObjectIdentifiers.rsaEncryption.getId()); + protected final String algName; protected final String OID; @@ -82,7 +85,9 @@ else if (algorithmName.equalsIgnoreCase(RSA.getAlgName())) else if (algorithmName.equalsIgnoreCase(RSAandMGF1.getAlgName())) return RSAandMGF1; else if (algorithmName.equalsIgnoreCase(ECDSA.getAlgName())) - return ECDSA; + return ECDSA; + else if (algorithmName.equalsIgnoreCase(RSA_PKCS1_V15.getAlgName())) + return RSA_PKCS1_V15; else return defaultAlgorithm; } diff --git a/src/main/java/org/nhindirect/stagent/cryptography/SMIMECryptographerImpl.java b/src/main/java/org/nhindirect/stagent/cryptography/SMIMECryptographerImpl.java index e95f533..0cccc82 100644 --- a/src/main/java/org/nhindirect/stagent/cryptography/SMIMECryptographerImpl.java +++ b/src/main/java/org/nhindirect/stagent/cryptography/SMIMECryptographerImpl.java @@ -22,6 +22,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY package org.nhindirect.stagent.cryptography; +import java.security.spec.MGF1ParameterSpec; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -36,6 +37,8 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; import javax.mail.MessagingException; import javax.mail.internet.ContentType; import javax.mail.internet.InternetHeaders; @@ -47,12 +50,17 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DLSequence; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.CMSAttributes; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.RSAESOAEPparams; import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute; import org.bouncycastle.asn1.smime.SMIMECapability; import org.bouncycastle.asn1.smime.SMIMECapabilityVector; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; @@ -70,7 +78,9 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import org.bouncycastle.mail.smime.SMIMEEnveloped; import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator; import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DefaultAlgorithmNameFinder; import org.bouncycastle.operator.OutputEncryptor; +import org.bouncycastle.operator.jcajce.JcaAlgorithmParametersConverter; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.nhindirect.common.crypto.CryptoExtensions; @@ -101,65 +111,125 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY */ @Slf4j public class SMIMECryptographerImpl implements Cryptographer -{ - // Using the BC PKCSObjectIdentifiers type is not compatible across versions of the BC library - public final static ASN1ObjectIdentifier x509CertificateObjectsIdent = new ASN1ObjectIdentifier("1.2.840.113549.1.9.22.1"); - +{ + // Using the BC PKCSObjectIdentifiers type is not compatible across versions of the BC library + public final static ASN1ObjectIdentifier x509CertificateObjectsIdent = new ASN1ObjectIdentifier("1.2.840.113549.1.9.22.1"); + public final static SMIMECryptographerImpl Default = new SMIMECryptographerImpl(); - + protected EncryptionAlgorithm m_encryptionAlgorithm; + + protected EncryptionAlgorithm m_keyEncryptionAlgorithm; + + protected DigestAlgorithm m_keyEncryptionDigestAlgorithm; protected DigestAlgorithm m_digestAlgorithm; protected boolean m_includeEpilogue = true; protected boolean enforceStrongEncryption; protected boolean enforceStrongDigests; - - private boolean m_logDigest = false; - + + private boolean m_logDigest = false; + /** * Constructs a Cryptographer with a default EncryptionAlgorithm and DigestAlgorithm. */ public SMIMECryptographerImpl() { - OptionsParameter param = OptionsManager.getInstance().getParameter(OptionsParameter.CRYPTOGRAHPER_SMIME_ENCRYPTION_ALGORITHM); - this.m_encryptionAlgorithm = (param == null) ? EncryptionAlgorithm.AES128 : EncryptionAlgorithm.fromString(param.getParamValue(), EncryptionAlgorithm.AES128); - + OptionsParameter param = OptionsManager.getInstance().getParameter(OptionsParameter.CRYPTOGRAHPER_SMIME_ENCRYPTION_ALGORITHM); + this.m_encryptionAlgorithm = (param == null) ? EncryptionAlgorithm.AES128 : EncryptionAlgorithm.fromString(param.getParamValue(), EncryptionAlgorithm.AES128); + + param = OptionsManager.getInstance().getParameter(OptionsParameter.CRYPTOGRAHPER_KEY_ENCRYPTION_ALGORITHM); + this.m_keyEncryptionAlgorithm = (param == null) ? EncryptionAlgorithm.RSA_OAEP : EncryptionAlgorithm.fromString(param.getParamValue(), EncryptionAlgorithm.RSA_OAEP); + + param = OptionsManager.getInstance().getParameter(OptionsParameter.CRYPTOGRAHPER_KEY_ENCRYPTION_DIGEST_ALGORITHM); + this.m_keyEncryptionDigestAlgorithm = (param == null) ? DigestAlgorithm.SHA1: DigestAlgorithm.fromString(param.getParamValue(), DigestAlgorithm.SHA1); + param = OptionsManager.getInstance().getParameter(OptionsParameter.CRYPTOGRAHPER_SMIME_DIGEST_ALGORITHM); this.m_digestAlgorithm = (param == null) ? DigestAlgorithm.SHA256WITHRSA : DigestAlgorithm.fromString(param.getParamValue(), DigestAlgorithm.SHA256WITHRSA); - + if (!isAllowedDigestAlgorithm(m_digestAlgorithm.getOID())) - throw new IllegalArgumentException("The digest algorithm " + m_digestAlgorithm.getAlgName() + " is not allowed"); - + throw new IllegalArgumentException("The digest algorithm " + m_digestAlgorithm.getAlgName() + " is not allowed"); + param = OptionsManager.getInstance().getParameter(OptionsParameter.ENFORCE_STRONG_DIGESTS); this.enforceStrongDigests = OptionsParameter.getParamValueAsBoolean(param, true); - + param = OptionsManager.getInstance().getParameter(OptionsParameter.ENFORCE_STRONG_ENCRYPTION); this.enforceStrongEncryption = OptionsParameter.getParamValueAsBoolean(param, true); - - this.m_logDigest = OptionsParameter.getParamValueAsBoolean(param, false); + + this.m_logDigest = OptionsParameter.getParamValueAsBoolean(param, false); } /** * Constructs a Cryptographer with an EncryptionAlgorithm and DigestAlgorithm. * @param encryptionAlgorithm The encryption algorithm used to encrypt the message. * @param digestAlgorithm The digest algorithm used to generate the message digest stored in the message signature. - */ + */ public SMIMECryptographerImpl(EncryptionAlgorithm encryptionAlgorithm, DigestAlgorithm digestAlgorithm) { this.m_encryptionAlgorithm = encryptionAlgorithm; this.m_digestAlgorithm = digestAlgorithm; - + + OptionsParameter param = OptionsManager.getInstance().getParameter(OptionsParameter.ENFORCE_STRONG_DIGESTS); + this.enforceStrongDigests = OptionsParameter.getParamValueAsBoolean(param, true); + + if (!isAllowedDigestAlgorithm(m_digestAlgorithm.getOID())) + throw new IllegalArgumentException("The digest algorith " + m_digestAlgorithm.getAlgName() + " is not allowed"); + + param = OptionsManager.getInstance().getParameter(OptionsParameter.ENFORCE_STRONG_ENCRYPTION); + this.enforceStrongEncryption = OptionsParameter.getParamValueAsBoolean(param, true); + + this.m_logDigest = OptionsParameter.getParamValueAsBoolean(param, false); + + param = OptionsManager.getInstance().getParameter(OptionsParameter.CRYPTOGRAHPER_KEY_ENCRYPTION_ALGORITHM); + this.m_keyEncryptionAlgorithm = (param == null) ? EncryptionAlgorithm.RSA_OAEP : EncryptionAlgorithm.fromString(param.getParamValue(), EncryptionAlgorithm.RSA_OAEP); + + param = OptionsManager.getInstance().getParameter(OptionsParameter.CRYPTOGRAHPER_KEY_ENCRYPTION_DIGEST_ALGORITHM); + this.m_keyEncryptionDigestAlgorithm = (param == null) ? DigestAlgorithm.SHA256 : DigestAlgorithm.fromString(param.getParamValue(), DigestAlgorithm.SHA256); + + } + + public SMIMECryptographerImpl(EncryptionAlgorithm encryptionAlgorithm, DigestAlgorithm digestAlgorithm, EncryptionAlgorithm keyEncryptionAlgorithm) + { + this.m_encryptionAlgorithm = encryptionAlgorithm; + this.m_digestAlgorithm = digestAlgorithm; + + OptionsParameter param = OptionsManager.getInstance().getParameter(OptionsParameter.ENFORCE_STRONG_DIGESTS); + this.enforceStrongDigests = OptionsParameter.getParamValueAsBoolean(param, true); + + if (!isAllowedDigestAlgorithm(m_digestAlgorithm.getOID())) + throw new IllegalArgumentException("The digest algorith " + m_digestAlgorithm.getAlgName() + " is not allowed"); + + param = OptionsManager.getInstance().getParameter(OptionsParameter.ENFORCE_STRONG_ENCRYPTION); + this.enforceStrongEncryption = OptionsParameter.getParamValueAsBoolean(param, true); + + this.m_logDigest = OptionsParameter.getParamValueAsBoolean(param, false); + + this.m_keyEncryptionAlgorithm = keyEncryptionAlgorithm; + + param = OptionsManager.getInstance().getParameter(OptionsParameter.CRYPTOGRAHPER_KEY_ENCRYPTION_DIGEST_ALGORITHM); + this.m_keyEncryptionDigestAlgorithm = (param == null) ? DigestAlgorithm.SHA256 : DigestAlgorithm.fromString(param.getParamValue(), DigestAlgorithm.SHA256); + + } + public SMIMECryptographerImpl(EncryptionAlgorithm encryptionAlgorithm, DigestAlgorithm digestAlgorithm, EncryptionAlgorithm keyEncryptionAlgorithm, DigestAlgorithm keyEncryptionDigestAlgorithm) + { + this.m_encryptionAlgorithm = encryptionAlgorithm; + this.m_digestAlgorithm = digestAlgorithm; + OptionsParameter param = OptionsManager.getInstance().getParameter(OptionsParameter.ENFORCE_STRONG_DIGESTS); this.enforceStrongDigests = OptionsParameter.getParamValueAsBoolean(param, true); - + if (!isAllowedDigestAlgorithm(m_digestAlgorithm.getOID())) - throw new IllegalArgumentException("The digest algorith " + m_digestAlgorithm.getAlgName() + " is not allowed"); - + throw new IllegalArgumentException("The digest algorith " + m_digestAlgorithm.getAlgName() + " is not allowed"); + param = OptionsManager.getInstance().getParameter(OptionsParameter.ENFORCE_STRONG_ENCRYPTION); this.enforceStrongEncryption = OptionsParameter.getParamValueAsBoolean(param, true); - - this.m_logDigest = OptionsParameter.getParamValueAsBoolean(param, false); + + this.m_logDigest = OptionsParameter.getParamValueAsBoolean(param, false); + + this.m_keyEncryptionAlgorithm = keyEncryptionAlgorithm; + + this.m_keyEncryptionDigestAlgorithm = keyEncryptionDigestAlgorithm; + } - /** * Gets the EncryptionAlgorithm. * @return The EncryptionAlgorithm used to encrypt messages. @@ -168,7 +238,25 @@ public EncryptionAlgorithm getEncryptionAlgorithm() { return this.m_encryptionAlgorithm; } - + + /** + * Gets the KeyEncryptionAlgorithm. + * @return The KeyEncryptionAlgorithm used to encrypt message key. + */ + public EncryptionAlgorithm getKeyEncryptionAlgorithm() + { + return this.m_keyEncryptionAlgorithm; + } + + /** + * Gets the KeyEncryptionDigestAlgorithm. + * @return The KeyEncryptionDigestAlgorithm used to encrypt message key. + */ + public DigestAlgorithm getKeyEncryptionDigestAlgorithm() + { + return this.m_keyEncryptionDigestAlgorithm; + } + /** * Sets the EncryptionAlgorithm * @param value The EncryptionAlgorithm used to encrypt messages. @@ -178,81 +266,99 @@ public void setEncryptionAlgorithm(EncryptionAlgorithm value) this.m_encryptionAlgorithm = value; } + /** + * Sets the KeyEncryptionAlgorithm + * @param value The KeyEncryptionAlgorithm used to encrypt message key. + */ + public void setKeyEncryptionAlgorithm(EncryptionAlgorithm value) + { + this.m_keyEncryptionAlgorithm = value; + } + + /** + * Sets the KeyEncryptionDigestAlgorithm + * @param value The KeyEncryptionAlgorithm used to encrypt message key. + */ + public void setKeyEncryptionDigestAlgorithm(DigestAlgorithm value) + { + this.m_keyEncryptionDigestAlgorithm = value; + } + /** * Gets the DigestAlgorithm. * @return The DigestAlgorithm used generate the message digest stored in the message signature. */ public DigestAlgorithm getDigestAlgorithm() { - return this.m_digestAlgorithm; + return this.m_digestAlgorithm; } - + /** * Sets the DigestAlgorithm. * @param value The DigestAlgorithm used generate the message digest stored in the message signature. - */ + */ public void setDigestAlgorithm(DigestAlgorithm value) { if (!isAllowedDigestAlgorithm(value.getOID())) - throw new IllegalArgumentException("The digest algorithm " + m_digestAlgorithm.getAlgName() + " is not allowed"); - + throw new IllegalArgumentException("The digest algorithm " + m_digestAlgorithm.getAlgName() + " is not allowed"); + this.m_digestAlgorithm = value; } - - /** + + /** * Indicates if message digests will be logged when verifying messages. - * @return True if the digests will be logged. False otherwise. + * @return True if the digests will be logged. False otherwise. */ public boolean isLogDigests() { - return this.m_logDigest; + return this.m_logDigest; } - + /** * Sets the option to enforce strong message digests. * @param value True if strong message digests are enforced. False otherwise. - */ + */ public void setStrongDigestEnforced(Boolean value) { this.enforceStrongDigests = value; } - + /** * Indicate if strong message digests are enforced * @return True if strong message digests are enforced. False otherwise. */ public boolean isStrongDigestEnforced() { - return this.enforceStrongDigests; + return this.enforceStrongDigests; } - - /** + + /** * Sets if message digests will be looged. * @param m_logDigest True if the digests will be logged. False otherwise. */ public void setLogDigests(boolean m_logDigest) { - this.m_logDigest = m_logDigest; + this.m_logDigest = m_logDigest; } - + /** * Sets the option to enforce strong message encryption * @param value True if strong encryption is enforced. False otherwise. - */ + */ public void setStrongEncryptionEnforced(Boolean value) { this.enforceStrongEncryption = value; } - + /** * Indicate if strong message encryption is enforced * @return True if strong encryption is enforced. False otherwise. */ public boolean isStrongEncryptionEnforced() { - return this.enforceStrongEncryption; + return this.enforceStrongEncryption; } - + /** * Indicates if the the Epilogue part of a multipart entity should be used to generate the message signature. * @return True if the the Epilogue part of a multipart entity should be used to generate the message signature. False otherwise. @@ -260,23 +366,23 @@ public boolean isStrongEncryptionEnforced() public boolean isIncludeMultipartEpilogueInSignature() { return this.m_includeEpilogue; - } - + } + /** * Sets if the the Epilogue part of a multipart entity should be used to generate the message signature. * @param value True if the the Epilogue part of a multipart entity should be used to generate the message signature. False otherwise. - */ + */ public void setIncludeMultipartEpilogueInSignature(boolean value) { this.m_includeEpilogue = value; } - + /* * Encryption */ - + /** - * + * * Encrypts a mulit part MIME entity using the provided certificate. * @param entity The entity that will be encrypted. * @param encryptingCertificate The public certificates that will be used to encrypt the message. @@ -284,12 +390,12 @@ public void setIncludeMultipartEpilogueInSignature(boolean value) */ public MimeEntity encrypt(MimeMultipart entity, X509Certificate encryptingCertificate) { - Collection certs = new ArrayList(); - certs.add(encryptingCertificate); - + Collection certs = new ArrayList(); + certs.add(encryptingCertificate); + return this.encrypt(entity, certs); - } - + } + /** * Encrypts a mulit part MIME entity using the provided certificates. * @param entity The entity that will be encrypted. @@ -297,30 +403,30 @@ public MimeEntity encrypt(MimeMultipart entity, X509Certificate encryptingCertif * @return A MimeEntity containing the encrypted part. */ @SuppressWarnings("deprecation") - public MimeEntity encrypt(MimeMultipart mmEntity, Collection encryptingCertificates) + public MimeEntity encrypt(MimeMultipart mmEntity, Collection encryptingCertificates) { - MimeEntity entToEncrypt = null; - - ByteArrayOutputStream oStream = new ByteArrayOutputStream(); - try - { - mmEntity.writeTo(oStream); - oStream.flush(); - InternetHeaders headers = new InternetHeaders(); - headers.addHeader(MimeStandard.ContentTypeHeader, mmEntity.getContentType()); - - - entToEncrypt = new MimeEntity(headers, oStream.toByteArray()); - IOUtils.closeQuietly(oStream); - } - catch (Exception e) - { - throw new MimeException(MimeError.InvalidMimeEntity, e); - } - - return this.encrypt(entToEncrypt, encryptingCertificates); - } - + MimeEntity entToEncrypt = null; + + ByteArrayOutputStream oStream = new ByteArrayOutputStream(); + try + { + mmEntity.writeTo(oStream); + oStream.flush(); + InternetHeaders headers = new InternetHeaders(); + headers.addHeader(MimeStandard.ContentTypeHeader, mmEntity.getContentType()); + + + entToEncrypt = new MimeEntity(headers, oStream.toByteArray()); + IOUtils.closeQuietly(oStream); + } + catch (Exception e) + { + throw new MimeException(MimeError.InvalidMimeEntity, e); + } + + return this.encrypt(entToEncrypt, encryptingCertificates); + } + /** * Encrypts an entity using the provided certificate. * @param entity The entity that will be encrypted. @@ -329,13 +435,13 @@ public MimeEntity encrypt(MimeMultipart mmEntity, Collection en */ public MimeEntity encrypt(MimeEntity entity, X509Certificate encryptingCertificate) { - Collection certs = new ArrayList(); - certs.add(encryptingCertificate); - + Collection certs = new ArrayList(); + certs.add(encryptingCertificate); + return this.encrypt(entity, certs); } - /** + /** * Encrypts an entity using the provided certificates. * @param entity The entity that will be encrypted. * @param encryptingCertificate The public certificates that will be used to encrypt the message. @@ -347,28 +453,28 @@ public MimeEntity encrypt(MimeEntity entity, Collection encryp { throw new IllegalArgumentException(); } - - MimeBodyPart partToEncrypt = entity; + + MimeBodyPart partToEncrypt = entity; MimeBodyPart encryptedPart = this.encrypt(partToEncrypt, encryptingCertificates); MimeEntity encryptedEntity = null; - + try { - byte[] encBytes = EntitySerializer.Default.serializeToBytes(encryptedPart); - ByteArrayInputStream inStream = new ByteArrayInputStream(EntitySerializer.Default.serializeToBytes(encryptedPart)); - encryptedEntity = new MimeEntity(inStream); - + byte[] encBytes = EntitySerializer.Default.serializeToBytes(encryptedPart); + ByteArrayInputStream inStream = new ByteArrayInputStream(EntitySerializer.Default.serializeToBytes(encryptedPart)); + encryptedEntity = new MimeEntity(inStream); + if (log.isDebugEnabled()) - { - writePostEncypt(encBytes); - } + { + writePostEncypt(encBytes); + } encryptedEntity.setHeader(MimeStandard.ContentTypeHeader, SMIMEStandard.EncryptedContentTypeHeaderValue); - + } catch (Exception e) { - throw new MimeException(MimeError.Unexpected, e); + throw new MimeException(MimeError.Unexpected, e); } return encryptedEntity; @@ -378,48 +484,96 @@ private MimeBodyPart encrypt(MimeBodyPart bodyPart, Collection { return this.createEncryptedEnvelope(bodyPart, encryptingCertificates); } - + private MimeBodyPart createEncryptedEnvelope(MimeBodyPart bodyPart, Collection encryptingCertificates) { if (bodyPart == null || encryptingCertificates == null || encryptingCertificates.size() == 0) { throw new IllegalArgumentException(); } - + if (log.isDebugEnabled()) - { - writePreEncypt(EntitySerializer.Default.serializeToBytes(bodyPart)); - } - + { + writePreEncypt(EntitySerializer.Default.serializeToBytes(bodyPart)); + } + final SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator(); - + if (!this.isAllowedKeyEncryptionAlgorithm(m_keyEncryptionAlgorithm.getOID())) + throw new NHINDException(MimeError.DisallowedEncryptionAlgorithm, "The key encryption algorithm " + m_keyEncryptionAlgorithm.algName + "("+ m_keyEncryptionAlgorithm.getOID() + + ") is not allowed"); + /* m_keyEncryptionDigestAlgorithm could be null if using RSA_PKCS1_V15 */ + if (m_keyEncryptionDigestAlgorithm != null && !this.isAllowedKeyEncryptionDigestAlgorithm(m_keyEncryptionDigestAlgorithm.getOID())) + throw new NHINDException(MimeError.DisallowedEncryptionAlgorithm, "The key encryption algorithm " + m_keyEncryptionDigestAlgorithm.algName + "("+ m_keyEncryptionDigestAlgorithm.getOID() + + ") is not allowed"); + MimeBodyPart retVal = null; - + try { for(X509Certificate cert : encryptingCertificates) { - // ensure the certificates key is allowed - if (isAllowedCertKey(cert)) - gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(cert).setProvider(CryptoExtensions.getJCEProviderName())); + // ensure the certificates key is allowed + if (isAllowedCertKey(cert)) { + if (log.isDebugEnabled()) { + log.debug("Encrypting: Encryption algorithm is " + this.m_encryptionAlgorithm.algName + "(" + m_encryptionAlgorithm.getOID().toString() + ")"); + log.debug("Encrypting: Key encryption algorithm is " + this.m_keyEncryptionAlgorithm.algName + "(" + this.m_keyEncryptionAlgorithm.getOID().toString() + ")"); + } + JcaAlgorithmParametersConverter paramsConverter = new JcaAlgorithmParametersConverter(); + if( m_keyEncryptionAlgorithm == EncryptionAlgorithm.RSA_OAEP) { + OAEPParameterSpec oaepSpec = null; + if( m_keyEncryptionDigestAlgorithm == DigestAlgorithm.SHA256){ + oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT); + } + if( m_keyEncryptionDigestAlgorithm == DigestAlgorithm.SHA384){ + oaepSpec = new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT); + } + if( m_keyEncryptionDigestAlgorithm == DigestAlgorithm.SHA512){ + oaepSpec = new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT); + } + if( m_keyEncryptionDigestAlgorithm == DigestAlgorithm.SHA1){ + oaepSpec = new OAEPParameterSpec("SHA-1", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); + } + + AlgorithmIdentifier algorithmIdentifier = paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, oaepSpec); + if (log.isDebugEnabled()) { + ASN1Encodable asn1Encodable = algorithmIdentifier.getParameters(); + RSAESOAEPparams rsaesoaePparams = (RSAESOAEPparams)asn1Encodable; + log.debug("Encrypting: Key encryption algorithm parameters: Hash Algorithm: " + rsaesoaePparams.getHashAlgorithm().getAlgorithm().getId() + " Mask Gen Algorithm: " + rsaesoaePparams.getMaskGenAlgorithm().getAlgorithm().getId() + " P Source Algorithm: " + rsaesoaePparams.getPSourceAlgorithm().getAlgorithm().getId()); + } + // JceKeyTransRecipientInfoGenerator has at least 2 constructors + // with just a cert as the arg, the algorithm defaults to new JceAsymmetricKeyWrapper(recipientCert) for the AsymmetricKeyWrapper, which is RSA PKCS15 + // you can also pass in the wrapper, RSA OAEP for example + JceKeyTransRecipientInfoGenerator jceKey = new JceKeyTransRecipientInfoGenerator(cert, algorithmIdentifier).setProvider(CryptoExtensions.getJCEProviderName()); + + // KeyEncryption set to RSA OAEP + gen.addRecipientInfoGenerator(jceKey); + } + if( m_keyEncryptionAlgorithm == EncryptionAlgorithm.RSA_PKCS1_V15) { + // Original version - KeyEncryption defaults to PKCS#1 v1.5 padding + gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(cert).setProvider(CryptoExtensions.getJCEProviderName())); + } + + + } } - - final ASN1ObjectIdentifier encryAlgOID = new ASN1ObjectIdentifier(this.m_encryptionAlgorithm.getOID()); - final OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(encryAlgOID). - setProvider(CryptoExtensions.getJCEProviderNameForTypeAndAlgorithm("Cipher", this.m_encryptionAlgorithm.getOID())).build(); - - retVal = gen.generate(bodyPart, encryptor); - - - //encryAlgOID, - //CryptoExtensions.getJCEProviderNameForTypeAndAlgorithm("Cipher", encryAlgOID)); + + final ASN1ObjectIdentifier encryAlgOID = new ASN1ObjectIdentifier(this.m_encryptionAlgorithm.getOID()); + final OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(encryAlgOID). + setProvider(CryptoExtensions.getJCEProviderNameForTypeAndAlgorithm("Cipher", this.m_encryptionAlgorithm.getOID())).build(); + + + retVal = gen.generate(bodyPart, encryptor); + + + //encryAlgOID, + //CryptoExtensions.getJCEProviderNameForTypeAndAlgorithm("Cipher", encryAlgOID)); } catch (Exception e) { - throw new MimeException(MimeError.Unexpected, e); + throw new MimeException(MimeError.Unexpected, e); } - + return retVal; } @@ -434,50 +588,50 @@ private MimeBodyPart createEncryptedEnvelope(MimeBodyPart bodyPart, Collection certs = new ArrayList(); - certs.add(decryptingCertificate); - + + Collection certs = new ArrayList(); + certs.add(decryptingCertificate); + MimeEntity retVal = this.decrypt(encryptedEntity, certs); - + // // And turn the decrypted bytes back into an entity // return retVal; } - + /** * Decrypts an entity with the provided certificates' private key. * @param encryptedEntity The entity that will be decrypted. * @param decryptingCertificate The certificates whose private keys will be used to decrypt the message. * @return A MimeEntity containing the decrypted part. - */ + */ public MimeEntity decrypt(MimeEntity encryptedEntity, Collection decryptingCertificates) { if (decryptingCertificates == null || decryptingCertificates.size() == 0) @@ -487,62 +641,89 @@ public MimeEntity decrypt(MimeEntity encryptedEntity, Collection signingCertificates) { return this.sign(message.extractEntityForSignature(this.m_includeEpilogue), signingCertificates); - } - + } + /** * Signs an entity with the provided certificate. * @param message The entity that will be signed. * @param signingCertificate The certificate used to sign the message. - * @return A signed entity that consists of a multipart/signed entity containing the original entity and a message signature. - */ - public SignedEntity sign(MimeEntity entity, X509Certificate signingCertificate) + * @return A signed entity that consists of a multipart/signed entity containing the original entity and a message signature. + */ + public SignedEntity sign(MimeEntity entity, X509Certificate signingCertificate) { - Collection certs = new ArrayList(); - certs.add(signingCertificate); - + Collection certs = new ArrayList(); + certs.add(signingCertificate); + return this.sign(entity, certs); } - + /** * Signs an entity with the provided certificates. * @param message The entity that will be signed. * @param signingCertificates The certificates used to sign the message. - * @return A signed entity that consists of a multipart/signed entity containing the original entity and a message signature. - */ + * @return A signed entity that consists of a multipart/signed entity containing the original entity and a message signature. + */ public SignedEntity sign(MimeEntity entity, Collection signingCertificates) { if (entity == null) @@ -616,124 +797,124 @@ public SignedEntity sign(MimeEntity entity, Collection signingC } byte[] messageBytes = EntitySerializer.Default.serializeToBytes(entity); // Serialize message out as ASCII encoded... - + MimeMultipart mm = this.createSignatureEntity(messageBytes, signingCertificates); SignedEntity retVal = null; - + try { - - retVal = new SignedEntity(new ContentType(mm.getContentType()), mm); + + retVal = new SignedEntity(new ContentType(mm.getContentType()), mm); } catch (ParseException e) { - throw new MimeException(MimeError.InvalidHeader, e); + throw new MimeException(MimeError.InvalidHeader, e); } - + return retVal; } protected MimeMultipart createSignatureEntity(byte[] entity, Collection signingCertificates) - { - MimeMultipart retVal = null; - try - { - final MimeBodyPart signedContent = new MimeBodyPart(new ByteArrayInputStream(entity)); - - final ASN1EncodableVector signedAttrs = new ASN1EncodableVector(); - final SMIMECapabilityVector caps = new SMIMECapabilityVector(); - - caps.addCapability(SMIMECapability.dES_EDE3_CBC); - caps.addCapability(SMIMECapability.rC2_CBC, 128); - caps.addCapability(SMIMECapability.dES_CBC); - caps.addCapability(new ASN1ObjectIdentifier("1.2.840.113549.1.7.1")); - caps.addCapability(x509CertificateObjectsIdent); - signedAttrs.add(new SMIMECapabilitiesAttribute(caps)); - - final List certList = new ArrayList(); - CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); - for (X509Certificate signer : signingCertificates) - { - if (signer instanceof X509CertificateEx && isAllowedCertKey(signer)) - { - ContentSigner digestSigner = new JcaContentSignerBuilder(this.m_digestAlgorithm.getAlgName()) - .setProvider(CryptoExtensions.getJCESensitiveProviderName()).build(((X509CertificateEx) signer).getPrivateKey()); - - - gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder() - .setProvider(CryptoExtensions.getJCEProviderName()).build()) - .build(digestSigner, signer)); - - certList.add(signer); - } - } - - final JcaCertStore certs = new JcaCertStore(certList); - gen.addCertificates(certs); - final CMSProcessableByteArray content = new CMSProcessableByteArray(entity); - - final CMSSignedData signedData = gen.generate(content); - - final String header = "signed; protocol=\"application/pkcs7-signature\"; micalg=" + - CryptoAlgorithmsHelper.toDigestAlgorithmMicalg(this.m_digestAlgorithm); - - final String encodedSig = StringUtils.toEncodedString(Base64.encodeBase64(signedData.getEncoded(), true), StandardCharsets.UTF_8); - - retVal = new MimeMultipart(header.toString()); - - final MimeBodyPart sig = new MimeBodyPart(new InternetHeaders(), encodedSig.getBytes("ASCII")); + { + MimeMultipart retVal = null; + try + { + final MimeBodyPart signedContent = new MimeBodyPart(new ByteArrayInputStream(entity)); + + final ASN1EncodableVector signedAttrs = new ASN1EncodableVector(); + final SMIMECapabilityVector caps = new SMIMECapabilityVector(); + + caps.addCapability(SMIMECapability.dES_EDE3_CBC); + caps.addCapability(SMIMECapability.rC2_CBC, 128); + caps.addCapability(SMIMECapability.dES_CBC); + caps.addCapability(new ASN1ObjectIdentifier("1.2.840.113549.1.7.1")); + caps.addCapability(x509CertificateObjectsIdent); + signedAttrs.add(new SMIMECapabilitiesAttribute(caps)); + + final List certList = new ArrayList(); + CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); + for (X509Certificate signer : signingCertificates) + { + if (signer instanceof X509CertificateEx && isAllowedCertKey(signer)) + { + ContentSigner digestSigner = new JcaContentSignerBuilder(this.m_digestAlgorithm.getAlgName()) + .setProvider(CryptoExtensions.getJCESensitiveProviderName()).build(((X509CertificateEx) signer).getPrivateKey()); + + + gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder() + .setProvider(CryptoExtensions.getJCEProviderName()).build()) + .build(digestSigner, signer)); + + certList.add(signer); + } + } + + final JcaCertStore certs = new JcaCertStore(certList); + gen.addCertificates(certs); + final CMSProcessableByteArray content = new CMSProcessableByteArray(entity); + + final CMSSignedData signedData = gen.generate(content); + + final String header = "signed; protocol=\"application/pkcs7-signature\"; micalg=" + + CryptoAlgorithmsHelper.toDigestAlgorithmMicalg(this.m_digestAlgorithm); + + final String encodedSig = StringUtils.toEncodedString(Base64.encodeBase64(signedData.getEncoded(), true), StandardCharsets.UTF_8); + + retVal = new MimeMultipart(header.toString()); + + final MimeBodyPart sig = new MimeBodyPart(new InternetHeaders(), encodedSig.getBytes("ASCII")); sig.addHeader("Content-Type", "application/pkcs7-signature; name=smime.p7s; smime-type=signed-data"); sig.addHeader("Content-Disposition", "attachment; filename=\"smime.p7s\""); sig.addHeader("Content-Description", "S/MIME Cryptographic Signature"); sig.addHeader("Content-Transfer-Encoding", "base64"); - + retVal.addBodyPart(signedContent); retVal.addBodyPart(sig); - } - catch (MessagingException e) - { - throw new MimeException(MimeError.InvalidMimeEntity, e); - } - catch (IOException e) - { - throw new SignatureException(SignatureError.InvalidMultipartSigned, e); - } - catch (Exception e) - { - throw new NHINDException(MimeError.Unexpected, e); - } - return retVal; - + } + catch (MessagingException e) + { + throw new MimeException(MimeError.InvalidMimeEntity, e); + } + catch (IOException e) + { + throw new SignatureException(SignatureError.InvalidMultipartSigned, e); + } + catch (Exception e) + { + throw new NHINDException(MimeError.Unexpected, e); + } + return retVal; + } - + /* * Construct an attribute table. Added private function to support multiple versions of BC libraries. */ public static AttributeTable createAttributeTable(ASN1EncodableVector signedAttrs) { - // Support for BC 146.... has a different constructor signature from 140 - - AttributeTable retVal = null; - - - if (retVal == null) - { - try - { - /* - * 146 version - */ - Constructor constr = AttributeTable.class.getConstructor(ASN1EncodableVector.class); - retVal = constr.newInstance(signedAttrs); - } - catch (Throwable t) - { - log.error("Attempt to use to bcmail-jdk15-146 DERObjectIdentifier(ASN1EncodableVector constructor failed.", t); - } - } - - return retVal; + // Support for BC 146.... has a different constructor signature from 140 + + AttributeTable retVal = null; + + + if (retVal == null) + { + try + { + /* + * 146 version + */ + Constructor constr = AttributeTable.class.getConstructor(ASN1EncodableVector.class); + retVal = constr.newInstance(signedAttrs); + } + catch (Throwable t) + { + log.error("Attempt to use to bcmail-jdk15-146 DERObjectIdentifier(ASN1EncodableVector constructor failed.", t); + } + } + + return retVal; } //----------------------------------------------------- @@ -750,52 +931,52 @@ public static AttributeTable createAttributeTable(ASN1EncodableVector signedAttr */ public void checkSignature(SignedEntity signedEntity, X509Certificate signerCertificate, Collection anchors) throws SignatureValidationException { - if (!isAllowedCertKey(signerCertificate)) - throw new SignatureValidationException("Signing certificate key size/strength is not allowed"); - - CMSSignedData signatureEnvelope = deserializeSignatureEnvelope(signedEntity); - + if (!isAllowedCertKey(signerCertificate)) + throw new SignatureValidationException("Signing certificate key size/strength is not allowed"); + + CMSSignedData signatureEnvelope = deserializeSignatureEnvelope(signedEntity); + SignerInformation logSigInfo = null; - try - { - // there may be multiple signatures in the signed part... iterate through all the signing certificates until one - // is verified with the signerCertificate - for (SignerInformation sigInfo : (Collection)signatureEnvelope.getSignerInfos().getSigners()) - { - logSigInfo = sigInfo; - // make sure the sender did not send the message with an explicitly disallowed digest algorithm - // such as MD5 - - if (!isAllowedDigestAlgorithm(sigInfo.getDigestAlgOID())) - throw new SignatureValidationException("Digest algorithm " + sigInfo.getDigestAlgOID() + " is not allowed."); - - if (sigInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(CryptoExtensions.getJCEProviderName()).build(signerCertificate))) - { - - return; // verified... return - } - - } - // at this point the signerCertificate cannot be verified with one of the signing certificates.... - throw new SignatureValidationException("Signature validation failure."); - } - catch (SignatureValidationException sve) - { - throw sve; - } - catch (Exception e) - { - throw new SignatureValidationException("Signature validation failure.", e); - } - finally - { - logDigests(logSigInfo); - } - } - + try + { + // there may be multiple signatures in the signed part... iterate through all the signing certificates until one + // is verified with the signerCertificate + for (SignerInformation sigInfo : (Collection)signatureEnvelope.getSignerInfos().getSigners()) + { + logSigInfo = sigInfo; + // make sure the sender did not send the message with an explicitly disallowed digest algorithm + // such as MD5 + + if (!isAllowedDigestAlgorithm(sigInfo.getDigestAlgOID())) + throw new SignatureValidationException("Digest algorithm " + sigInfo.getDigestAlgOID() + " is not allowed."); + + if (sigInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(CryptoExtensions.getJCEProviderName()).build(signerCertificate))) + { + + return; // verified... return + } + + } + // at this point the signerCertificate cannot be verified with one of the signing certificates.... + throw new SignatureValidationException("Signature validation failure."); + } + catch (SignatureValidationException sve) + { + throw sve; + } + catch (Exception e) + { + throw new SignatureValidationException("Signature validation failure.", e); + } + finally + { + logDigests(logSigInfo); + } + } + /** * Determines if a specific digest algorithm is allowed by policy and in conformance with the applicability statement. - * This check can be turned off with the ENFORCE_STRONG_DIGESTS options parameter. + * This check can be turned off with the ENFORCE_STRONG_DIGESTS options parameter. * As of ANSI/DS 2019-01-100-2021 approved on May 13, 2021, certain digests are explicitly disallowed. * The ENFORCE_STRONG_DIGESTS options parameter is only relevant of optional or digests listed as SHOULD NOT * be used. This parameter DOES NOT apply to explicitly disallowed algorithms. @@ -804,16 +985,16 @@ public void checkSignature(SignedEntity signedEntity, X509Certificate signerCert protected boolean isAllowedDigestAlgorithm(String digestOID) { - /* - * Dis-allow MD5 explicitly and SHA1 - * may include other algorithms in further implementations - */ - return (digestOID.equalsIgnoreCase(DigestAlgorithm.MD5.getOID()) || digestOID.equalsIgnoreCase(DigestAlgorithm.SHA1.getOID()) || - digestOID.equalsIgnoreCase(DigestAlgorithm.SHA1WITHRSA.getOID())) ? false: true; - + /* + * Dis-allow MD5 explicitly and SHA1 + * may include other algorithms in further implementations + */ + return (digestOID.equalsIgnoreCase(DigestAlgorithm.MD5.getOID()) || digestOID.equalsIgnoreCase(DigestAlgorithm.SHA1.getOID()) || + digestOID.equalsIgnoreCase(DigestAlgorithm.SHA1WITHRSA.getOID())) ? false: true; + } - + /** * Determines if a specific encryption algorithm is allowed by policy and in conformance with the applicability statement. * This check can be turned off with the ENFORCE_STRONG_ENCRYPTION options parameter. @@ -821,16 +1002,54 @@ protected boolean isAllowedDigestAlgorithm(String digestOID) */ protected boolean isAllowedEncryptionAlgorithm(String encryptionOID) { - if (!this.isStrongEncryptionEnforced()) - return true; - - /* - * Dis-allow those algorithms explicitly outlined as SHOULD- if section 2.7 of RFC5751 - * may include other algorithms in further implementations - */ - return encryptionOID.equalsIgnoreCase(EncryptionAlgorithm.DES_EDE3_CBC.getOID()) ? false : true; + if (!this.isStrongEncryptionEnforced()) + return true; + + /* + * Dis-allow those algorithms explicitly outlined as SHOULD- if section 2.7 of RFC5751 + * may include other algorithms in further implementations + */ + return encryptionOID.equalsIgnoreCase(EncryptionAlgorithm.DES_EDE3_CBC.getOID()) ? false : true; + } + /** + * Determines if a specific key encryption algorithm is allowed by policy and in conformance with the applicability statement. + * This check can be turned off with the ENFORCE_STRONG_ENCRYPTION options parameter. + * @return + */ + protected boolean isAllowedKeyEncryptionAlgorithm(String encryptionOID) + { + if (!this.isStrongEncryptionEnforced()) + return true; + + /* + * Dis-allow those algorithms explicitly deprecated as of NIST 800-56B + * may include other algorithms in further implementations + */ + /* Allow RSA_PKCS1_V15 for now */ + //return encryptionOID.equalsIgnoreCase(EncryptionAlgorithm.RSA_PKCS1_V15.getOID()) ? false : true; + return true; } - + + /** + * Determines if a specific key encryption digest algorithm is allowed by policy and in conformance with the applicability statement. + * This check can be turned off with the ENFORCE_STRONG_ENCRYPTION options parameter. + * @return + */ + protected boolean isAllowedKeyEncryptionDigestAlgorithm(String digestOID) + { + if (!this.isStrongEncryptionEnforced()) + return true; + + /* + * Dis-allow those algorithms explicitly deprecated as of NIST 800-56B + * may include other algorithms in further implementations + */ + return true; + // allow SHA1 for now + //return digestOID.equalsIgnoreCase(DigestAlgorithm.SHA1.getOID()) ? false : true; + } + + /** * Determines if a certificate has a key of acceptable size/strength. * RSA keys MUST be at 2048 bits in length. @@ -839,43 +1058,43 @@ protected boolean isAllowedEncryptionAlgorithm(String encryptionOID) */ protected boolean isAllowedCertKey(X509Certificate cert) { - // Check if it's an RSA key - if (cert.getPublicKey().getAlgorithm().contains("RSA")) - { - final RSAPublicKey rsaPk = (RSAPublicKey) cert.getPublicKey(); - return rsaPk.getModulus().bitLength() >= 2048; - } - - return true; + // Check if it's an RSA key + if (cert.getPublicKey().getAlgorithm().contains("RSA")) + { + final RSAPublicKey rsaPk = (RSAPublicKey) cert.getPublicKey(); + return rsaPk.getModulus().bitLength() >= 2048; + } + + return true; } - - protected void logDigests(SignerInformation sigInfo) + + protected void logDigests(SignerInformation sigInfo) { - // it is assumed that the verify function has already been called, other wise the getContentDigest function - // will fail - if (this.m_logDigest && sigInfo != null) - { - try - { - //get the digests - final Attribute digAttr = sigInfo.getSignedAttributes().get(CMSAttributes.messageDigest); - final ASN1Encodable hashObj = digAttr.getAttrValues().getObjectAt(0); - final byte[] signedDigest = ((ASN1OctetString)hashObj).getOctets(); - final String signedDigestHex = org.apache.commons.codec.binary.Hex.encodeHexString(signedDigest); - - log.info("Signed Message Digest: {}", signedDigestHex); - - // should have the computed digest now - final byte[] digest = sigInfo.getContentDigest(); - final String digestHex = org.apache.commons.codec.binary.Hex.encodeHexString(digest); - log.info("Computed Message Digest: {}", digestHex); - } - catch (Throwable t) - { /* no-op.... logging digests is a quiet operation */} - } + // it is assumed that the verify function has already been called, other wise the getContentDigest function + // will fail + if (this.m_logDigest && sigInfo != null) + { + try + { + //get the digests + final Attribute digAttr = sigInfo.getSignedAttributes().get(CMSAttributes.messageDigest); + final ASN1Encodable hashObj = digAttr.getAttrValues().getObjectAt(0); + final byte[] signedDigest = ((ASN1OctetString)hashObj).getOctets(); + final String signedDigestHex = org.apache.commons.codec.binary.Hex.encodeHexString(signedDigest); + + log.info("Signed Message Digest: {}", signedDigestHex); + + // should have the computed digest now + final byte[] digest = sigInfo.getContentDigest(); + final String digestHex = org.apache.commons.codec.binary.Hex.encodeHexString(digest); + log.info("Computed Message Digest: {}", digestHex); + } + catch (Throwable t) + { /* no-op.... logging digests is a quiet operation */} + } } - - + + /** * Extracts the ASN1 encoded signature data from the signed entity. * @param entity The entity containing the original signed part and the message signature. @@ -884,35 +1103,35 @@ protected void logDigests(SignerInformation sigInfo) public CMSSignedData deserializeSignatureEnvelope(SignedEntity entity) { - - if (entity == null) + + if (entity == null) { throw new NHINDException(); } - CMSSignedData signed = null; - - try - { - //signed = new SMIMESigned(entity.getMimeMultipart()); - byte[] messageBytes = EntitySerializer.Default.serializeToBytes(entity.getContent()); + CMSSignedData signed = null; + + try + { + //signed = new SMIMESigned(entity.getMimeMultipart()); + byte[] messageBytes = EntitySerializer.Default.serializeToBytes(entity.getContent()); MimeBodyPart signedContent = null; - - signedContent = new MimeBodyPart(new ByteArrayInputStream(messageBytes)); - - signed = new CMSSignedData(new CMSProcessableBodyPart(signedContent), entity.getMimeMultipart().getBodyPart(1).getInputStream()); - - } - catch (Exception e) - { - e.printStackTrace(); - throw new MimeException(MimeError.Unexpected, e); - } - - return signed; + + signedContent = new MimeBodyPart(new ByteArrayInputStream(messageBytes)); + + signed = new CMSSignedData(new CMSProcessableBodyPart(signedContent), entity.getMimeMultipart().getBodyPart(1).getInputStream()); + + } + catch (Exception e) + { + e.printStackTrace(); + throw new MimeException(MimeError.Unexpected, e); + } + + return signed; } - + public CMSSignedData deserializeEnvelopedSignature(MimeEntity envelopeEntity) { if (envelopeEntity == null) @@ -932,165 +1151,171 @@ public CMSSignedData deserializeEnvelopedSignature(MimeEntity envelopeEntity) public CMSSignedData deserializeEnvelopedSignature(byte[] messageBytes) { - CMSSignedData signed = null; - - try - { - signed = new CMSSignedData(messageBytes); - } - catch (Exception e) - { - e.printStackTrace(); - throw new MimeException(MimeError.Unexpected, e); - } - - return signed; - } - - + CMSSignedData signed = null; + + try + { + signed = new CMSSignedData(messageBytes); + } + catch (Exception e) + { + e.printStackTrace(); + throw new MimeException(MimeError.Unexpected, e); + } + + return signed; + } + + @SuppressWarnings("deprecation") - private void writePreEncypt(byte message[]) + private void writePreEncypt(byte message[]) { - String path = System.getProperty("user.dir") + "/tmp"; - File tmpDir = new File(path); - - if (!tmpDir.exists()) - { - if (!tmpDir.mkdir()) - return; - } - - System.currentTimeMillis(); - - File outFile = new File(path + "/preEncypt_" + System.currentTimeMillis() + ".eml"); - - - try - { - if (!outFile.exists()) - { - if (!outFile.createNewFile()) - return; - } - BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(outFile)); - - oStream.write(message, 0, message.length); - oStream.flush(); - IOUtils.closeQuietly(oStream); - } - catch (Exception e) - { - e.printStackTrace(); - } + String path = System.getProperty("user.dir") + "/tmp"; + File tmpDir = new File(path); + + if (!tmpDir.exists()) + { + if (!tmpDir.mkdir()) + return; + } + + System.currentTimeMillis(); + + File outFile = new File(path + "/preEncypt_" + System.currentTimeMillis() + ".eml"); + + + try + { + if (!outFile.exists()) + { + if (!outFile.createNewFile()) + return; + } + BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(outFile)); + + oStream.write(message, 0, message.length); + oStream.flush(); + IOUtils.closeQuietly(oStream); + } + catch (Exception e) + { + e.printStackTrace(); + } } - + @SuppressWarnings("deprecation") - private void writePostEncypt(byte message[]) + private void writePostEncypt(byte message[]) { - String path = System.getProperty("user.dir") + "/tmp"; - File tmpDir = new File(path); - - if (!tmpDir.exists()) - { - if (!tmpDir.mkdir()) - return; - } - - System.currentTimeMillis(); - - File outFile = new File(path + "/postEncypt_" + System.currentTimeMillis() + ".eml"); - - - try - { - if (!outFile.exists()) - { - if (!outFile.createNewFile()) - return; - } - BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(outFile)); - - oStream.write(message, 0, message.length); - oStream.flush(); - IOUtils.closeQuietly(oStream); - } - catch (Exception e) - { - e.printStackTrace(); - } + String path = System.getProperty("user.dir") + "/tmp"; + File tmpDir = new File(path); + + if (!tmpDir.exists()) + { + if (!tmpDir.mkdir()) + return; + } + + System.currentTimeMillis(); + + File outFile = new File(path + "/postEncypt_" + System.currentTimeMillis() + ".eml"); + + + try + { + if (!outFile.exists()) + { + if (!outFile.createNewFile()) + return; + } + BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(outFile)); + + oStream.write(message, 0, message.length); + oStream.flush(); + IOUtils.closeQuietly(oStream); + } + catch (Exception e) + { + e.printStackTrace(); + } } - - + + @SuppressWarnings("deprecation") - private void writePreDecrypt(byte message[]) + private void writePreDecrypt(byte message[]) { - String path = System.getProperty("user.dir") + "/tmp"; - File tmpDir = new File(path); - - if (!tmpDir.exists()) - { - if (!tmpDir.mkdir()) - return; - } - - System.currentTimeMillis(); - - File outFile = new File(path + "/preDecrypt_" + System.currentTimeMillis() + ".eml"); - - - try - { - if (!outFile.exists()) - { - if (!outFile.createNewFile()) - return; - } - BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(outFile)); - - oStream.write(message, 0, message.length); - oStream.flush(); - IOUtils.closeQuietly(oStream); - } - catch (Exception e) - { - e.printStackTrace(); - } + String path = System.getProperty("user.dir") + "/tmp"; + File tmpDir = new File(path); + + if (!tmpDir.exists()) + { + if (!tmpDir.mkdir()) + return; + } + + System.currentTimeMillis(); + + File outFile = new File(path + "/preDecrypt_" + System.currentTimeMillis() + ".eml"); + + + try + { + if (!outFile.exists()) + { + if (!outFile.createNewFile()) + return; + } + BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(outFile)); + + oStream.write(message, 0, message.length); + oStream.flush(); + IOUtils.closeQuietly(oStream); + } + catch (Exception e) + { + e.printStackTrace(); + } } - + @SuppressWarnings("deprecation") - private void writePostDecrypt(byte message[]) + private void writePostDecrypt(byte message[]) { - String path = System.getProperty("user.dir") + "/tmp"; - File tmpDir = new File(path); - - if (!tmpDir.exists()) - { - if (!tmpDir.mkdir()) - return; - } - - System.currentTimeMillis(); - - File outFile = new File(path + "/postDecrypt_" + System.currentTimeMillis() + ".eml"); - - - try - { - if (!outFile.exists()) - { - if (!outFile.createNewFile()) - return; - - } - BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(outFile)); - - oStream.write(message, 0, message.length); - oStream.flush(); - IOUtils.closeQuietly(oStream); - } - catch (Exception e) - { - e.printStackTrace(); - } + String path = System.getProperty("user.dir") + "/tmp"; + File tmpDir = new File(path); + + if (!tmpDir.exists()) + { + if (!tmpDir.mkdir()) + return; + } + + System.currentTimeMillis(); + + File outFile = new File(path + "/postDecrypt_" + System.currentTimeMillis() + ".eml"); + + + try + { + if (!outFile.exists()) + { + if (!outFile.createNewFile()) + return; + + } + BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(outFile)); + + oStream.write(message, 0, message.length); + oStream.flush(); + IOUtils.closeQuietly(oStream); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + public static String getMaskFunctionGeneratorFromOID(String mfgOID){ + if( mfgOID.equals(PKCSObjectIdentifiers.id_mgf1.getId())) + return "MGF1"; + + return "Unknown MFG: " + mfgOID; } -} +} \ No newline at end of file diff --git a/src/main/java/org/nhindirect/stagent/cryptography/bc/DirectJceAsymmetricKeyUnwrapper.java b/src/main/java/org/nhindirect/stagent/cryptography/bc/DirectJceAsymmetricKeyUnwrapper.java index 9a27fa7..bb1923a 100644 --- a/src/main/java/org/nhindirect/stagent/cryptography/bc/DirectJceAsymmetricKeyUnwrapper.java +++ b/src/main/java/org/nhindirect/stagent/cryptography/bc/DirectJceAsymmetricKeyUnwrapper.java @@ -2,6 +2,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.security.AlgorithmParameters; import java.security.Key; import java.security.PrivateKey; import java.util.HashMap; @@ -11,7 +12,9 @@ import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.operator.DefaultAlgorithmNameFinder; import org.bouncycastle.operator.GenericKey; import org.bouncycastle.operator.OperatorException; import org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper; @@ -20,60 +23,69 @@ public class DirectJceAsymmetricKeyUnwrapper extends JceAsymmetricKeyUnwrapper { protected Map extraMappings = new HashMap<>(); - protected PrivateKey privateKey; - - + protected PrivateKey privateKey; + + public DirectJceAsymmetricKeyUnwrapper(AlgorithmIdentifier algorithmIdentifier, PrivateKey privKey) { super(algorithmIdentifier, privKey); this.privateKey = privKey; } - + @Override public JceAsymmetricKeyUnwrapper setAlgorithmMapping(ASN1ObjectIdentifier algorithm, String algorithmName) { - super.setAlgorithmMapping(algorithm, algorithmName); - + super.setAlgorithmMapping(algorithm, algorithmName); + extraMappings.put(algorithm, algorithmName); return this; } - + @Override public GenericKey generateUnwrappedKey(AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedKey) throws OperatorException + { + try { - try - { - Key sKey = null; + Key sKey = null; - Class parentClass = Class.forName("org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper"); - Field helpField = parentClass.getDeclaredField("helper"); - helpField.setAccessible(true); - - Class helperClazz = Class.forName("org.bouncycastle.operator.jcajce.OperatorHelper"); + Class parentClass = Class.forName("org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper"); + Field helpField = parentClass.getDeclaredField("helper"); + helpField.setAccessible(true); - Method cipherMeth = helperClazz.getDeclaredMethod("createAsymmetricWrapper", ASN1ObjectIdentifier.class, Map.class); - cipherMeth.setAccessible(true); - - Cipher keyCipher = (Cipher)cipherMeth.invoke(helpField.get(this), this.getAlgorithmIdentifier().getAlgorithm(), extraMappings); + Class helperClazz = Class.forName("org.bouncycastle.operator.jcajce.OperatorHelper"); + Method cipherMeth = helperClazz.getDeclaredMethod("createAsymmetricWrapper", ASN1ObjectIdentifier.class, Map.class); + cipherMeth.setAccessible(true); - // some providers do not support UNWRAP (this appears to be only for asymmetric algorithms) - if (sKey == null) - { - keyCipher.init(Cipher.DECRYPT_MODE, privateKey); - sKey = new SecretKeySpec(keyCipher.doFinal(encryptedKey), encryptedKeyAlgorithm.getAlgorithm().getId()); - } + Cipher keyCipher = (Cipher)cipherMeth.invoke(helpField.get(this), this.getAlgorithmIdentifier().getAlgorithm(), extraMappings); - return new JceGenericKey(encryptedKeyAlgorithm, sKey); - } - catch (Exception e) + + // some providers do not support UNWRAP (this appears to be only for asymmetric algorithms) + if (sKey == null) { - throw new OperatorException("Decrypt failed: " + e.getMessage(), e); + + if( this.getAlgorithmIdentifier().getAlgorithm().getId().equals(PKCSObjectIdentifiers.id_RSAES_OAEP.toString())) { + // Get the SHA Digest from the algorithm identifier + AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(this.getAlgorithmIdentifier().getAlgorithm().toString()); + algorithmParameters.init(this.getAlgorithmIdentifier().getParameters().toASN1Primitive().getEncoded()); + keyCipher.init(Cipher.DECRYPT_MODE, privateKey, algorithmParameters); + } else { + keyCipher.init(Cipher.DECRYPT_MODE, privateKey); + } + byte[] var1 = keyCipher.doFinal(encryptedKey); // emm + sKey = new SecretKeySpec(keyCipher.doFinal(encryptedKey), encryptedKeyAlgorithm.getAlgorithm().getId()); } - } - + return new JceGenericKey(encryptedKeyAlgorithm, sKey); + } + catch (Exception e) + { + throw new OperatorException("Decrypt failed: " + e.getMessage(), e); + } + + } + -} +} \ No newline at end of file diff --git a/src/test/java/org/nhindirect/stagent/cryptography/CryptographerTest.java b/src/test/java/org/nhindirect/stagent/cryptography/CryptographerTest.java index 899db5a..0b1ba50 100644 --- a/src/test/java/org/nhindirect/stagent/cryptography/CryptographerTest.java +++ b/src/test/java/org/nhindirect/stagent/cryptography/CryptographerTest.java @@ -1,3 +1,4 @@ + package org.nhindirect.stagent.cryptography; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -6,6 +7,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; @@ -46,65 +48,65 @@ import org.nhindirect.stagent.utils.TestUtils; public class CryptographerTest -{ +{ @BeforeEach public void setUp() { - CryptoExtensions.registerJCEProviders(); + CryptoExtensions.registerJCEProviders(); } - + @Test public void testEncryptAndDecryptMimeEntityAES128() throws Exception { testEncryptAndDecryptMimeEntity(EncryptionAlgorithm.AES128, true, false); } - + @Test public void testEncryptAndDecryptMimeEntityAES256() throws Exception { testEncryptAndDecryptMimeEntity(EncryptionAlgorithm.AES256, true, false); - } - + } + @Test public void testEncryptAndDecryptMimeEntityRSA_3DES() throws Exception { testEncryptAndDecryptMimeEntity(EncryptionAlgorithm.RSA_3DES, false, false); - } - + } + @Test public void testEncryptAndDecryptMimeEntityAES192() throws Exception { testEncryptAndDecryptMimeEntity(EncryptionAlgorithm.AES192, true, false); - } + } @Test public void testEncryptAndDecryptMimeEntityRSA_3DES_enforceStrongEncr_assertException() throws Exception { testEncryptAndDecryptMimeEntity(EncryptionAlgorithm.RSA_3DES, true, true); - } - + } + @Test public void testEncryptAndDecryptMimeEntityDefaultAlg() throws Exception { testEncryptAndDecryptMimeEntity(null, true, false); - } - + } + private void testEncryptAndDecryptMimeEntity(EncryptionAlgorithm encAlg, boolean enforceStrongEncryption, boolean expectDecException) throws Exception { X509Certificate cert = TestUtils.getExternalCert("user1"); - + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); if (encAlg != null) cryptographer.setEncryptionAlgorithm(encAlg); cryptographer.setStrongEncryptionEnforced(enforceStrongEncryption); - - + + MimeEntity entity = new MimeEntity(); entity.setText("Hello world."); entity.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); entity.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); - - + + MimeEntity encEntity = cryptographer.encrypt(entity, cert); assertNotNull(encEntity); /* @@ -114,9 +116,9 @@ private void testEncryptAndDecryptMimeEntity(EncryptionAlgorithm encAlg, boolean final ContentType type = new ContentType(encEntity.getContentType()); assertTrue(type.match(SMIMEStandard.CmsEnvelopeMediaType)); assertFalse(type.match(SMIMEStandard.CmsEnvelopeMediaTypeAlt)); - + X509CertificateEx certex = TestUtils.getInternalCert("user1"); - + if (expectDecException) { boolean exceptionOccured = false; @@ -133,71 +135,71 @@ private void testEncryptAndDecryptMimeEntity(EncryptionAlgorithm encAlg, boolean else { MimeEntity decryEntity = cryptographer.decrypt(encEntity, certex); - + assertNotNull(decryEntity); - + byte[] decryEntityBytes = EntitySerializer.Default.serializeToBytes(decryEntity); byte[] entityBytes = EntitySerializer.Default.serializeToBytes(entity); - + assertTrue(Arrays.equals(decryEntityBytes, entityBytes)); } } - + protected String pkcs11ProviderName; - + @Test public void testEncryptAndDecryptMimeEntity_hsmDecryption() throws Exception { pkcs11ProviderName = TestUtils.setupSafeNetToken(); if (!StringUtils.isEmpty(pkcs11ProviderName)) testEncryptAndDecryptMimeEntity_hsmDecryption(EncryptionAlgorithm.AES128); - } - + } + private void testEncryptAndDecryptMimeEntity_hsmDecryption(EncryptionAlgorithm encAlg) throws Exception { - + OptionsManager.destroyInstance(); - + CryptoExtensions.registerJCEProviders(); - + try { final PKCS11Credential cred = new BootstrappedPKCS11Credential("1Kingpuff"); final MutableKeyStoreProtectionManager mgr = new StaticPKCS11TokenKeyStoreProtectionManager(cred, "", ""); final CacheableKeyStoreManagerCertificateStore store = new CacheableKeyStoreManagerCertificateStore(mgr); store.add(TestUtils.getInternalCert("user1")); - + X509Certificate cert = TestUtils.getExternalCert("user1"); - + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); - + cryptographer.setEncryptionAlgorithm(encAlg); - + MimeEntity entity = new MimeEntity(); entity.setText("Hello world."); entity.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); entity.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); - + MimeEntity encEntity = cryptographer.encrypt(entity, cert); - + assertNotNull(encEntity); - + // open up the pkcs11 store and find the private key KeyStore ks = KeyStore.getInstance("PKCS11"); - ks.load(null, "1Kingpuff".toCharArray()); - + ks.load(null, "1Kingpuff".toCharArray()); + X509CertificateEx decryptCert = null; - + final Enumeration aliases = ks.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); - + Certificate pkcs11Cert = ks.getCertificate(alias); if (pkcs11Cert != null &&pkcs11Cert instanceof X509Certificate) { - + // check if there is private key Key key = ks.getKey(alias, null); if (key != null && key instanceof PrivateKey && CryptoExtensions.certSubjectContainsName((X509Certificate)pkcs11Cert, "user1@cerner.com")) @@ -206,221 +208,221 @@ private void testEncryptAndDecryptMimeEntity_hsmDecryption(EncryptionAlgorithm e break; } } - } - + } + MimeEntity decryEntity = cryptographer.decrypt(encEntity, decryptCert); - + assertNotNull(decryEntity); - + byte[] decryEntityBytes = EntitySerializer.Default.serializeToBytes(decryEntity); byte[] entityBytes = EntitySerializer.Default.serializeToBytes(entity); - + assertTrue(Arrays.equals(decryEntityBytes, entityBytes)); - } + } finally { System.setProperty("org.nhindirect.stagent.cryptography.JCESensitiveProviderName", ""); System.setProperty("org.nhindirect.stagent.cryptography.JCESensitiveProviderClassNames", ""); - + OptionsManager.destroyInstance(); } } - + @Test public void testEncryptAndDecryptMultipartEntityAES128() throws Exception { testEncryptAndDecryptMultipartEntity(EncryptionAlgorithm.AES128, true); } - + @Test public void testEncryptAndDecryptMultipartEntityAES192() throws Exception { testEncryptAndDecryptMultipartEntity(EncryptionAlgorithm.AES192, true); } - + @Test public void testEncryptAndDecryptMultipartEntityAES256() throws Exception { testEncryptAndDecryptMultipartEntity(EncryptionAlgorithm.AES256, true); - } - + } + @Test public void testEncryptAndDecryptMultipartEntityRSA_3DES() throws Exception { testEncryptAndDecryptMultipartEntity(EncryptionAlgorithm.RSA_3DES, false); } - + private void testEncryptAndDecryptMultipartEntity(EncryptionAlgorithm encAlgo, boolean enforceStrongEncryption) throws Exception - { + { X509Certificate cert = TestUtils.getExternalCert("user1"); - + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); cryptographer.setEncryptionAlgorithm(encAlgo); cryptographer.setStrongEncryptionEnforced(enforceStrongEncryption); - + MimeEntity entityText = new MimeEntity(); entityText.setText("Hello world."); entityText.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); entityText.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); - + MimeEntity entityXML = new MimeEntity(); entityXML.setText(""); - entityXML.setHeader(MimeStandard.ContentTypeHeader, "text/xml"); - + entityXML.setHeader(MimeStandard.ContentTypeHeader, "text/xml"); + MimeMultipart mpEntity = new MimeMultipart(); - + mpEntity.addBodyPart(entityText); mpEntity.addBodyPart(entityXML); - + MimeEntity encEntity = cryptographer.encrypt(mpEntity, cert); - + assertNotNull(encEntity); - + X509CertificateEx certex = TestUtils.getInternalCert("user1"); - + MimeEntity decryEntity = cryptographer.decrypt(encEntity, certex); - + assertNotNull(decryEntity); - + ByteArrayOutputStream oStream = new ByteArrayOutputStream(); mpEntity.writeTo(oStream); InternetHeaders hdrs = new InternetHeaders(); hdrs.addHeader(MimeStandard.ContentTypeHeader, mpEntity.getContentType()); MimeEntity orgEntity = new MimeEntity(hdrs, oStream.toByteArray()); - + byte[] decryEntityBytes = EntitySerializer.Default.serializeToBytes(decryEntity); byte[] entityBytes = EntitySerializer.Default.serializeToBytes(orgEntity); System.out.println("Original:\r\n" + new String(entityBytes)); - System.out.println("\r\n\r\n\r\nNew:\r\n" + new String(decryEntityBytes)); - - - assertTrue(Arrays.equals(decryEntityBytes, entityBytes)); - - - } - + System.out.println("\r\n\r\n\r\nNew:\r\n" + new String(decryEntityBytes)); + + + assertTrue(Arrays.equals(decryEntityBytes, entityBytes)); + + + } + @Test public void testSignMimeEntitySHA256() throws Exception { testSignMimeEntity(DigestAlgorithm.SHA256WITHRSA); - } - + } + @Test public void testSignMimeEntitySHA384() throws Exception { testSignMimeEntity(DigestAlgorithm.SHA384WITHRSA); - } - + } + @Test public void testSignMimeEntitySHA512() throws Exception { testSignMimeEntity(DigestAlgorithm.SHA512WITHRSA); - } - + } + private void testSignMimeEntity(DigestAlgorithm digAlg) throws Exception - { + { X509CertificateEx certex = TestUtils.getInternalCert("user1"); - + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); cryptographer.setDigestAlgorithm(digAlg); - + MimeEntity entity = new MimeEntity(); entity.setText("Hello world."); entity.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); entity.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); - + SignedEntity signedEnt = cryptographer.sign(entity, certex); - + assertNotNull(signedEnt); - + byte[] signedEntityBytes = EntitySerializer.Default.serializeToBytes(signedEnt.getContent()); - byte[] entityBytes = EntitySerializer.Default.serializeToBytes(entity); - + byte[] entityBytes = EntitySerializer.Default.serializeToBytes(entity); + assertTrue(Arrays.equals(signedEntityBytes, entityBytes)); assertNotNull(signedEnt.getSignature()); - + X509Certificate cert = TestUtils.getExternalCert("user1"); - - + + cryptographer.checkSignature(signedEnt, cert, new ArrayList()); } @Test public void testSignMimeEntity_SHA1Digest_assertNotAllowedAlgorithm() throws Exception - { - + { + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); Assertions.assertThrows(IllegalArgumentException.class, () ->{ cryptographer.setDigestAlgorithm(DigestAlgorithm.SHA1WITHRSA); }); - + } - + @Test public void testSignMimeEntity_SHA256Digest_forceStrongDigest_assertValidation() throws Exception - { + { X509CertificateEx certex = TestUtils.getInternalCert("user1"); - + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); cryptographer.setDigestAlgorithm(DigestAlgorithm.SHA256WITHRSA); - + MimeEntity entity = new MimeEntity(); entity.setText("Hello world."); entity.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); entity.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); - + SignedEntity signedEnt = cryptographer.sign(entity, certex); - + assertNotNull(signedEnt); - + byte[] signedEntityBytes = EntitySerializer.Default.serializeToBytes(signedEnt.getContent()); - byte[] entityBytes = EntitySerializer.Default.serializeToBytes(entity); - + byte[] entityBytes = EntitySerializer.Default.serializeToBytes(entity); + assertTrue(Arrays.equals(signedEntityBytes, entityBytes)); assertNotNull(signedEnt.getSignature()); - + X509Certificate cert = TestUtils.getExternalCert("user1"); - + cryptographer.checkSignature(signedEnt, cert, new ArrayList()); - + } - + @Test public void testSignMimeEntity_SHA1Digest_assertNotAllowedDigestAlgorithm() throws Exception - { + { SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); - + Assertions.assertThrows(IllegalArgumentException.class, () ->{ cryptographer.setDigestAlgorithm(DigestAlgorithm.SHA1WITHRSA); }); - - + + } - + @Test public void testEncryptAndSignMimeEntity() throws Exception - { + { X509Certificate cert = TestUtils.getInternalCACert("user1"); - + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); - + MimeEntity entity = new MimeEntity(); entity.setText("Hello world."); entity.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); entity.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); MimeEntity encEntity = cryptographer.encrypt(entity, cert); - + assertNotNull(encEntity); - + X509CertificateEx certex = TestUtils.getInternalCert("user1"); SignedEntity signedEnt = cryptographer.sign(entity, certex); - + assertNotNull(signedEnt); cryptographer.checkSignature(signedEnt, cert, new ArrayList()); @@ -431,21 +433,21 @@ public void testEncryptAndSignMimeEntity() throws Exception public void testEncryptWithSingleCert_wrongDecryptCert_assertFailDecrypt() throws Exception { X509Certificate cert = TestUtils.getExternalCert("user1"); - + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); - + MimeEntity entity = new MimeEntity(); entity.setText("Hello world."); entity.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); entity.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); - - + + MimeEntity encEntity = cryptographer.encrypt(entity, cert); - + assertNotNull(encEntity); - + X509CertificateEx certex = TestUtils.getInternalCert("altnameonly"); - + boolean exceptionOccured = false; try { @@ -454,57 +456,200 @@ public void testEncryptWithSingleCert_wrongDecryptCert_assertFailDecrypt() throw catch (NHINDException e) { if (e.getError().equals(MimeError.Unexpected)); - exceptionOccured = true; + exceptionOccured = true; } assertTrue(exceptionOccured); } - + @Test public void testEncryptWithSingleCert_decryptWithMutlipeCerts_onlyOneCertCorrect_assertDecrypted() throws Exception { X509Certificate cert = TestUtils.getExternalCert("user1"); - + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); - + MimeEntity entity = new MimeEntity(); entity.setText("Hello world."); entity.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); entity.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); - - + + MimeEntity encEntity = cryptographer.encrypt(entity, cert); - + assertNotNull(encEntity); - + X509CertificateEx certex1 = TestUtils.getInternalCert("altnameonly"); X509CertificateEx certex2 = TestUtils.getInternalCert("user1"); MimeEntity decryEntity = cryptographer.decrypt(encEntity, Arrays.asList(certex1, certex2)); assertNotNull(decryEntity); - + byte[] decryEntityBytes = EntitySerializer.Default.serializeToBytes(decryEntity); byte[] entityBytes = EntitySerializer.Default.serializeToBytes(entity); - + assertTrue(Arrays.equals(decryEntityBytes, entityBytes)); - } - + } + @SuppressWarnings("deprecation") public void testvalidateSignature() throws Exception { final String str = FileUtils.readFileToString(new File("./src/test/resources/org/nhindirect/stagent/msgSig.txt")); - + byte[] byteData = Base64.decode(str); + + + CMSSignedData signed = new CMSSignedData(byteData); + + Store certs = signed.getCertificates(); + + + for (X509CertificateHolder cert : certs.getMatches(null)) + { + FileUtils.writeByteArrayToFile(new File("./testCert.der"), cert.getEncoded()); + } + } + + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityDefaultAlg() throws Exception + { + testEncryptAndDecryptKeyEncryptionMimeEntity(null, null, null, true, false, false); + } + + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityStrongEncryptionAlg() throws Exception + { + testEncryptAndDecryptKeyEncryptionMimeEntity(null, EncryptionAlgorithm.RSA_OAEP, null, true, false, false); + } + + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityStrongEncryptionAlgKeyEncryptDigestSHA256() throws Exception + { + testEncryptAndDecryptKeyEncryptionMimeEntity(null, EncryptionAlgorithm.RSA_OAEP, DigestAlgorithm.SHA256, true, false, false); + } + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityStrongEncryptionAlgKeyEncryptDigestSHA384() throws Exception + { + testEncryptAndDecryptKeyEncryptionMimeEntity(null, EncryptionAlgorithm.RSA_OAEP, DigestAlgorithm.SHA384, true, false, false); + } + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityStrongEncryptionAlgKeyEncryptDigestSHA512() throws Exception + { + testEncryptAndDecryptKeyEncryptionMimeEntity(null, EncryptionAlgorithm.RSA_OAEP, DigestAlgorithm.SHA512, true, false, false); + } + + @Disabled // SHA1 is allowed for now + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityStrongEncryptionAlgWeakKeyEncryptionDigestAlg() throws Exception + { + // This should throw an encryption exception, enforceStrongEncryption is set, SHA-1 is requested for key encryption digest + testEncryptAndDecryptKeyEncryptionMimeEntity(null, EncryptionAlgorithm.RSA_OAEP, DigestAlgorithm.SHA1, true, true, false); + } + public void testEncryptAndDecryptKeyEncryptionMimeEntityWeakEncryptionAlgStrongKeyEncryptionDigestAlg() throws Exception + { + // This should throw an encryption exception, enforceStrongEncryption is set, SHA-1 is requested for key encryption digest + testEncryptAndDecryptKeyEncryptionMimeEntity(null, EncryptionAlgorithm.RSA_PKCS1_V15, DigestAlgorithm.SHA256, true, true, false); + } - - CMSSignedData signed = new CMSSignedData(byteData); - - Store certs = signed.getCertificates(); - - - for (X509CertificateHolder cert : certs.getMatches(null)) - { - FileUtils.writeByteArrayToFile(new File("./testCert.der"), cert.getEncoded()); - } - } + @Disabled // PKCS1.5 is allowed for now + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityWeakKeyEncryptionAlg() throws Exception + { + // This should throw an encryption exception, enforceStrongEncryption is set, PKCS#1 V1.5 is requested for key encryption + testEncryptAndDecryptKeyEncryptionMimeEntity(null, EncryptionAlgorithm.RSA_PKCS1_V15, null, true, true, false); + } + + @Disabled // SHA1 is allowed for now + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityWeakKeyEncryptionDigestAlg() throws Exception + { + // This should throw an encryption exception, enforceStrongEncryption is set, SHA-1 is requested for key encryption digest + testEncryptAndDecryptKeyEncryptionMimeEntity(null, null, DigestAlgorithm.SHA1, true, true, false); + } + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityWeakKeyEncryptionDigestAlgNoEnforce() throws Exception + { + // This should NOT throw an encryption exception, enforceStrongEncryption is NOT set, SHA-1 is requested for key encryption digest + testEncryptAndDecryptKeyEncryptionMimeEntity(null, null, DigestAlgorithm.SHA1, false, false, false); + } + @Test + public void testEncryptAndDecryptKeyEncryptionMimeEntityWeakKeyEncryptionAlgNoEnforce() throws Exception + { + // This should NOT throw an encryption exception, enforceStrongEncryption is NOT set, PKCS#1V1.5 is requested for key encryption + testEncryptAndDecryptKeyEncryptionMimeEntity(null, EncryptionAlgorithm.RSA_PKCS1_V15, null, false, false, false); + } + private void testEncryptAndDecryptKeyEncryptionMimeEntity(EncryptionAlgorithm encAlg, EncryptionAlgorithm keyEncAlg, DigestAlgorithm keyEncDigAlg, boolean enforceStrongEncryption, boolean expectEncException, boolean expectDecException) throws Exception + { + X509Certificate cert = TestUtils.getExternalCert("user1"); + + SMIMECryptographerImpl cryptographer = new SMIMECryptographerImpl(); + if (encAlg != null) + cryptographer.setEncryptionAlgorithm(encAlg); + if (keyEncAlg != null) + cryptographer.setKeyEncryptionAlgorithm(keyEncAlg); + if (keyEncDigAlg != null) + cryptographer.setKeyEncryptionDigestAlgorithm(keyEncDigAlg); + cryptographer.setStrongEncryptionEnforced(enforceStrongEncryption); + + MimeEntity entity = new MimeEntity(); + entity.setText("Hello world."); + entity.setHeader(MimeStandard.ContentTypeHeader, "text/plain"); + entity.setHeader(MimeStandard.ContentTransferEncodingHeader, "7bit"); + + MimeEntity encEntity = null; + if (expectEncException) + { + boolean exceptionOccured = false; + try + { + encEntity = cryptographer.encrypt(entity, cert); + assertNotNull(encEntity); + } + catch (Exception e) + { + exceptionOccured = true; + } + assertTrue(exceptionOccured); + return; + } else { + encEntity = cryptographer.encrypt(entity, cert); + assertNotNull(encEntity); + } + + + /* + * explicit header checking for compliance with Applicability + * Statement v 1.2 + */ + final ContentType type = new ContentType(encEntity.getContentType()); + assertTrue(type.match(SMIMEStandard.CmsEnvelopeMediaType)); + assertFalse(type.match(SMIMEStandard.CmsEnvelopeMediaTypeAlt)); + + X509CertificateEx certex = TestUtils.getInternalCert("user1"); + + if (expectDecException) + { + boolean exceptionOccured = false; + try + { + cryptographer.decrypt(encEntity, certex); + } + catch (Exception e) + { + exceptionOccured = true; + } + assertTrue(exceptionOccured); + } + else + { + MimeEntity decryEntity = cryptographer.decrypt(encEntity, certex); + + assertNotNull(decryEntity); + + byte[] decryEntityBytes = EntitySerializer.Default.serializeToBytes(decryEntity); + byte[] entityBytes = EntitySerializer.Default.serializeToBytes(entity); + + assertTrue(Arrays.equals(decryEntityBytes, entityBytes)); + } + } }