diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java index 628acd24cef..f564669552e 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java @@ -48,6 +48,10 @@ public class PrismConstants { public static final String NS_MATCHING_RULE = NS_PREFIX + "matching-rule-3"; public static final String PREFIX_NS_MATCHING = "mr"; + + public static final String NS_PREFIX_CRYPTO = NS_PREFIX + "crypto/"; + public static final String NS_PREFIX_CRYPTO_ALGORITHM = NS_PREFIX_CRYPTO + "algorithm/"; + public static final String NS_CRYPTO_ALGORITHM_PBKD = NS_PREFIX_CRYPTO_ALGORITHM + "pbkd-3"; // Annotations diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/BaseProtector.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/BaseProtector.java index bea17186aca..cb71b26563d 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/BaseProtector.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/BaseProtector.java @@ -68,5 +68,5 @@ public boolean isEncrypted(ProtectedStringType ps) { Validate.notNull(ps, "Protected string must not be null."); return ps.isEncrypted(); } - + } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectedData.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectedData.java index a3e425a15ba..2e40d9470e9 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectedData.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectedData.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2014 Evolveum + * Copyright (c) 2014-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package com.evolveum.midpoint.prism.crypto; import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType; +import com.evolveum.prism.xml.ns._public.types_3.HashedDataType; /** * @author Radovan Semancik @@ -27,6 +28,10 @@ public interface ProtectedData { abstract void setClearBytes(byte[] bytes); + abstract T getClearValue(); + + abstract void setClearValue(T data); + abstract void destroyCleartext(); EncryptedDataType getEncryptedDataType(); @@ -35,4 +40,11 @@ public interface ProtectedData { boolean isEncrypted(); + HashedDataType getHashedDataType(); + + void setHashedData(HashedDataType hashedDataType); + + boolean isHashed(); + + boolean canSupportType(Class type); } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/Protector.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/Protector.java index 53510b69758..6317d1b063a 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/Protector.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/Protector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,19 +52,6 @@ public interface Protector { */ String decryptString(ProtectedStringType protectedString) throws EncryptionException; -// /** -// * -// * @param protectedString -// * @return decrypted DOM {@link Element} -// * @throws EncryptionException -// * this is thrown probably in case JRE/JDK doesn't have JCE -// * installed -// * @throws IllegalArgumentException -// * if protectedString argument is null or EncryptedData in -// * protectedString argument is null -// */ -// Element decrypt(ProtectedStringType protectedString) throws EncryptionException; - /** * * @param text @@ -76,29 +63,15 @@ public interface Protector { */ ProtectedStringType encryptString(String text) throws EncryptionException; -// /** -// * -// * @param plain -// * @return {@link ProtectedStringType} with encrypted element inside it. If -// * input argument is null, method returns null. -// * @throws EncryptionException -// * this is thrown probably in case JRE/JDK doesn't have JCE -// * installed -// */ -// ProtectedStringType encrypt(Element plain) throws EncryptionException; -// -// /** -// * Encrypts the ProtectedStringType "in place". -// * @param ps -// * @throws EncryptionException -// */ -// void encrypt(ProtectedStringType ps) throws EncryptionException; -// /** * Returns true if protected string contains encrypted data that seems valid. + * DEPRECATED. Use ProtectedStringType.isEncrypted() instead */ + @Deprecated boolean isEncrypted(ProtectedStringType ps); - - boolean compare(ProtectedStringType a, ProtectedStringType b) throws EncryptionException; + + void hash(ProtectedData protectedData) throws EncryptionException, SchemaException; + + boolean compare(ProtectedStringType a, ProtectedStringType b) throws EncryptionException, SchemaException; } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/AESProtector.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectorImpl.java similarity index 70% rename from infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/AESProtector.java rename to infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectorImpl.java index 6a4b725aa04..5f06de2728e 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/AESProtector.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,19 +26,26 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.security.SecureRandom; import java.security.UnrecoverableKeyException; +import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Enumeration; import java.util.List; +import java.util.Random; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.xml.namespace.QName; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; @@ -47,31 +54,44 @@ import org.apache.xml.security.encryption.XMLCipher; import org.apache.xml.security.utils.Base64; +import com.evolveum.midpoint.prism.PrismConstants; +import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.prism.xml.ns._public.types_3.CipherDataType; +import com.evolveum.prism.xml.ns._public.types_3.DigestMethodType; import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType; import com.evolveum.prism.xml.ns._public.types_3.EncryptionMethodType; +import com.evolveum.prism.xml.ns._public.types_3.HashedDataType; import com.evolveum.prism.xml.ns._public.types_3.KeyInfoType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; /** - * Class that manages encrypted string values. Java Cryptography Extension is + * Class that manages encrypted and hashed values. Java Cryptography Extension is * needed because this class is using AES-256 for encrypting/decrypting xml * data. * * @author Radovan Semancik * @author lazyman */ -public class AESProtector extends BaseProtector { +public class ProtectorImpl extends BaseProtector { + + private static final String ALGORITHM_PKKDF2_NAME = "PBKDF2WithHmacSHA512"; + private static final QName ALGORITH_PBKDF2_WITH_HMAC_SHA512_QNAME = new QName(PrismConstants.NS_CRYPTO_ALGORITHM_PBKD, ALGORITHM_PKKDF2_NAME); + private static final String ALGORITH_PBKDF2_WITH_HMAC_SHA512_URI = QNameUtil.qNameToUri(ALGORITH_PBKDF2_WITH_HMAC_SHA512_QNAME); private static final String KEY_DIGEST_TYPE = "SHA1"; private static final String DEFAULT_ENCRYPTION_ALGORITHM = XMLCipher.AES_128; private static final char[] KEY_PASSWORD = "midpoint".toCharArray(); - private static final Trace LOGGER = TraceManager.getTrace(AESProtector.class); + private static final String DEFAULT_DIGEST_ALGORITHM = ALGORITH_PBKDF2_WITH_HMAC_SHA512_URI; +// "http://www.w3.org/2009/xmlenc11#pbkdf2" + + private Random randomNumberGenerator; + + private static final Trace LOGGER = TraceManager.getTrace(ProtectorImpl.class); private String keyStorePath; private String keyStorePassword; @@ -79,6 +99,7 @@ public class AESProtector extends BaseProtector { private String requestedJceProviderName = null; private String encryptionAlgorithm; + private String digestAlgorithm; private List trustManagers; private static final KeyStore keyStore; @@ -92,7 +113,7 @@ public class AESProtector extends BaseProtector { } /** - * @throws SystemException if jceks keystore is not available on {@link AESProtector#getKeyStorePath} + * @throws SystemException if jceks keystore is not available on {@link ProtectorImpl#getKeyStorePath} */ public void init() { InputStream stream = null; @@ -113,11 +134,11 @@ public void init() { LOGGER.warn("Using default keystore from classpath ({}).", getKeyStorePath()); // Read from class path - stream = AESProtector.class.getClassLoader().getResourceAsStream(getKeyStorePath()); + stream = ProtectorImpl.class.getClassLoader().getResourceAsStream(getKeyStorePath()); // ugly dirty hack to have second chance to find keystore on // class path if (stream == null) { - stream = AESProtector.class.getClassLoader().getResourceAsStream( + stream = ProtectorImpl.class.getClassLoader().getResourceAsStream( "com/../../" + getKeyStorePath()); } } @@ -147,6 +168,8 @@ public void init() { new Object[]{getKeyStorePath(), ex.getMessage()}, ex); throw new SystemException(ex.getMessage(), ex); } + + randomNumberGenerator = new SecureRandom(); } public String getRequestedJceProviderName() { @@ -173,6 +196,28 @@ private String getCipherAlgorithm() { } } + private String getDigestAlgorithm() { + if (digestAlgorithm != null) { + return digestAlgorithm; + } else { + return DEFAULT_DIGEST_ALGORITHM; + } + } + + // TODO: make it configurable + + private int getPbkdKeyLength() { + return 256; + } + + private int getPbkdIterations() { + return 10000; + } + + private int getPbkdSaltLength() { + return 32; + } + /** * @return the encryptionKeyAlias * @throws IllegalStateException if encryption key digest is null or empty string @@ -428,24 +473,161 @@ private SecretKey getSecretKeyByDigest(String digest) throws EncryptionException throw new EncryptionException("Key '" + digest + "' is not in keystore."); } + + @Override + public void hash(ProtectedData protectedData) throws EncryptionException, SchemaException { + if (protectedData.isHashed()) { + throw new IllegalArgumentException("Attempt to hash protected data that are already hashed"); + } + String algorithmUri = getDigestAlgorithm(); + QName algorithmQName = QNameUtil.uriToQName(algorithmUri); + String algorithmNamespace = algorithmQName.getNamespaceURI(); + if (algorithmNamespace == null) { + throw new SchemaException("No algorithm namespace"); + } + + HashedDataType hashedDataType; + switch (algorithmNamespace) { + case PrismConstants.NS_CRYPTO_ALGORITHM_PBKD: + if (!protectedData.canSupportType(String.class)) { + throw new SchemaException("Non-string proteted data"); + } + hashedDataType = hashPbkd((ProtectedData) protectedData, algorithmUri, algorithmQName.getLocalPart()); + break; + default: + throw new SchemaException("Unkown namespace "+algorithmNamespace); + } + + protectedData.setHashedData(hashedDataType); + protectedData.destroyCleartext(); + } + + private HashedDataType hashPbkd(ProtectedData protectedData, String algorithmUri, String algorithmName) throws EncryptionException { + + char[] clearChars = protectedData.getClearValue().toCharArray(); + byte[] salt = generatePbkdSalt(); + int iterations = getPbkdIterations(); + + SecretKeyFactory secretKeyFactory; + try { + secretKeyFactory = SecretKeyFactory.getInstance( algorithmName ); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException(e.getMessage(), e); + } + PBEKeySpec keySpec = new PBEKeySpec( clearChars, salt, iterations, getPbkdKeyLength() ); + SecretKey key; + try { + key = secretKeyFactory.generateSecret( keySpec ); + } catch (InvalidKeySpecException e) { + throw new EncryptionException(e.getMessage(), e); + } + byte[] hashBytes = key.getEncoded( ); + + HashedDataType hashedDataType = new HashedDataType(); + + DigestMethodType digestMethod = new DigestMethodType(); + digestMethod.setAlgorithm(algorithmUri); + digestMethod.setSalt(salt); + digestMethod.setWorkFactor(iterations); + hashedDataType.setDigestMethod(digestMethod); + + hashedDataType.setDigestValue(hashBytes); + + return hashedDataType; + } + + private byte[] generatePbkdSalt() { + byte[] salt = new byte[getPbkdSaltLength()/8]; + randomNumberGenerator.nextBytes(salt); + return salt; + } @Override - public boolean compare(ProtectedStringType a, ProtectedStringType b) throws EncryptionException { + public boolean compare(ProtectedStringType a, ProtectedStringType b) throws EncryptionException, SchemaException { + if (a == b) { + return true; + } if (a == null && b == null) { return true; } if (a == null || b == null) { return false; } - String aClear = decryptString(a); - String bClear = decryptString(b); - if (aClear == null && bClear == null) { - return true; + if (a.isHashed() && b.isHashed()) { + throw new SchemaException("Cannot compare two hased protected strings"); } - if (aClear == null || bClear == null) { - return false; + + if (a.isHashed() || b.isHashed()) { + String clear; + ProtectedStringType hashedPs; + if (a.isHashed()) { + hashedPs = a; + clear = decryptString(b); + } else { + hashedPs = b; + clear = decryptString(a); + } + return compareHashed(hashedPs, clear.toCharArray()); + + } else { + String aClear = decryptString(a); + String bClear = decryptString(b); + if (aClear == null && bClear == null) { + return true; + } + if (aClear == null || bClear == null) { + return false; + } + return aClear.equals(bClear); + } + } + + private boolean compareHashed(ProtectedStringType hashedPs, char[] clearChars) throws SchemaException, EncryptionException { + HashedDataType hashedDataType = hashedPs.getHashedDataType(); + DigestMethodType digestMethodType = hashedDataType.getDigestMethod(); + if (digestMethodType == null) { + throw new SchemaException("No digest type"); + } + String algorithmUri = digestMethodType.getAlgorithm(); + QName algorithmQName = QNameUtil.uriToQName(algorithmUri); + String algorithmNamespace = algorithmQName.getNamespaceURI(); + if (algorithmNamespace == null) { + throw new SchemaException("No algorithm namespace"); + } + + switch (algorithmNamespace) { + case PrismConstants.NS_CRYPTO_ALGORITHM_PBKD: + return compareHashedPbkd(hashedDataType, algorithmQName.getLocalPart(), clearChars); + default: + throw new SchemaException("Unkown namespace "+algorithmNamespace); } - return aClear.equals(bClear); } + + private boolean compareHashedPbkd(HashedDataType hashedDataType, String algorithmName, char[] clearChars) throws EncryptionException { + DigestMethodType digestMethodType = hashedDataType.getDigestMethod(); + byte[] salt = digestMethodType.getSalt(); + Integer workFactor = digestMethodType.getWorkFactor(); + byte[] digestValue = hashedDataType.getDigestValue(); + int keyLen = digestValue.length * 8; + + SecretKeyFactory secretKeyFactory; + try { + secretKeyFactory = SecretKeyFactory.getInstance( algorithmName ); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException(e.getMessage(), e); + } + PBEKeySpec keySpec = new PBEKeySpec( clearChars, salt, workFactor, keyLen ); + SecretKey key; + try { + key = secretKeyFactory.generateSecret( keySpec ); + } catch (InvalidKeySpecException e) { + throw new EncryptionException(e.getMessage(), e); + } + byte[] hashBytes = key.getEncoded( ); + + return Arrays.equals(digestValue, hashBytes); + } + + } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/BeanMarshaller.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/BeanMarshaller.java index 1e093f34fc3..7f48c706c83 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/BeanMarshaller.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/BeanMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -454,6 +454,10 @@ public MapXNode marshalProtectedDataType(Object o, SerializationContext sc) EncryptedDataType encryptedDataType = protectedType.getEncryptedDataType(); MapXNode xEncryptedDataType = (MapXNode) marshall(encryptedDataType); xmap.put(ProtectedDataType.F_ENCRYPTED_DATA, xEncryptedDataType); + } else if (protectedType.getHashedDataType() != null) { + HashedDataType hashedDataType = protectedType.getHashedDataType(); + MapXNode xHashedDataType = (MapXNode) marshall(hashedDataType); + xmap.put(ProtectedDataType.F_HASHED_DATA, xHashedDataType); } else if (protectedType.getClearValue() != null){ QName type = XsdTypeMapper.toXsdType(protectedType.getClearValue().getClass()); PrimitiveXNode xClearValue = createPrimitiveXNode(protectedType.getClearValue(), type); diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/XNodeProcessorUtil.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/XNodeProcessorUtil.java index deafc191db8..085fa747461 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/XNodeProcessorUtil.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/XNodeProcessorUtil.java @@ -31,6 +31,7 @@ import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType; +import com.evolveum.prism.xml.ns._public.types_3.HashedDataType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedDataType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; @@ -84,6 +85,14 @@ public static void parseProtectedType(ProtectedDataType protectedType, Ma } } } + RootXNode xHashedData = xmap.getEntryAsRoot(ProtectedDataType.F_HASHED_DATA); + if (xHashedData != null) { + if (!(xHashedData.getSubnode() instanceof MapXNode)) { + throw new SchemaException("Cannot parse hashedData from "+xHashedData); + } + HashedDataType hashedDataType = prismContext.parserFor(xHashedData).context(pc).parseRealValue(HashedDataType.class); + protectedType.setHashedData(hashedDataType); + } // protected data empty..check for clear value if (protectedType.isEmpty()){ XNode xClearValue = xmap.get(ProtectedDataType.F_CLEAR_VALUE); diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/DigestMethodType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/DigestMethodType.java new file mode 100644 index 00000000000..0db44b7aabb --- /dev/null +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/DigestMethodType.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 Evolveum + * + * 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 com.evolveum.prism.xml.ns._public.types_3; + +import java.io.Serializable; +import java.util.Arrays; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; + + +/** + * JAXB representation of DigestMethodType. + * Manually created (not generated) + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "DigestMethodType", propOrder = { + "algorithm", + "salt", + "workFactor" +}) +public class DigestMethodType implements Serializable, Cloneable { + + @XmlElement(required = true) + @XmlSchemaType(name = "anyURI") + protected String algorithm; + + @XmlElement(required = false) + protected byte[] salt; + + @XmlElement(required = false) + protected Integer workFactor; + + /** + * Gets the value of the algorithm property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getAlgorithm() { + return algorithm; + } + + /** + * Sets the value of the algorithm property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setAlgorithm(String value) { + this.algorithm = value; + } + + public byte[] getSalt() { + return salt; + } + + public void setSalt(byte[] salt) { + this.salt = salt; + } + + public Integer getWorkFactor() { + return workFactor; + } + + public void setWorkFactor(Integer workFactor) { + this.workFactor = workFactor; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode()); + result = prime * result + Arrays.hashCode(salt); + result = prime * result + ((workFactor == null) ? 0 : workFactor.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + DigestMethodType other = (DigestMethodType) obj; + if (algorithm == null) { + if (other.algorithm != null) { + return false; + } + } else if (!algorithm.equals(other.algorithm)) { + return false; + } + if (!Arrays.equals(salt, other.salt)) { + return false; + } + if (workFactor == null) { + if (other.workFactor != null) { + return false; + } + } else if (!workFactor.equals(other.workFactor)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "DigestMethodType(algorithm=" + algorithm + ", salt=" + (salt==null?"null":"["+salt.length+" bytes]") + ", workFactor=" + workFactor + ")"; + } + + public DigestMethodType clone() { + DigestMethodType cloned = new DigestMethodType(); + cloned.setAlgorithm(getAlgorithm()); + cloned.setSalt(salt.clone()); + cloned.setWorkFactor(getWorkFactor()); + return cloned; + } + +} diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/HashedDataType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/HashedDataType.java new file mode 100644 index 00000000000..9eac9a8c971 --- /dev/null +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/HashedDataType.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017 Evolveum + * + * 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 com.evolveum.prism.xml.ns._public.types_3; + +import java.io.Serializable; +import java.util.Arrays; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * JAXB representation of HashedDataType. + * Manually created (not generated) + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "HashedDataType", propOrder = { + "digestMethod", + "digestValue" +}) +public class HashedDataType implements Serializable, Cloneable { + + @XmlElement(required = true) + protected DigestMethodType digestMethod; + + @XmlElement(required = true) + protected byte[] digestValue; + + public DigestMethodType getDigestMethod() { + return digestMethod; + } + + public void setDigestMethod(DigestMethodType digestMethod) { + this.digestMethod = digestMethod; + } + + public byte[] getDigestValue() { + return digestValue; + } + + public void setDigestValue(byte[] digestValue) { + this.digestValue = digestValue; + } + + @Override + public String toString() { + return "HashedDataType(digestMethod=" + digestMethod + ", digestValue=" + (digestValue==null?"null":"["+digestValue.length+" bytes])"); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((digestMethod == null) ? 0 : digestMethod.hashCode()); + result = prime * result + Arrays.hashCode(digestValue); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + HashedDataType other = (HashedDataType) obj; + if (digestMethod == null) { + if (other.digestMethod != null) { + return false; + } + } else if (!digestMethod.equals(other.digestMethod)) { + return false; + } + if (!Arrays.equals(digestValue, other.digestValue)) { + return false; + } + return true; + } + + @Override + public HashedDataType clone() { + HashedDataType cloned = new HashedDataType(); + cloned.setDigestMethod(getDigestMethod().clone()); + cloned.setDigestValue(digestValue.clone()); + return cloned; + } +} diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ObjectFactory.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ObjectFactory.java index b534ea9982e..dff1b211c30 100644 --- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ObjectFactory.java +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,6 +55,7 @@ public class ObjectFactory implements Serializable { private final static QName _PolyStringTypeNorm_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "norm"); private final static QName _PolyStringTypeOrig_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "orig"); private final static QName _ProtectedDataTypeEncryptedData_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "encryptedData"); + private final static QName _ProtectedDataTypeHashedData_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "hashedData"); private final static QName _ItemPathType_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "path"); private final static QName _Object_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "object"); @@ -241,6 +242,20 @@ public JAXBElement createProtectedStringTypeEncryptedData(Enc return new JAXBElement(_ProtectedDataTypeEncryptedData_QNAME, EncryptedDataType.class, ProtectedStringType.class, value); } + /** + * Create an instance of {@link JAXBElement }{@code <}{@link HashedDataType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://prism.evolveum.com/xml/ns/public/types-3", name = "hashedData", scope = ProtectedDataType.class) + public JAXBElement createProtectedDataTypeHashedData(HashedDataType value) { + return new JAXBElement(_ProtectedDataTypeHashedData_QNAME, HashedDataType.class, ProtectedDataType.class, value); + } + + @XmlElementDecl(namespace = "http://prism.evolveum.com/xml/ns/public/types-3", name = "hashedData", scope = ProtectedStringType.class) + public JAXBElement createProtectedStringTypeHashedData(HashedDataType value) { + return new JAXBElement(_ProtectedDataTypeHashedData_QNAME, HashedDataType.class, ProtectedStringType.class, value); + } + @XmlElementDecl(namespace = "http://prism.evolveum.com/xml/ns/public/types-3", name = "path", scope = ItemPathType.class) public JAXBElement createItemPathType(ItemPath value) { return new JAXBElement(_ItemPathType_QNAME, ItemPath.class, ItemPathType.class, value); diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedByteArrayType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedByteArrayType.java index 1c937d9c61f..b7bc02e0060 100644 --- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedByteArrayType.java +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedByteArrayType.java @@ -52,5 +52,10 @@ public void setClearBytes(byte[] bytes) { setClearValue(ArrayUtils.toObject(bytes)); } + @Override + public boolean canSupportType(Class type) { + return byte[].class.isAssignableFrom(type); + } + } diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedDataType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedDataType.java index 0f526327bf4..b1d62f231f8 100644 --- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedDataType.java +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedDataType.java @@ -1,11 +1,18 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2014.02.04 at 01:34:24 PM CET -// - - +/** + * Copyright (c) 2010-2017 Evolveum + * + * 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 com.evolveum.prism.xml.ns._public.types_3; import java.io.Serializable; @@ -37,33 +44,7 @@ /** - * - * TODO - * May be either encrypted or hashed or provided in the clear (e.g. for debugging). - * - * This type is marked as "mixed" because it may have alternative representation where - * just the plaintext value is presented as the only value. - * - * This is considered to be primitive built-in type for prism objects. - * - * - *

Java class for ProtectedDataType complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="ProtectedDataType">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="encryptedData" type="{http://prism.evolveum.com/xml/ns/public/types-3}EncryptedDataType" minOccurs="0"/>
- *         <any namespace='##other'/>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * + * This class was originally generated. But it was heavily modified by hand. * */ @XmlAccessorType(XmlAccessType.FIELD) @@ -78,6 +59,7 @@ public abstract class ProtectedDataType implements ProtectedData, Serializ public static final QName COMPLEX_TYPE = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "ProtectedDataType"); public final static QName F_ENCRYPTED_DATA = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "encryptedData"); + public final static QName F_HASHED_DATA = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "hashedData"); public final static QName F_CLEAR_VALUE = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "clearValue"); public static final String NS_XML_ENC = "http://www.w3.org/2001/04/xmlenc#"; @@ -95,6 +77,9 @@ public abstract class ProtectedDataType implements ProtectedData, Serializ @XmlTransient private EncryptedDataType encryptedDataType; + @XmlTransient + private HashedDataType hashedDataType; + @XmlTransient private T clearValue; @@ -157,10 +142,27 @@ public boolean isEncrypted() { return encryptedDataType != null; } + @Override + public HashedDataType getHashedDataType() { + return hashedDataType; + } + + @Override + public void setHashedData(HashedDataType hashedDataType) { + this.hashedDataType = hashedDataType; + } + + @Override + public boolean isHashed() { + return hashedDataType != null; + } + + @Override public T getClearValue() { return clearValue; } + @Override public void setClearValue(T clearValue) { this.clearValue = clearValue; } @@ -175,8 +177,13 @@ private JAXBElement toJaxbElement(EncryptedDataType encrypted return new JAXBElement(F_ENCRYPTED_DATA, EncryptedDataType.class, encryptedDataType); } + private JAXBElement toJaxbElement(HashedDataType hashedDataType) { + return new JAXBElement(F_ENCRYPTED_DATA, HashedDataType.class, hashedDataType); + } + private void clearContent() { encryptedDataType = null; + hashedDataType = null; } private boolean addContent(Object newObject) { @@ -192,6 +199,9 @@ private boolean addContent(Object newObject) { if (QNameUtil.match(F_ENCRYPTED_DATA, jaxbElement.getName())) { encryptedDataType = (EncryptedDataType) jaxbElement.getValue(); return true; + } else if (QNameUtil.match(F_HASHED_DATA, jaxbElement.getName())) { + hashedDataType = (HashedDataType) jaxbElement.getValue(); + return true; } else { throw new IllegalArgumentException("Attempt to add unknown JAXB element "+jaxbElement); } @@ -250,37 +260,52 @@ private EncryptedDataType convertXmlEncToEncryptedDate(Element eEncryptedData) { } public boolean isEmpty() { - return encryptedDataType == null && clearValue == null; + return encryptedDataType == null && hashedDataType == null && clearValue == null; } - + @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((clearValue == null) ? 0 : clearValue.hashCode()); result = prime * result + ((encryptedDataType == null) ? 0 : encryptedDataType.hashCode()); + result = prime * result + ((hashedDataType == null) ? 0 : hashedDataType.hashCode()); return result; } @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null) + } + if (obj == null) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } ProtectedDataType other = (ProtectedDataType) obj; if (clearValue == null) { - if (other.clearValue != null) + if (other.clearValue != null) { return false; - } else if (!clearValue.equals(other.clearValue)) + } + } else if (!clearValue.equals(other.clearValue)) { return false; + } if (encryptedDataType == null) { - if (other.encryptedDataType != null) + if (other.encryptedDataType != null) { return false; - } else if (!encryptedDataType.equals(other.encryptedDataType)) + } + } else if (!encryptedDataType.equals(other.encryptedDataType)) { return false; + } + if (hashedDataType == null) { + if (other.hashedDataType != null) { + return false; + } + } else if (!hashedDataType.equals(other.hashedDataType)) { + return false; + } return true; } @@ -292,6 +317,10 @@ public String toString() { sb.append("encrypted="); sb.append(encryptedDataType); } + if (hashedDataType != null) { + sb.append("hashed="); + sb.append(hashedDataType); + } if (clearValue != null) { sb.append("clearValue="); sb.append(clearValue); @@ -303,36 +332,16 @@ public String toString() { protected void cloneTo(ProtectedDataType cloned) { cloned.clearValue = CloneUtil.clone(clearValue); cloned.encryptedDataType = CloneUtil.clone(encryptedDataType); - + cloned.hashedDataType = CloneUtil.clone(hashedDataType); + // content is virtual, there is no point in copying it - -// for (Object o : getContent()) { -// if (o instanceof JAXBElement) { -// JAXBElement je = (JAXBElement) o; -// Object v = je.getValue(); -// if (v == null) { -// // do nothing -// } else if (v instanceof EncryptedDataType) { -// EncryptedDataType edt = (EncryptedDataType) v; -// cloned.addContent(new JAXBElement(je.getName(), (Class) je.getDeclaredType(), edt.clone())); -// } else { -// throw new IllegalStateException("Unknown JAXB element "+je.getName()+" ("+je.getValue().getClass()+") in ProtectedDataType"); -// } -// } else if (o instanceof Element) { -// cloned.addContent(((Element) o).cloneNode(true)); -// } else if (o instanceof String) { -// cloned.addContent(o); // will this work? -// } else { -// throw new IllegalStateException("Unknown object of type "+o.getClass()+ " in ProtectedDataType"); -// } -// } } class ContentList implements List, Serializable { @Override public int size() { - if (encryptedDataType != null) { + if (encryptedDataType != null || hashedDataType != null) { return 1; } else { return 0; @@ -341,7 +350,7 @@ public int size() { @Override public boolean isEmpty() { - return encryptedDataType == null; + return encryptedDataType == null && hashedDataType == null; } @Override @@ -363,7 +372,11 @@ public boolean hasNext() { public Object next() { if (index == 0) { index++; - return toJaxbElement(encryptedDataType); + if (encryptedDataType == null) { + return toJaxbElement(hashedDataType); + } else { + return toJaxbElement(encryptedDataType); + } } else { return null; } @@ -378,11 +391,15 @@ public void remove() { @Override public Object[] toArray() { - if (encryptedDataType == null) { + if (encryptedDataType == null && hashedDataType == null) { return new Object[0]; } else { Object[] a = new Object[1]; - a[0] = toJaxbElement(encryptedDataType); + if (encryptedDataType == null) { + a[0] = toJaxbElement(hashedDataType); + } else { + a[0] = toJaxbElement(encryptedDataType); + } return a; } } @@ -448,7 +465,11 @@ public void clear() { public Object get(int index) { if (index == 0) { // what if encryptedDataType is null and clearValue is set? [pm] - return toJaxbElement(encryptedDataType); + if (encryptedDataType == null) { + return toJaxbElement(hashedDataType); + } else { + return toJaxbElement(encryptedDataType); + } } else { return null; } diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedStringType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedStringType.java index 332425e0e50..6ea2223c581 100644 --- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedStringType.java +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedStringType.java @@ -1,11 +1,18 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2014.02.04 at 01:34:24 PM CET -// - - +/** + * Copyright (c) 2010-2017 Evolveum + * + * 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 com.evolveum.prism.xml.ns._public.types_3; import java.io.UnsupportedEncodingException; @@ -19,27 +26,7 @@ /** - * - * Specific subtype for protected STRING data. - * - * - *

Java class for ProtectedStringType complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="ProtectedStringType">
- *   <complexContent>
- *     <extension base="{http://prism.evolveum.com/xml/ns/public/types-3}ProtectedDataType">
- *       <sequence>
- *         <element name="clearValue" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *       </sequence>
- *     </extension>
- *   </complexContent>
- * </complexType>
- * 
- * - * + * This class was originally generated. But it was heavily modified by hand. */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "ProtectedStringType") @@ -71,6 +58,12 @@ public void setClearBytes(byte[] bytes) { } } + @Override + public boolean canSupportType(Class type) { + return String.class.isAssignableFrom(type); + } + + @Override public boolean equals(Object obj) { return super.equals(obj); @@ -96,4 +89,5 @@ public static String bytesToString(byte[] clearBytes) { throw new SystemException("Unsupported charset '"+CHARSET+"', is this system from 19th century?", e); } } + } diff --git a/infra/prism/src/main/resources/xml/ns/public/types-3.xsd b/infra/prism/src/main/resources/xml/ns/public/types-3.xsd index a2bcc23346f..a9e58143ccf 100644 --- a/infra/prism/src/main/resources/xml/ns/public/types-3.xsd +++ b/infra/prism/src/main/resources/xml/ns/public/types-3.xsd @@ -174,7 +174,7 @@ - + @@ -184,6 +184,7 @@ + @@ -250,6 +251,40 @@ + + + + TODO + Contains data protected by (non-reversible) hashing (message digest). + + Loosely based on XML digital signature standard. But we cannot use full + standard as we are not bound to XML. We need this to work also for + JSON and YAML and other languages. + + + + + + + + + + + + TODO + + Loosely based on XML encryption standard. But we cannot use full + standard as we are not bound to XML. We need this to work also for + JSON and YAML and other languages. + + + + + + + + + diff --git a/infra/prism/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java b/infra/prism/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java index 5621b2f3309..d206fb9a4da 100644 --- a/infra/prism/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java +++ b/infra/prism/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,8 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; +import com.evolveum.midpoint.prism.crypto.Protector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.foo.AccountConstructionType; import com.evolveum.midpoint.prism.schema.SchemaRegistryImpl; @@ -211,6 +213,9 @@ public class PrismInternalTestUtil implements PrismContextFactory { public static final QName WEAPONS_WEAPON_BRAND_TYPE_QNAME = new QName(NS_WEAPONS, "WeaponBrandType"); + public static final String KEYSTORE_PATH = "src/test/resources/keystore.jceks"; + public static final String KEYSTORE_PASSWORD = "changeit"; + public static PrismContextImpl constructInitializedPrismContext() throws SchemaException, SAXException, IOException { PrismContextImpl context = constructPrismContext(); context.initialize(); @@ -483,4 +488,13 @@ public static void assertContainerDefinition(PrismContainer container, String co public static PrismSchema getFooSchema(PrismContext prismContext) { return prismContext.getSchemaRegistry().findSchemaByNamespace(NS_FOO); } + + public static Protector createProtector(String xmlCipher){ + ProtectorImpl protector = new ProtectorImpl(); + protector.setKeyStorePassword(KEYSTORE_PASSWORD); + protector.setKeyStorePath(KEYSTORE_PATH); + protector.setEncryptionAlgorithm(xmlCipher); + protector.init(); + return protector; + } } diff --git a/infra/prism/src/test/java/com/evolveum/midpoint/prism/crypto/TestProtector.java b/infra/prism/src/test/java/com/evolveum/midpoint/prism/crypto/TestProtector.java index 760f18b264c..85af21be71c 100644 --- a/infra/prism/src/test/java/com/evolveum/midpoint/prism/crypto/TestProtector.java +++ b/infra/prism/src/test/java/com/evolveum/midpoint/prism/crypto/TestProtector.java @@ -1,54 +1,206 @@ +/** + * Copyright (c) 2010-2017 Evolveum + * + * 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 com.evolveum.midpoint.prism.crypto; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; +import static org.testng.AssertJUnit.assertFalse; import org.apache.xml.security.encryption.XMLCipher; import org.testng.AssertJUnit; import org.testng.annotations.Test; import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismInternalTestUtil; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; public class TestProtector { - public static final String KEYSTORE_PATH = "src/test/resources/keystore.jceks"; - public static final String KEYSTORE_PASSWORD = "changeit"; - - private PrismContext prismContext; - private static transient Trace LOGGER = TraceManager.getTrace(TestProtector.class); - public static Protector createProtector(String xmlCipher){ - AESProtector protector = new AESProtector(); - protector.setKeyStorePassword(KEYSTORE_PASSWORD); - protector.setKeyStorePath(KEYSTORE_PATH); - protector.setEncryptionAlgorithm(xmlCipher); - protector.init(); - return protector; + @Test + public void testProtectorEncryptionRoundTrip() throws Exception { + String value = "someValue"; + + Protector protector256 = PrismInternalTestUtil.createProtector(XMLCipher.AES_256); + Protector protector128 = PrismInternalTestUtil.createProtector(XMLCipher.AES_128); + + ProtectedStringType pdt = new ProtectedStringType(); + pdt.setClearValue(value); + assertFalse(pdt.isEmpty()); + assertFalse(pdt.isHashed()); + assertFalse(pdt.isEncrypted()); + + // WHEN + protector256.encrypt(pdt); + + // THEN + assertFalse(pdt.isEmpty()); + assertTrue(pdt.isEncrypted()); + assertFalse(pdt.isHashed()); + assertNull(pdt.getClearValue()); + + // WHEN + protector128.decrypt(pdt); + + // THEN + assertFalse(pdt.isEmpty()); + assertFalse(pdt.isEncrypted()); + assertFalse(pdt.isHashed()); + AssertJUnit.assertEquals(value, pdt.getClearValue()); + + // WHEN + ProtectedStringType pstEnc = protector256.encryptString(value); + + // THEN + assertFalse(pstEnc.isEmpty()); + assertTrue(pstEnc.isEncrypted()); + assertFalse(pstEnc.isHashed()); + + // WHEN + String clear = protector256.decryptString(pstEnc); + assertNotNull(clear); + + // THEN + AssertJUnit.assertEquals(value, clear); + + // WHEN + boolean compare1 = protector256.compare(pdt, pstEnc); + + // THEN + assertTrue("compare1 failed", compare1); + + // WHEN + boolean compare2 = protector256.compare(pstEnc, pdt); + + // THEN + assertTrue("compare2 failed", compare2); + + ProtectedStringType wrongPst = new ProtectedStringType(); + wrongPst.setClearValue("nonono This is not it"); + + // WHEN + boolean compare5 = protector256.compare(pdt, wrongPst); + + // THEN + assertFalse("compare5 unexpected success", compare5); + + // WHEN + boolean compare6 = protector256.compare(wrongPst, pdt); + + // THEN + assertFalse("compare6 unexpected success", compare6); + } + + @Test + public void testProtectorHashRoundTrip() throws Exception { + String value = "someValue"; + ProtectedStringType pst = new ProtectedStringType(); + pst.setClearValue(value); + assertFalse(pst.isEmpty()); + + Protector protector256 = PrismInternalTestUtil.createProtector(XMLCipher.AES_256); + + // WHEN + protector256.hash(pst); + + // THEN + assertFalse(pst.isEmpty()); + assertTrue(pst.isHashed()); + assertFalse(pst.isEncrypted()); + assertNull(pst.getClearValue()); + + ProtectedStringType checkPstClear = new ProtectedStringType(); + checkPstClear.setClearValue(value); + + // WHEN + boolean compare1 = protector256.compare(pst, checkPstClear); + + // THEN + assertTrue("compare1 failed", compare1); + + // WHEN + boolean compare2 = protector256.compare(checkPstClear, pst); + + // THEN + assertTrue("compare2 failed", compare2); + + ProtectedStringType checkPstEnc = new ProtectedStringType(); + checkPstEnc.setClearValue(value); + protector256.encrypt(checkPstEnc); + + // WHEN + boolean compare3 = protector256.compare(pst, checkPstEnc); + + // THEN + assertTrue("compare3 failed", compare3); + + // WHEN + boolean compare4 = protector256.compare(checkPstEnc, pst); + + // THEN + assertTrue("compare4 failed", compare4); + + ProtectedStringType wrongPst = new ProtectedStringType(); + wrongPst.setClearValue("nonono This is not it"); + + // WHEN + boolean compare5 = protector256.compare(pst, wrongPst); + + // THEN + assertFalse("compare5 unexpected success", compare5); + + // WHEN + boolean compare6 = protector256.compare(wrongPst, pst); + + // THEN + assertFalse("compare6 unexpected success", compare6); + + ProtectedStringType wrongPstEnc = new ProtectedStringType(); + wrongPstEnc.setClearValue("nonono This is not it"); + protector256.encrypt(wrongPstEnc); + + // WHEN + boolean compare7 = protector256.compare(pst, wrongPstEnc); + + // THEN + assertFalse("compare7 unexpected success", compare7); + + // WHEN + boolean compare8 = protector256.compare(wrongPstEnc, pst); + + // THEN + assertFalse("compare8 unexpected success", compare8); + + // change the hash ... comparison should fail + pst.getHashedDataType().getDigestValue()[1] = 0x12; + + // WHEN + boolean compare9 = protector256.compare(pst, checkPstClear); + + // THEN + assertFalse("compare9 unexpected success", compare9); + + // WHEN + boolean compare10 = protector256.compare(checkPstClear, pst); + + // THEN + assertFalse("compare10 unexpected success", compare10); + } - - - @Test - public void testProtectorKeyStore() throws Exception{ - - - String value = "someValue"; - - Protector protector256 = createProtector(XMLCipher.AES_256); - - ProtectedStringType pdt = new ProtectedStringType(); - pdt.setClearValue(value); - protector256.encrypt(pdt); - - Protector protector128 = createProtector(XMLCipher.AES_128); - protector128.decrypt(pdt); - - AssertJUnit.assertEquals(value, pdt.getClearValue()); - - ProtectedStringType pst = protector256.encryptString(value); - String clear = protector256.decryptString(pst); - - AssertJUnit.assertEquals(value, clear); - - } } diff --git a/infra/prism/src/test/java/com/evolveum/midpoint/prism/lex/TestProtectedString.java b/infra/prism/src/test/java/com/evolveum/midpoint/prism/lex/TestProtectedString.java index 02338435e9d..5feec07e764 100644 --- a/infra/prism/src/test/java/com/evolveum/midpoint/prism/lex/TestProtectedString.java +++ b/infra/prism/src/test/java/com/evolveum/midpoint/prism/lex/TestProtectedString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,12 +52,12 @@ public void setupDebug() throws SchemaException, SAXException, IOException { } @Test - public void testParseProtectedString() throws Exception { - final String TEST_NAME = "testParseProtectedString"; + public void testParseProtectedStringEncrypted() throws Exception { + final String TEST_NAME = "testParseProtectedStringEncrypted"; displayTestTitle(TEST_NAME); // GIVEN - Protector protector = TestProtector.createProtector(XMLCipher.AES_128); // TODO move to a util class + Protector protector = PrismInternalTestUtil.createProtector(XMLCipher.AES_128); ProtectedStringType protectedStringType = protector.encryptString("salalala"); PrismContext prismContext = PrismTestUtil.getPrismContext(); @@ -67,6 +67,31 @@ public void testParseProtectedString() throws Exception { MapXNode protectedStringTypeXNode = ((PrismContextImpl) prismContext).getBeanMarshaller().marshalProtectedDataType(protectedStringType, null); System.out.println("Protected string type XNode: " + protectedStringTypeXNode.debugDump()); + // THEN + ProtectedStringType unmarshalled = new ProtectedStringType(); + XNodeProcessorUtil.parseProtectedType(unmarshalled, protectedStringTypeXNode, prismContext, ParsingContext.createDefault()); + System.out.println("Unmarshalled value: " + unmarshalled); + assertEquals("Unmarshalled value differs from the original", protectedStringType, unmarshalled); + } + + @Test + public void testParseProtectedStringHashed() throws Exception { + final String TEST_NAME = "testParseProtectedStringHashed"; + displayTestTitle(TEST_NAME); + + // GIVEN + ProtectedStringType protectedStringType = new ProtectedStringType(); + protectedStringType.setClearValue("blabla"); + Protector protector = PrismInternalTestUtil.createProtector(XMLCipher.AES_128); + protector.hash(protectedStringType); + + PrismContext prismContext = PrismTestUtil.getPrismContext(); + + // WHEN + + MapXNode protectedStringTypeXNode = ((PrismContextImpl) prismContext).getBeanMarshaller().marshalProtectedDataType(protectedStringType, null); + System.out.println("Protected string type XNode: " + protectedStringTypeXNode.debugDump()); + // THEN ProtectedStringType unmarshalled = new ProtectedStringType(); XNodeProcessorUtil.parseProtectedType(unmarshalled, protectedStringTypeXNode, prismContext, ParsingContext.createDefault()); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java index af32213f130..532d3faa553 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java @@ -31,7 +31,7 @@ import com.evolveum.midpoint.model.common.expression.script.xpath.XPathScriptEvaluator; import com.evolveum.midpoint.model.common.stringpolicy.ValuePolicyGenerator; import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.schema.util.ObjectResolver; import com.evolveum.midpoint.security.api.SecurityEnforcer; import com.evolveum.midpoint.test.util.MidPointTestConstants; @@ -42,8 +42,8 @@ */ public class ExpressionTestUtil { - public static AESProtector createInitializedProtector(PrismContext prismContext) { - AESProtector protector = new AESProtector(); + public static ProtectorImpl createInitializedProtector(PrismContext prismContext) { + ProtectorImpl protector = new ProtectorImpl(); protector.setKeyStorePath(MidPointTestConstants.KEYSTORE_PATH); protector.setKeyStorePassword(MidPointTestConstants.KEYSTORE_PASSWORD); //protector.setPrismContext(prismContext); @@ -51,7 +51,7 @@ public static AESProtector createInitializedProtector(PrismContext prismContext) return protector; } - public static ExpressionFactory createInitializedExpressionFactory(ObjectResolver resolver, AESProtector protector, + public static ExpressionFactory createInitializedExpressionFactory(ObjectResolver resolver, ProtectorImpl protector, PrismContext prismContext, SecurityEnforcer securityEnforcer) { ExpressionFactory expressionFactory = new ExpressionFactory(resolver, prismContext); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpression.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpression.java index 44aa3968dbc..37558b35b8a 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpression.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpression.java @@ -26,7 +26,7 @@ import org.testng.annotations.Test; import org.xml.sax.SAXException; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.MidPointPrismContextFactory; @@ -70,7 +70,7 @@ public void setup() throws SchemaException, SAXException, IOException { prismContext = PrismTestUtil.createInitializedPrismContext(); ObjectResolver resolver = new DirectoryFileObjectResolver(MidPointTestConstants.OBJECTS_DIR); - AESProtector protector = ExpressionTestUtil.createInitializedProtector(prismContext); + ProtectorImpl protector = ExpressionTestUtil.createInitializedProtector(prismContext); expressionFactory = ExpressionTestUtil.createInitializedExpressionFactory(resolver, protector, prismContext, null); } diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java index 13cc13c29fa..2680c88ecf0 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java @@ -25,7 +25,7 @@ import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.MidPointPrismContextFactory; @@ -90,7 +90,7 @@ public void setup() throws SchemaException, SAXException, IOException { public void setupFactory() { PrismContext prismContext = PrismTestUtil.getPrismContext(); ObjectResolver resolver = new DirectoryFileObjectResolver(OBJECTS_DIR); - Protector protector = new AESProtector(); + Protector protector = new ProtectorImpl(); Collection functions = new ArrayList(); functions.add(ExpressionUtil.createBasicFunctionLibrary(prismContext, protector)); scriptExpressionfactory = new ScriptExpressionFactory(resolver, prismContext, protector); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestExpressionFunctions.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestExpressionFunctions.java index 46bf8234d4d..6a34ecee024 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestExpressionFunctions.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestExpressionFunctions.java @@ -18,7 +18,7 @@ import com.evolveum.midpoint.model.common.expression.functions.BasicExpressionFunctions; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.util.PrismTestUtil; @@ -381,7 +381,7 @@ public void testParseDateTime() throws Exception { private BasicExpressionFunctions createBasicFunctions() throws SchemaException, SAXException, IOException { PrismContext prismContext = PrismTestUtil.createInitializedPrismContext(); - Protector protector = new AESProtector(); + Protector protector = new ProtectorImpl(); return new BasicExpressionFunctions(prismContext, protector); } diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java index 48f9ca9ecdb..2461d3c9dd3 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java @@ -20,7 +20,7 @@ import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary; import com.evolveum.midpoint.model.common.expression.script.jsr223.Jsr223ScriptEvaluator; import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.MidPointPrismContextFactory; @@ -79,7 +79,7 @@ public void setupFactory() { System.out.println("Setting up expression factory and evaluator"); PrismContext prismContext = PrismTestUtil.getPrismContext(); ObjectResolver resolver = new DirectoryFileObjectResolver(OBJECTS_DIR); - Protector protector = new AESProtector(); + Protector protector = new ProtectorImpl(); Collection functions = new ArrayList(); functions.add(ExpressionUtil.createBasicFunctionLibrary(prismContext, protector)); scriptExpressionfactory = new ScriptExpressionFactory(resolver, prismContext, protector); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/mapping/MappingTestEvaluator.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/mapping/MappingTestEvaluator.java index 0ce3b81d0d2..0fe06eb1bab 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/mapping/MappingTestEvaluator.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/mapping/MappingTestEvaluator.java @@ -40,7 +40,7 @@ import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; @@ -84,7 +84,7 @@ public class MappingTestEvaluator { private PrismContext prismContext; private MappingFactory mappingFactory; - AESProtector protector; + ProtectorImpl protector; public PrismContext getPrismContext() { return prismContext; @@ -108,7 +108,7 @@ public void init() throws SchemaException, SAXException, IOException { mappingFactory.setProtector(protector); } - public AESProtector getProtector() { + public ProtectorImpl getProtector() { return protector; } diff --git a/repo/system-init/src/main/java/com/evolveum/midpoint/init/ConfigurableProtectorFactory.java b/repo/system-init/src/main/java/com/evolveum/midpoint/init/ConfigurableProtectorFactory.java index cc715a89007..318911912a9 100644 --- a/repo/system-init/src/main/java/com/evolveum/midpoint/init/ConfigurableProtectorFactory.java +++ b/repo/system-init/src/main/java/com/evolveum/midpoint/init/ConfigurableProtectorFactory.java @@ -17,7 +17,7 @@ package com.evolveum.midpoint.init; import com.evolveum.midpoint.common.configuration.api.MidpointConfiguration; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; @@ -82,7 +82,7 @@ public void init() { } public Protector getProtector() { - AESProtector protector = new AESProtector(); + ProtectorImpl protector = new ProtectorImpl(); protector.setEncryptionKeyAlias(protectorConfig.getEncryptionKeyAlias()); protector.setKeyStorePassword(protectorConfig.getKeyStorePassword()); protector.setKeyStorePath(protectorConfig.getKeyStorePath()); diff --git a/tools/repo-ninja/src/main/java/com/evolveum/midpoint/tools/ninja/KeyStoreDumper.java b/tools/repo-ninja/src/main/java/com/evolveum/midpoint/tools/ninja/KeyStoreDumper.java index df4ebe9d191..79da8d35d54 100644 --- a/tools/repo-ninja/src/main/java/com/evolveum/midpoint/tools/ninja/KeyStoreDumper.java +++ b/tools/repo-ninja/src/main/java/com/evolveum/midpoint/tools/ninja/KeyStoreDumper.java @@ -15,7 +15,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.crypto.Protector; @@ -32,8 +32,8 @@ public void execute(){ System.out.println("###################################################"); System.out.println("Printing keys from key store"); - if (protector instanceof AESProtector){ - AESProtector aesProtector = (AESProtector) protector; + if (protector instanceof ProtectorImpl){ + ProtectorImpl aesProtector = (ProtectorImpl) protector; System.out.println("Using key store from location: " + aesProtector.getKeyStorePath()); // System.out.println("Cipher: " + aesProtector.getXmlCipher()); @@ -65,8 +65,8 @@ public void execute(){ System.out.println(" Algorithm: " + key.getAlgorithm()); System.out.println(" Format: " + key.getFormat()); System.out.println(" Key length: " + key.getEncoded().length * 8); - if (protector instanceof AESProtector) { - System.out.println(" Key name: " + ((AESProtector) protector).getSecretKeyDigest(key)); + if (protector instanceof ProtectorImpl) { + System.out.println(" Key name: " + ((ProtectorImpl) protector).getSecretKeyDigest(key)); } // Cipher cipher = Cipher.getInstance(key.getAlgorithm()); // System.out.println(" Cipher algorithm" + cipher.getAlgorithm());