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

org.nervousync.utils.CertificateUtils Maven / Gradle / Ivy

There is a newer version: 1.2.1
Show newest version
/*
 * Licensed to the Nervousync Studio (NSYC) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.nervousync.utils;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.nervousync.commons.core.Globals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.Optional;

/**
 * 

Certificate Utils

* *
    Generate Keypair
*
    Signature and generate X.509 certificate
*
    Parse X.509 certificate from certificate file, PKCS12 file or binary data arrays
*
    Validate X.509 certificate period and signature
*
    Read PublicKey/PrivateKey from binary data arrays or PKCS12 file
*
    Signature and generate PKCS12 file
*
*

数字证书工具

* *
    生成密钥对
*
    签发X.509证书
*
    从证书文件、PKCS12文件或二进制数据中读取X.509证书
*
    验证X.509证书的有效期、数字签名
*
    从PKCS12文件或二进制数据中读取公钥和私钥
*
    生成PKCS12文件
*
*/ public final class CertificateUtils { private static final Logger LOGGER = LoggerFactory.getLogger(CertificateUtils.class); static { /* * Add Bouncy Castle Provider * 添加Bouncy Castle算法库 */ Security.addProvider(new BouncyCastleProvider()); } private CertificateUtils() { } /** *

Generate KeyPair using given algorithm/secure random algorithm/key size

*

根据给定的算法、安全随机数算法、密钥长度生成密钥对

* * @param algorithm Algorithm * 算法 * @param randomAlgorithm Secure Random Algorithm * 安全随机数算法 * @param keySize Key size * 密钥长度 * @return Generated key pair * 生成的密钥对 */ public static KeyPair keyPair(final String algorithm, final String randomAlgorithm, final int keySize) { if (keySize % 128 != 0) { LOGGER.error("Key size is invalid"); return null; } KeyPair keyPair = null; try { SecureRandom secureRandom; if (StringUtils.isEmpty(randomAlgorithm)) { LOGGER.error("Random algorithm not configure, use default: SHA1PRNG"); secureRandom = SecureRandom.getInstance("SHA1PRNG"); } else { secureRandom = SecureRandom.getInstance(randomAlgorithm); } // Initialize keyPair instance KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm, "BC"); if (algorithm.equalsIgnoreCase("EC")) { ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec("sm2p256v1"); keyPairGenerator.initialize(ecGenParameterSpec, secureRandom); } else { keyPairGenerator.initialize(keySize, secureRandom); } keyPair = keyPairGenerator.generateKeyPair(); } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException e) { LOGGER.error("Initialize key pair generator error! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } } return keyPair; } /** *

Convert public key to X.509 certificate, signature certificate by given private key and signature algorithm

*

转换PublicKey为X509证书,并使用给定的私钥和签名算法进行签名

* * @param publicKey Public key * 公钥 * @param serialNumber Certificate Serial Number * 证书的序列号 * @param beginDate Certificate Begin Date * 证书有效期起始时间 * @param endDate Certificate End Date * 证书有效期截至时间 * @param commonName Certificate Common Name * 证书的公用名称(CN字段) * @param signKey Certificate Signer Private Key * 证书签发者的私钥 * @param signAlgorithm Signature Algorithm * 签名算法 * @return Generated X.509 certificate * 生成的X.509格式证书 */ public static X509Certificate x509(final PublicKey publicKey, final long serialNumber, final Date beginDate, final Date endDate, final String commonName, final PrivateKey signKey, final String signAlgorithm) { if (publicKey == null || signKey == null || StringUtils.isEmpty(signAlgorithm)) { return null; } X500Name subjectDN = new X500Name("CN=" + commonName); SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); X509v3CertificateBuilder x509v3CertificateBuilder = new X509v3CertificateBuilder(subjectDN, BigInteger.valueOf(serialNumber), beginDate, endDate, subjectDN, publicKeyInfo); try { x509v3CertificateBuilder.addExtension(Extension.basicConstraints, Boolean.FALSE, new BasicConstraints(Boolean.FALSE)); ContentSigner contentSigner = new JcaContentSignerBuilder(signAlgorithm).setProvider("BC").build(signKey); X509CertificateHolder certificateHolder = x509v3CertificateBuilder.build(contentSigner); return new JcaX509CertificateConverter().getCertificate(certificateHolder); } catch (OperatorCreationException | GeneralSecurityException | IOException e) { LOGGER.error("Generate PKCS12 Certificate Failed! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } } return null; } /** *

Read X.509 Certificate

*

读取X.509格式的证书

* * @param certBytes Certificate Data Bytes * 证书的字节数组 * @return Read X.509 certificate or null for invalid * 读取的X.509证书, 如果数据非法,则返回null */ public static X509Certificate x509(final byte[] certBytes) { return x509(certBytes, Boolean.FALSE); } /** *

Read X.509 Certificate and verified by given public key

*

读取X.509格式的证书并使用给定的公钥验证证书签名

* * @param certBytes Certificate Data Bytes * 证书的字节数组 * @param verifyKey Verifier Public Key * 验证签名使用的公钥 * @return Read X.509 certificate or null for invalid * 读取的X.509证书, 如果数据非法或证书签名验证失败,则返回null */ public static X509Certificate x509(byte[] certBytes, PublicKey verifyKey) { return x509(certBytes, verifyKey, Boolean.FALSE); } /** *

Read X.509 Certificate and check certificate validity period

*

读取X.509格式的证书并检查证书是否在有效期内

* * @param certBytes Certificate Data Bytes * 证书的字节数组 * @param checkValidity true for check certificate signature, false for not check * true检查证书有效期, false不检查证书有效期 * @return Read X.509 certificate or null for invalid * 读取的X.509证书, 如果数据非法或证书未在有效期内,则返回null */ public static X509Certificate x509(byte[] certBytes, boolean checkValidity) { return x509(certBytes, null, checkValidity); } /** *

Read X.509 Certificate, verified by given public key and check certificate validity period

*

读取X.509格式的证书,使用给定的公钥验证证书签名并检查证书是否在有效期内

* * @param certBytes Certificate Data Bytes * 证书的字节数组 * @param verifyKey Verifier Public Key * 验证签名使用的公钥 * @param checkValidity true for check certificate signature, false for not check * true检查证书有效期, false不检查证书有效期 * @return Read X.509 certificate or null for invalid * 读取的X.509证书, 如果数据非法、证书签名验证失败或证书未在有效期内,则返回null */ public static X509Certificate x509(byte[] certBytes, PublicKey verifyKey, boolean checkValidity) { X509Certificate x509Certificate; try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC"); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(certBytes); x509Certificate = (X509Certificate) certificateFactory.generateCertificate(byteArrayInputStream); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Certificate SN: {}", x509Certificate.getSerialNumber().toString()); } if (checkValidity) { x509Certificate.checkValidity(); } if (verifyKey != null) { x509Certificate.verify(verifyKey, "BC"); } } catch (Exception e) { LOGGER.error("Certificate is invalid! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } x509Certificate = null; } return x509Certificate; } /** *

Check X.509 Certificate Validity Period

*

检查证书是否在有效期内

* * @param x509Certificate X.509 Certificate * X.509证书 * @return Verify Result. true for success, false for failed * 验证结果。true验证通过, false验证失败 */ public static boolean verify(final X509Certificate x509Certificate) { return verify(x509Certificate, null, Boolean.TRUE); } /** *

Verify X.509 Certificate Signature by given Public Key

*

使用给定的公钥检查证书签名是否有效

* * @param x509Certificate X.509 Certificate * X.509证书 * @param verifyKey Verifier Public Key * 验证签名使用的公钥 * @return Verify Result. true for success, false for failed * 验证结果。true验证通过, false验证失败 */ public static boolean verify(final X509Certificate x509Certificate, final PublicKey verifyKey) { return verify(x509Certificate, verifyKey, Boolean.FALSE); } /** *

Check X.509 Certificate Validity Period and Verify Signature by given Public Key

*

检查证书是否在有效期内并使用给定的公钥检查证书签名是否有效

* * @param x509Certificate X.509 Certificate * X.509证书 * @param verifyKey Verifier Public Key * 验证签名使用的公钥 * @param checkValidity true for check certificate signature, false for not check * true检查证书有效期, false不检查证书有效期 * @return Verify Result. true for success, false for failed * 验证结果。true验证通过, false验证失败 */ public static boolean verify(final X509Certificate x509Certificate, final PublicKey verifyKey, final boolean checkValidity) { if (x509Certificate == null) { return Boolean.FALSE; } try { if (checkValidity) { x509Certificate.checkValidity(); } x509Certificate.verify((verifyKey == null) ? x509Certificate.getPublicKey() : verifyKey, "BC"); return Boolean.TRUE; } catch (Exception e) { LOGGER.error("Certificate is invalid! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } return Boolean.FALSE; } } /** *

Read X.509 certificate from Keystore/PKCS12 data bytes

*

从Keystore/PKCS12的二进制数据中读取X.509证书

* * @param storeBytes Keystore/PKCS12 data bytes * Keystore/PKCS12的二进制数据 * @param certAlias Certificate alias name * 证书别名 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @return Read X.509 certificate or null for invalid * 读取的X.509证书, 如果数据非法或未找到别名指定的证书,则返回null */ public static X509Certificate x509(byte[] storeBytes, String certAlias, String password) { return x509(storeBytes, certAlias, password, null, Boolean.FALSE); } /** *

Read X.509 certificate from file. File format: Keystore/PKCS12

*

从指定路径的Keystore/PKCS12文件中读取X.509证书, 文件格式为:Keystore/PKCS12

* * @param storePath Keystore/PKCS12 file path * Keystore/PKCS12文件路径 * @param certAlias Certificate alias name * 证书别名 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @return Read X.509 certificate or null for invalid * 读取的X.509证书, 如果数据非法或未找到别名指定的证书,则返回null */ public static X509Certificate x509(String storePath, String certAlias, String password) { try { return x509(FileUtils.readFileBytes(storePath), certAlias, password); } catch (IOException e) { return null; } } /** *

Read X.509 certificate from Keystore/PKCS12 data bytes, Validity Period and Verify Signature by given Public Key.

*

从Keystore/PKCS12的二进制数据中读取X.509证书,检查证书是否在有效期内并使用给定的公钥检查证书签名是否有效

* * @param storeBytes Keystore/PKCS12 data bytes * Keystore/PKCS12的二进制数据 * @param certAlias Certificate alias name * 证书别名 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @param verifyKey Verifier Public Key * 验证签名使用的公钥 * @param checkValidity true for check certificate signature, false for not check * true检查证书有效期, false不检查证书有效期 * @return Read X.509 certificate or null for invalid * 读取的X.509证书, 如果数据非法、未找到别名指定的证书、证书未在有效期或证书签名错误,则返回null */ public static X509Certificate x509(final byte[] storeBytes, final String certAlias, final String password, final PublicKey verifyKey, final boolean checkValidity) { return Optional.ofNullable(loadKeyStore(storeBytes, password)) .filter(keyStore -> checkKey(keyStore, certAlias)) .map(keyStore -> { X509Certificate x509Certificate; try { x509Certificate = (X509Certificate) keyStore.getCertificate(certAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Certificate SN: {}", x509Certificate.getSerialNumber().toString()); } if (checkValidity) { x509Certificate.checkValidity(); } if (verifyKey != null) { x509Certificate.verify(verifyKey, "BC"); } } catch (Exception e) { LOGGER.error("Certificate is invalid! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } x509Certificate = null; } return x509Certificate; }) .orElse(null); } /** *

Generate PublicKey from key data bytes and given algorithm

*

根据给定的算法和二进制数据生成公钥

* * @param algorithm Key algorithm * 算法 * @param keyBytes Key data bytes * 二进制数据 * @return Generated publicKey or null if data bytes invalid * 生成的公钥,如果二进制数据非法则返回null */ public static PublicKey publicKey(String algorithm, byte[] keyBytes) { try { return KeyFactory.getInstance(algorithm).generatePublic(new X509EncodedKeySpec(keyBytes)); } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { LOGGER.error("Generate key from data bytes error! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } } return null; } /** *

Generate PrivateKey from key data bytes and given algorithm

*

根据给定的算法和二进制数据生成私钥

* * @param algorithm Key algorithm * 算法 * @param keyBytes Key data bytes * 二进制数据 * @return Generated privateKey or null if data bytes invalid * 生成的私钥,如果二进制数据非法则返回null */ public static PrivateKey privateKey(String algorithm, byte[] keyBytes) { try { return KeyFactory.getInstance(algorithm).generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { LOGGER.error("Generate key from data bytes error! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } return null; } } /** *

Read PrivateKey from Keystore/PKCS12 data bytes

* * @param storeBytes Keystore/PKCS12 data bytes * Keystore/PKCS12的二进制数据 * @param certAlias Certificate alias name * 证书别名 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @return Read PrivateKey or null if data bytes invalid * 读取的私钥, 如果数据非法、未找到别名指定的证书,则返回null */ public static PrivateKey privateKey(byte[] storeBytes, String certAlias, String password) { KeyStore keyStore = loadKeyStore(storeBytes, password); if (keyStore != null && CertificateUtils.checkKey(keyStore, certAlias)) { return CertificateUtils.privateKey(keyStore, certAlias, password); } return null; } /** *

Read PrivateKey from file. File format: Keystore/PKCS12

*

从指定路径的Keystore/PKCS12文件中读取私钥, 文件格式为:Keystore/PKCS12

* * @param storePath Keystore/PKCS12 file path * Keystore/PKCS12文件路径 * @param certAlias Certificate alias name * 证书别名 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @return Read PrivateKey or null if invalid * 读取的私钥, 如果数据非法或未找到别名指定的证书,则返回null */ public static PrivateKey privateKey(String storePath, String certAlias, String password) { KeyStore keyStore = loadKeyStore(storePath, password); if (keyStore != null && CertificateUtils.checkKey(keyStore, certAlias)) { return CertificateUtils.privateKey(keyStore, certAlias, password); } return null; } /** *

Generate PKCS12 Data Bytes, include PrivateKey, PublicKey and generate signature

*

生成PKCS12格式的证书二进制数据,包含公钥、私钥及数字签名

* * @param keyPair Key pair * 密钥对 * @param serialNumber Certificate Serial Number * 证书的序列号 * @param beginDate Certificate Begin Date * 证书有效期起始时间 * @param endDate Certificate End Date * 证书有效期截至时间 * @param certAlias Certificate alias name * 证书别名 * @param commonName Certificate Common Name * 证书的公用名称(CN字段) * @param passWord Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @param signKey Certificate Signer Private Key * 证书签发者的私钥 * @param signAlgorithm Signature Algorithm * 签名算法 * @return Generated PKCS12 data bytes or 0 length byte array if has error * 生成的PKCS12格式二进制数据,如果出错则返回长度为0的二进制数据 */ public static byte[] PKCS12(final KeyPair keyPair, final long serialNumber, final Date beginDate, final Date endDate, final String certAlias, final String commonName, final String passWord, final PrivateKey signKey, final String signAlgorithm) { char[] charArray; if (StringUtils.isEmpty(passWord)) { charArray = Globals.DEFAULT_VALUE_STRING.toCharArray(); } else { charArray = passWord.toCharArray(); } X500Name subjectDN = new X500Name("CN=" + commonName); SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); X509v3CertificateBuilder x509v3CertificateBuilder = new X509v3CertificateBuilder(subjectDN, BigInteger.valueOf(serialNumber), beginDate, endDate, subjectDN, publicKeyInfo); ByteArrayOutputStream byteArrayOutputStream = null; try { x509v3CertificateBuilder.addExtension(Extension.basicConstraints, Boolean.FALSE, new BasicConstraints(Boolean.FALSE)); ContentSigner contentSigner = new JcaContentSignerBuilder(signAlgorithm).setProvider("BC") .build(signKey == null ? keyPair.getPrivate() : signKey); X509CertificateHolder certificateHolder = x509v3CertificateBuilder.build(contentSigner); X509Certificate x509Certificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateHolder); KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC"); keyStore.load(null, null); keyStore.setKeyEntry(certAlias, keyPair.getPrivate(), charArray, new Certificate[]{x509Certificate}); byteArrayOutputStream = new ByteArrayOutputStream(); keyStore.store(byteArrayOutputStream, charArray); return byteArrayOutputStream.toByteArray(); } catch (OperatorCreationException | GeneralSecurityException | IOException e) { LOGGER.error("Generate PKCS12 Certificate Failed! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } return new byte[0]; } finally { IOUtils.closeStream(byteArrayOutputStream); } } /** *

Read PKCS12 KeyStore from data bytes

*

从二进制数据中读取PKCS12格式的密钥库

* * @param storeBytes Keystore/PKCS12 data bytes * Keystore/PKCS12的二进制数据 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @return Read PKCS12 KeyStore or null for error * 读取的PKCS12格式密钥库, 如果数据非法则返回null */ public static KeyStore loadKeyStore(byte[] storeBytes, String password) { return loadKeyStore(new ByteArrayInputStream(storeBytes), password); } /** *

Read PKCS12 KeyStore from given file path

*

从指定的文件位置读取PKCS12格式的密钥库

* * @param storePath Keystore/PKCS12 file path * Keystore/PKCS12文件路径 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @return Read PKCS12 KeyStore or null for error * 读取的PKCS12格式密钥库, 如果文件未找到或数据非法则返回null */ public static KeyStore loadKeyStore(String storePath, String password) { try { return loadKeyStore(new FileInputStream(storePath), password); } catch (FileNotFoundException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Read data error! ", e); } } return null; } /** *

Read PKCS12 KeyStore from input stream

*

从输入流读取PKCS12格式的密钥库

* * @param inputStream Input stream * 输入流 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @return Read PKCS12 KeyStore or null for error * 读取的PKCS12格式密钥库, 如果数据非法则返回null */ public static KeyStore loadKeyStore(InputStream inputStream, String password) { KeyStore keyStore; try { keyStore = KeyStore.getInstance("PKCS12", "BC"); keyStore.load(inputStream, password == null ? null : password.toCharArray()); } catch (Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Read data error! ", e); } keyStore = null; } finally { IOUtils.closeStream(inputStream); } return keyStore; } /** *

Check PKCS12 contains certificate alias

*

检查PKCS12标准的密钥库中是否包含给定别名的证书

* * @param keyStore PKCS12 KeyStore * PKCS12标准的密钥库 * @param certAlias Certificate alias name * 证书别名 * @return Check result. true for exists false for not found * 检查结果 true证书存在 false证书不存在 */ public static boolean checkKey(KeyStore keyStore, String certAlias) { if (keyStore == null || certAlias == null) { return Boolean.FALSE; } try { return keyStore.isKeyEntry(certAlias); } catch (KeyStoreException e) { return Boolean.FALSE; } } /** *

Read X.509 certificate from PKCS12 KeyStore

*

从PKCS12格式的密钥库中读取X.509证书

* * @param keyStore PKCS12 KeyStore * PKCS12标准的密钥库 * @param certAlias Certificate alias name * 证书别名 * @return Read X.509 certificate or null for not found * 读取的X.509证书, 如果数据非法、未找到别名指定的证书,则返回null */ public static X509Certificate x509(KeyStore keyStore, String certAlias) { try { return (X509Certificate) keyStore.getCertificate(certAlias); } catch (KeyStoreException e) { LOGGER.error("Read certificate error!"); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } return null; } } /** *

Read PrivateKey from PKCS12 KeyStore

*

从PKCS12格式的密钥库中读取私钥

* * @param keyStore PKCS12 KeyStore * PKCS12标准的密钥库 * @param certAlias Certificate alias name * 证书别名 * @param password Password of Keystore/PKCS12 * Keystore/PKCS12的密码 * @return Read PrivateKey or null if invalid * 读取的私钥, 如果数据非法或未找到别名指定的证书,则返回null */ public static PrivateKey privateKey(KeyStore keyStore, String certAlias, String password) { try { return (PrivateKey) keyStore.getKey(certAlias, password == null ? null : password.toCharArray()); } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) { LOGGER.error("Read private key from key store error! "); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Stack message: ", e); } return null; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy