Add spongy castle sources to libraries folder
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package org.spongycastle.cert.crmf.jcajce;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.spongycastle.cert.crmf.CRMFException;
|
||||
import org.spongycastle.jcajce.DefaultJcaJceHelper;
|
||||
import org.spongycastle.jcajce.NamedJcaJceHelper;
|
||||
import org.spongycastle.jcajce.ProviderJcaJceHelper;
|
||||
import org.spongycastle.operator.GenericKey;
|
||||
import org.spongycastle.operator.OutputEncryptor;
|
||||
|
||||
public class JceCRMFEncryptorBuilder
|
||||
{
|
||||
private ASN1ObjectIdentifier encryptionOID;
|
||||
private int keySize;
|
||||
|
||||
private CRMFHelper helper = new CRMFHelper(new DefaultJcaJceHelper());
|
||||
private SecureRandom random;
|
||||
|
||||
public JceCRMFEncryptorBuilder(ASN1ObjectIdentifier encryptionOID)
|
||||
{
|
||||
this(encryptionOID, -1);
|
||||
}
|
||||
|
||||
public JceCRMFEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize)
|
||||
{
|
||||
this.encryptionOID = encryptionOID;
|
||||
this.keySize = keySize;
|
||||
}
|
||||
|
||||
public JceCRMFEncryptorBuilder setProvider(Provider provider)
|
||||
{
|
||||
this.helper = new CRMFHelper(new ProviderJcaJceHelper(provider));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JceCRMFEncryptorBuilder setProvider(String providerName)
|
||||
{
|
||||
this.helper = new CRMFHelper(new NamedJcaJceHelper(providerName));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JceCRMFEncryptorBuilder setSecureRandom(SecureRandom random)
|
||||
{
|
||||
this.random = random;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public OutputEncryptor build()
|
||||
throws CRMFException
|
||||
{
|
||||
return new CRMFOutputEncryptor(encryptionOID, keySize, random);
|
||||
}
|
||||
|
||||
private class CRMFOutputEncryptor
|
||||
implements OutputEncryptor
|
||||
{
|
||||
private SecretKey encKey;
|
||||
private AlgorithmIdentifier algorithmIdentifier;
|
||||
private Cipher cipher;
|
||||
|
||||
CRMFOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
|
||||
throws CRMFException
|
||||
{
|
||||
KeyGenerator keyGen = helper.createKeyGenerator(encryptionOID);
|
||||
|
||||
if (random == null)
|
||||
{
|
||||
random = new SecureRandom();
|
||||
}
|
||||
|
||||
if (keySize < 0)
|
||||
{
|
||||
keyGen.init(random);
|
||||
}
|
||||
else
|
||||
{
|
||||
keyGen.init(keySize, random);
|
||||
}
|
||||
|
||||
cipher = helper.createCipher(encryptionOID);
|
||||
encKey = keyGen.generateKey();
|
||||
AlgorithmParameters params = helper.generateParameters(encryptionOID, encKey, random);
|
||||
|
||||
try
|
||||
{
|
||||
cipher.init(Cipher.ENCRYPT_MODE, encKey, params, random);
|
||||
}
|
||||
catch (GeneralSecurityException e)
|
||||
{
|
||||
throw new CRMFException("unable to initialize cipher: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
//
|
||||
// If params are null we try and second guess on them as some providers don't provide
|
||||
// algorithm parameter generation explicity but instead generate them under the hood.
|
||||
//
|
||||
if (params == null)
|
||||
{
|
||||
params = cipher.getParameters();
|
||||
}
|
||||
|
||||
algorithmIdentifier = helper.getAlgorithmIdentifier(encryptionOID, params);
|
||||
}
|
||||
|
||||
public AlgorithmIdentifier getAlgorithmIdentifier()
|
||||
{
|
||||
return algorithmIdentifier;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream(OutputStream dOut)
|
||||
{
|
||||
return new CipherOutputStream(dOut, cipher);
|
||||
}
|
||||
|
||||
public GenericKey getKey()
|
||||
{
|
||||
return new GenericKey(encKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.spongycastle.cert.jcajce;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.spongycastle.util.CollectionStore;
|
||||
import org.spongycastle.x509.X509AttributeCertificate;
|
||||
|
||||
/**
|
||||
* Class for storing Attribute Certificates for later lookup.
|
||||
* <p>
|
||||
* The class will convert X509AttributeCertificate objects into X509AttributeCertificateHolder objects.
|
||||
* </p>
|
||||
*/
|
||||
public class JcaAttrCertStore
|
||||
extends CollectionStore
|
||||
{
|
||||
/**
|
||||
* Basic constructor.
|
||||
*
|
||||
* @param collection - initial contents for the store, this is copied.
|
||||
*/
|
||||
public JcaAttrCertStore(Collection collection)
|
||||
throws IOException
|
||||
{
|
||||
super(convertCerts(collection));
|
||||
}
|
||||
|
||||
public JcaAttrCertStore(X509AttributeCertificate attrCert)
|
||||
throws IOException
|
||||
{
|
||||
this(convertCert(attrCert));
|
||||
}
|
||||
|
||||
private static Collection convertCert(X509AttributeCertificate attrCert)
|
||||
throws IOException
|
||||
{
|
||||
List tmp = new ArrayList();
|
||||
|
||||
tmp.add(attrCert);
|
||||
|
||||
return convertCerts(tmp);
|
||||
}
|
||||
|
||||
private static Collection convertCerts(Collection collection)
|
||||
throws IOException
|
||||
{
|
||||
List list = new ArrayList(collection.size());
|
||||
|
||||
for (Iterator it = collection.iterator(); it.hasNext();)
|
||||
{
|
||||
Object o = it.next();
|
||||
|
||||
if (o instanceof X509AttributeCertificate)
|
||||
{
|
||||
X509AttributeCertificate cert = (X509AttributeCertificate)o;
|
||||
|
||||
list.add(new JcaX509AttributeCertificateHolder(cert));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package org.spongycastle.cms.bc;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.spongycastle.cms.CMSAlgorithm;
|
||||
import org.spongycastle.cms.CMSException;
|
||||
import org.spongycastle.crypto.BufferedBlockCipher;
|
||||
import org.spongycastle.crypto.CipherKeyGenerator;
|
||||
import org.spongycastle.crypto.StreamCipher;
|
||||
import org.spongycastle.crypto.io.CipherOutputStream;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
import org.spongycastle.operator.GenericKey;
|
||||
import org.spongycastle.operator.OutputEncryptor;
|
||||
import org.spongycastle.util.Integers;
|
||||
|
||||
public class BcCMSContentEncryptorBuilder
|
||||
{
|
||||
private static Map keySizes = new HashMap();
|
||||
|
||||
static
|
||||
{
|
||||
keySizes.put(CMSAlgorithm.AES128_CBC, Integers.valueOf(128));
|
||||
keySizes.put(CMSAlgorithm.AES192_CBC, Integers.valueOf(192));
|
||||
keySizes.put(CMSAlgorithm.AES256_CBC, Integers.valueOf(256));
|
||||
|
||||
keySizes.put(CMSAlgorithm.CAMELLIA128_CBC, Integers.valueOf(128));
|
||||
keySizes.put(CMSAlgorithm.CAMELLIA192_CBC, Integers.valueOf(192));
|
||||
keySizes.put(CMSAlgorithm.CAMELLIA256_CBC, Integers.valueOf(256));
|
||||
}
|
||||
|
||||
private static int getKeySize(ASN1ObjectIdentifier oid)
|
||||
{
|
||||
Integer size = (Integer)keySizes.get(oid);
|
||||
|
||||
if (size != null)
|
||||
{
|
||||
return size.intValue();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private ASN1ObjectIdentifier encryptionOID;
|
||||
private int keySize;
|
||||
|
||||
private EnvelopedDataHelper helper = new EnvelopedDataHelper();
|
||||
private SecureRandom random;
|
||||
|
||||
public BcCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID)
|
||||
{
|
||||
this(encryptionOID, getKeySize(encryptionOID));
|
||||
}
|
||||
|
||||
public BcCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize)
|
||||
{
|
||||
this.encryptionOID = encryptionOID;
|
||||
this.keySize = keySize;
|
||||
}
|
||||
|
||||
public BcCMSContentEncryptorBuilder setSecureRandom(SecureRandom random)
|
||||
{
|
||||
this.random = random;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public OutputEncryptor build()
|
||||
throws CMSException
|
||||
{
|
||||
return new CMSOutputEncryptor(encryptionOID, keySize, random);
|
||||
}
|
||||
|
||||
private class CMSOutputEncryptor
|
||||
implements OutputEncryptor
|
||||
{
|
||||
private KeyParameter encKey;
|
||||
private AlgorithmIdentifier algorithmIdentifier;
|
||||
private Object cipher;
|
||||
|
||||
CMSOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
|
||||
throws CMSException
|
||||
{
|
||||
if (random == null)
|
||||
{
|
||||
random = new SecureRandom();
|
||||
}
|
||||
|
||||
CipherKeyGenerator keyGen = helper.createKeyGenerator(encryptionOID, random);
|
||||
|
||||
encKey = new KeyParameter(keyGen.generateKey());
|
||||
|
||||
algorithmIdentifier = helper.generateAlgorithmIdentifier(encryptionOID, encKey, random);
|
||||
|
||||
cipher = helper.createContentCipher(true, encKey, algorithmIdentifier);
|
||||
}
|
||||
|
||||
public AlgorithmIdentifier getAlgorithmIdentifier()
|
||||
{
|
||||
return algorithmIdentifier;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream(OutputStream dOut)
|
||||
{
|
||||
if (cipher instanceof BufferedBlockCipher)
|
||||
{
|
||||
return new CipherOutputStream(dOut, (BufferedBlockCipher)cipher);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CipherOutputStream(dOut, (StreamCipher)cipher);
|
||||
}
|
||||
}
|
||||
|
||||
public GenericKey getKey()
|
||||
{
|
||||
return new GenericKey(algorithmIdentifier, encKey.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package org.spongycastle.cms.jcajce;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.spongycastle.cms.CMSAlgorithm;
|
||||
import org.spongycastle.cms.CMSException;
|
||||
import org.spongycastle.operator.GenericKey;
|
||||
import org.spongycastle.operator.OutputEncryptor;
|
||||
import org.spongycastle.util.Integers;
|
||||
|
||||
public class JceCMSContentEncryptorBuilder
|
||||
{
|
||||
private static Map keySizes = new HashMap();
|
||||
|
||||
static
|
||||
{
|
||||
keySizes.put(CMSAlgorithm.AES128_CBC, Integers.valueOf(128));
|
||||
keySizes.put(CMSAlgorithm.AES192_CBC, Integers.valueOf(192));
|
||||
keySizes.put(CMSAlgorithm.AES256_CBC, Integers.valueOf(256));
|
||||
|
||||
keySizes.put(CMSAlgorithm.CAMELLIA128_CBC, Integers.valueOf(128));
|
||||
keySizes.put(CMSAlgorithm.CAMELLIA192_CBC, Integers.valueOf(192));
|
||||
keySizes.put(CMSAlgorithm.CAMELLIA256_CBC, Integers.valueOf(256));
|
||||
}
|
||||
|
||||
private static int getKeySize(ASN1ObjectIdentifier oid)
|
||||
{
|
||||
Integer size = (Integer)keySizes.get(oid);
|
||||
|
||||
if (size != null)
|
||||
{
|
||||
return size.intValue();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private ASN1ObjectIdentifier encryptionOID;
|
||||
private int keySize;
|
||||
|
||||
private EnvelopedDataHelper helper = new EnvelopedDataHelper(new DefaultJcaJceExtHelper());
|
||||
private SecureRandom random;
|
||||
|
||||
public JceCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID)
|
||||
{
|
||||
this(encryptionOID, getKeySize(encryptionOID));
|
||||
}
|
||||
|
||||
public JceCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize)
|
||||
{
|
||||
this.encryptionOID = encryptionOID;
|
||||
this.keySize = keySize;
|
||||
}
|
||||
|
||||
public JceCMSContentEncryptorBuilder setProvider(Provider provider)
|
||||
{
|
||||
this.helper = new EnvelopedDataHelper(new ProviderJcaJceExtHelper(provider));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JceCMSContentEncryptorBuilder setProvider(String providerName)
|
||||
{
|
||||
this.helper = new EnvelopedDataHelper(new NamedJcaJceExtHelper(providerName));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JceCMSContentEncryptorBuilder setSecureRandom(SecureRandom random)
|
||||
{
|
||||
this.random = random;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public OutputEncryptor build()
|
||||
throws CMSException
|
||||
{
|
||||
return new CMSOutputEncryptor(encryptionOID, keySize, random);
|
||||
}
|
||||
|
||||
private class CMSOutputEncryptor
|
||||
implements OutputEncryptor
|
||||
{
|
||||
private SecretKey encKey;
|
||||
private AlgorithmIdentifier algorithmIdentifier;
|
||||
private Cipher cipher;
|
||||
|
||||
CMSOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
|
||||
throws CMSException
|
||||
{
|
||||
KeyGenerator keyGen = helper.createKeyGenerator(encryptionOID);
|
||||
|
||||
if (random == null)
|
||||
{
|
||||
random = new SecureRandom();
|
||||
}
|
||||
|
||||
if (keySize < 0)
|
||||
{
|
||||
keyGen.init(random);
|
||||
}
|
||||
else
|
||||
{
|
||||
keyGen.init(keySize, random);
|
||||
}
|
||||
|
||||
cipher = helper.createCipher(encryptionOID);
|
||||
encKey = keyGen.generateKey();
|
||||
AlgorithmParameters params = helper.generateParameters(encryptionOID, encKey, random);
|
||||
|
||||
try
|
||||
{
|
||||
cipher.init(Cipher.ENCRYPT_MODE, encKey, params, random);
|
||||
}
|
||||
catch (GeneralSecurityException e)
|
||||
{
|
||||
throw new CMSException("unable to initialize cipher: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
//
|
||||
// If params are null we try and second guess on them as some providers don't provide
|
||||
// algorithm parameter generation explicity but instead generate them under the hood.
|
||||
//
|
||||
if (params == null)
|
||||
{
|
||||
params = cipher.getParameters();
|
||||
}
|
||||
|
||||
algorithmIdentifier = helper.getAlgorithmIdentifier(encryptionOID, params);
|
||||
}
|
||||
|
||||
public AlgorithmIdentifier getAlgorithmIdentifier()
|
||||
{
|
||||
return algorithmIdentifier;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream(OutputStream dOut)
|
||||
{
|
||||
return new CipherOutputStream(dOut, cipher);
|
||||
}
|
||||
|
||||
public GenericKey getKey()
|
||||
{
|
||||
return new GenericKey(encKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package org.spongycastle.cms.jcajce;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.security.AlgorithmParameterGenerator;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.RC2ParameterSpec;
|
||||
|
||||
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.spongycastle.cms.CMSException;
|
||||
import org.spongycastle.jcajce.io.MacOutputStream;
|
||||
import org.spongycastle.operator.GenericKey;
|
||||
import org.spongycastle.operator.MacCalculator;
|
||||
|
||||
public class JceCMSMacCalculatorBuilder
|
||||
{
|
||||
private ASN1ObjectIdentifier macOID;
|
||||
private int keySize;
|
||||
|
||||
private EnvelopedDataHelper helper = new EnvelopedDataHelper(new DefaultJcaJceExtHelper());
|
||||
private SecureRandom random;
|
||||
private MacOutputStream macOutputStream;
|
||||
|
||||
public JceCMSMacCalculatorBuilder(ASN1ObjectIdentifier macOID)
|
||||
{
|
||||
this(macOID, -1);
|
||||
}
|
||||
|
||||
public JceCMSMacCalculatorBuilder(ASN1ObjectIdentifier macOID, int keySize)
|
||||
{
|
||||
this.macOID = macOID;
|
||||
this.keySize = keySize;
|
||||
}
|
||||
|
||||
public JceCMSMacCalculatorBuilder setProvider(Provider provider)
|
||||
{
|
||||
this.helper = new EnvelopedDataHelper(new ProviderJcaJceExtHelper(provider));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JceCMSMacCalculatorBuilder setProvider(String providerName)
|
||||
{
|
||||
this.helper = new EnvelopedDataHelper(new NamedJcaJceExtHelper(providerName));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public JceCMSMacCalculatorBuilder setSecureRandom(SecureRandom random)
|
||||
{
|
||||
this.random = random;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MacCalculator build()
|
||||
throws CMSException
|
||||
{
|
||||
return new CMSOutputEncryptor(macOID, keySize, random);
|
||||
}
|
||||
|
||||
private class CMSOutputEncryptor
|
||||
implements MacCalculator
|
||||
{
|
||||
private SecretKey encKey;
|
||||
private AlgorithmIdentifier algorithmIdentifier;
|
||||
private Mac mac;
|
||||
private SecureRandom random;
|
||||
|
||||
CMSOutputEncryptor(ASN1ObjectIdentifier macOID, int keySize, SecureRandom random)
|
||||
throws CMSException
|
||||
{
|
||||
KeyGenerator keyGen = helper.createKeyGenerator(macOID);
|
||||
|
||||
if (random == null)
|
||||
{
|
||||
random = new SecureRandom();
|
||||
}
|
||||
|
||||
this.random = random;
|
||||
|
||||
if (keySize < 0)
|
||||
{
|
||||
keyGen.init(random);
|
||||
}
|
||||
else
|
||||
{
|
||||
keyGen.init(keySize, random);
|
||||
}
|
||||
|
||||
encKey = keyGen.generateKey();
|
||||
|
||||
AlgorithmParameterSpec paramSpec = generateParameterSpec(macOID, encKey);
|
||||
|
||||
algorithmIdentifier = helper.getAlgorithmIdentifier(macOID, paramSpec);
|
||||
mac = helper.createContentMac(encKey, algorithmIdentifier);
|
||||
}
|
||||
|
||||
public AlgorithmIdentifier getAlgorithmIdentifier()
|
||||
{
|
||||
return algorithmIdentifier;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream()
|
||||
{
|
||||
return new MacOutputStream(mac);
|
||||
}
|
||||
|
||||
public byte[] getMac()
|
||||
{
|
||||
return mac.doFinal();
|
||||
}
|
||||
|
||||
public GenericKey getKey()
|
||||
{
|
||||
return new GenericKey(encKey);
|
||||
}
|
||||
|
||||
protected AlgorithmParameterSpec generateParameterSpec(ASN1ObjectIdentifier macOID, SecretKey encKey)
|
||||
throws CMSException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (macOID.equals(PKCSObjectIdentifiers.RC2_CBC))
|
||||
{
|
||||
byte[] iv = new byte[8];
|
||||
|
||||
random.nextBytes(iv);
|
||||
|
||||
return new RC2ParameterSpec(encKey.getEncoded().length * 8, iv);
|
||||
}
|
||||
|
||||
AlgorithmParameterGenerator pGen = helper.createAlgorithmParameterGenerator(macOID);
|
||||
|
||||
AlgorithmParameters p = pGen.generateParameters();
|
||||
|
||||
return p.getParameterSpec(IvParameterSpec.class);
|
||||
}
|
||||
catch (GeneralSecurityException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user