8132082: Let OracleUcrypto accept RSAPrivateKey
Reviewed-by: xuelei, valeriep, coffeys
This commit is contained in:
parent
33994176ee
commit
a8cbc27bce
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -39,6 +39,7 @@ SUNWprivate_1.1 {
|
||||
Java_com_oracle_security_ucrypto_NativeCipher_nativeUpdate;
|
||||
Java_com_oracle_security_ucrypto_NativeCipher_nativeFinal;
|
||||
Java_com_oracle_security_ucrypto_NativeKey_nativeFree;
|
||||
Java_com_oracle_security_ucrypto_NativeKey_00024RSAPrivate_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeKey_00024RSAPrivateCrt_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeKey_00024RSAPublic_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeInit;
|
||||
@ -56,6 +57,7 @@ SUNWprivate_1.1 {
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeCipher_nativeUpdate;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeCipher_nativeFinal;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeKey_nativeFree;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeKey_00024RSAPrivate_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeKey_00024RSAPrivateCrt_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeKey_00024RSAPublic_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeInit;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,19 +31,9 @@ import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.lang.ref.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.KeyFactorySpi;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.RSAPrivateCrtKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.*;
|
||||
|
||||
/**
|
||||
* Wrapper class for native keys needed for using ucrypto APIs.
|
||||
@ -87,6 +77,41 @@ abstract class NativeKey implements Key {
|
||||
return b;
|
||||
}
|
||||
|
||||
static final class RSAPrivate extends NativeKey implements RSAPrivateKey {
|
||||
|
||||
private static final long serialVersionUID = 1622705588904302831L;
|
||||
|
||||
private final RSAPrivateKeySpec keySpec;
|
||||
private final long keyId;
|
||||
|
||||
RSAPrivate(KeySpec keySpec) throws InvalidKeySpecException {
|
||||
super(2);
|
||||
long pKey = 0L;
|
||||
if (keySpec instanceof RSAPrivateKeySpec) {
|
||||
RSAPrivateKeySpec ks = (RSAPrivateKeySpec) keySpec;
|
||||
BigInteger mod = ks.getModulus();
|
||||
BigInteger privateExp = ks.getPrivateExponent();
|
||||
pKey = nativeInit(NativeKey.getMagnitude(mod),
|
||||
NativeKey.getMagnitude(privateExp));
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Only supports RSAPrivateKeySpec");
|
||||
}
|
||||
if (pKey == 0L) {
|
||||
throw new UcryptoException("Error constructing RSA PrivateKey");
|
||||
}
|
||||
// track native resource clean up
|
||||
new KeyRef(this, pKey);
|
||||
this.keySpec = (RSAPrivateKeySpec) keySpec;
|
||||
this.keyId = pKey;
|
||||
}
|
||||
|
||||
long value() { return keyId; }
|
||||
public BigInteger getModulus() { return keySpec.getModulus(); };
|
||||
public BigInteger getPrivateExponent() { return keySpec.getPrivateExponent(); };
|
||||
|
||||
private native static long nativeInit(byte[] mod, byte[] privExp);
|
||||
}
|
||||
|
||||
static final class RSAPrivateCrt extends NativeKey implements RSAPrivateCrtKey {
|
||||
|
||||
private static final long serialVersionUID = 6812507588904302831L;
|
||||
@ -119,7 +144,7 @@ abstract class NativeKey implements Key {
|
||||
throw new InvalidKeySpecException("Only supports RSAPrivateCrtKeySpec");
|
||||
}
|
||||
if (pKey == 0L) {
|
||||
throw new UcryptoException("Error constructing RSA PrivateKey");
|
||||
throw new UcryptoException("Error constructing RSA PrivateCrtKey");
|
||||
}
|
||||
// track native resource clean up
|
||||
new KeyRef(this, pKey);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -37,9 +37,11 @@ import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.RSAPrivateCrtKeySpec;
|
||||
import java.security.spec.RSAPrivateKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.security.interfaces.RSAKey;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
@ -205,8 +207,8 @@ public class NativeRSACipher extends CipherSpi {
|
||||
// Make sure the proper opmode uses the proper key
|
||||
if (doEncrypt && (!(newKey instanceof RSAPublicKey))) {
|
||||
throw new InvalidKeyException("RSAPublicKey required for encryption");
|
||||
} else if (!doEncrypt && (!(newKey instanceof RSAPrivateCrtKey))) {
|
||||
throw new InvalidKeyException("RSAPrivateCrtKey required for decryption");
|
||||
} else if (!doEncrypt && (!(newKey instanceof RSAPrivateKey))) {
|
||||
throw new InvalidKeyException("RSAPrivateKey required for decryption");
|
||||
}
|
||||
|
||||
NativeKey nativeKey = null;
|
||||
@ -223,17 +225,26 @@ public class NativeRSACipher extends CipherSpi {
|
||||
throw new InvalidKeyException(ikse);
|
||||
}
|
||||
} else {
|
||||
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) newKey;
|
||||
try {
|
||||
nativeKey = (NativeKey) keyFactory.engineGeneratePrivate
|
||||
(new RSAPrivateCrtKeySpec(privateKey.getModulus(),
|
||||
privateKey.getPublicExponent(),
|
||||
privateKey.getPrivateExponent(),
|
||||
privateKey.getPrimeP(),
|
||||
privateKey.getPrimeQ(),
|
||||
privateKey.getPrimeExponentP(),
|
||||
privateKey.getPrimeExponentQ(),
|
||||
privateKey.getCrtCoefficient()));
|
||||
if (newKey instanceof RSAPrivateCrtKey) {
|
||||
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) newKey;
|
||||
nativeKey = (NativeKey) keyFactory.engineGeneratePrivate
|
||||
(new RSAPrivateCrtKeySpec(privateKey.getModulus(),
|
||||
privateKey.getPublicExponent(),
|
||||
privateKey.getPrivateExponent(),
|
||||
privateKey.getPrimeP(),
|
||||
privateKey.getPrimeQ(),
|
||||
privateKey.getPrimeExponentP(),
|
||||
privateKey.getPrimeExponentQ(),
|
||||
privateKey.getCrtCoefficient()));
|
||||
} else if (newKey instanceof RSAPrivateKey) {
|
||||
RSAPrivateKey privateKey = (RSAPrivateKey) newKey;
|
||||
nativeKey = (NativeKey) keyFactory.engineGeneratePrivate
|
||||
(new RSAPrivateKeySpec(privateKey.getModulus(),
|
||||
privateKey.getPrivateExponent()));
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported type of RSAPrivateKey");
|
||||
}
|
||||
} catch (InvalidKeySpecException ikse) {
|
||||
throw new InvalidKeyException(ikse);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -38,16 +38,11 @@ import java.security.PublicKey;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.KeyFactorySpi;
|
||||
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.RSAPrivateCrtKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.security.spec.*;
|
||||
|
||||
/**
|
||||
* Ucrypto-private KeyFactory class for generating native keys
|
||||
* needed for using ucrypto APIs. Given that it's not used
|
||||
* externally, it only needs to support RSAPrivateCrtKeySpec
|
||||
* and RSAPublicKeySpec objects.
|
||||
* needed for using ucrypto APIs.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
@ -56,7 +51,13 @@ public final class NativeRSAKeyFactory extends KeyFactorySpi {
|
||||
@Override
|
||||
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
return new NativeKey.RSAPrivateCrt(keySpec);
|
||||
if (keySpec instanceof RSAPrivateCrtKeySpec) {
|
||||
return new NativeKey.RSAPrivateCrt(keySpec);
|
||||
} else if (keySpec instanceof RSAPrivateKeySpec) {
|
||||
return new NativeKey.RSAPrivate(keySpec);
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Unsupported key spec");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,9 +43,8 @@ import java.security.PublicKey;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.RSAPrivateCrtKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.*;
|
||||
|
||||
import sun.nio.ch.DirectBuffer;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@ -192,25 +191,31 @@ class NativeRSASignature extends SignatureSpi {
|
||||
int newSigLength = sigLength;
|
||||
// Need to check RSA key length whenever a new private key is set
|
||||
if (privateKey != key) {
|
||||
if (privateKey instanceof RSAPrivateCrtKey) {
|
||||
RSAPrivateCrtKey rsaPrivKey = (RSAPrivateCrtKey) privateKey;
|
||||
BigInteger mod = rsaPrivKey.getModulus();
|
||||
newSigLength = checkRSAKeyLength(mod);
|
||||
try {
|
||||
if (!(privateKey instanceof RSAPrivateKey)) {
|
||||
throw new InvalidKeyException("RSAPrivateKey required");
|
||||
}
|
||||
RSAPrivateKey rsaPrivKey = (RSAPrivateKey) privateKey;
|
||||
BigInteger mod = rsaPrivKey.getModulus();
|
||||
newSigLength = checkRSAKeyLength(mod);
|
||||
BigInteger pe = rsaPrivKey.getPrivateExponent();
|
||||
try {
|
||||
if (rsaPrivKey instanceof RSAPrivateCrtKey) {
|
||||
RSAPrivateCrtKey rsaPrivCrtKey = (RSAPrivateCrtKey) rsaPrivKey;
|
||||
newKey = (NativeKey) keyFactory.engineGeneratePrivate
|
||||
(new RSAPrivateCrtKeySpec(mod,
|
||||
rsaPrivKey.getPublicExponent(),
|
||||
rsaPrivKey.getPrivateExponent(),
|
||||
rsaPrivKey.getPrimeP(),
|
||||
rsaPrivKey.getPrimeQ(),
|
||||
rsaPrivKey.getPrimeExponentP(),
|
||||
rsaPrivKey.getPrimeExponentQ(),
|
||||
rsaPrivKey.getCrtCoefficient()));
|
||||
} catch (InvalidKeySpecException ikse) {
|
||||
throw new InvalidKeyException(ikse);
|
||||
rsaPrivCrtKey.getPublicExponent(),
|
||||
pe,
|
||||
rsaPrivCrtKey.getPrimeP(),
|
||||
rsaPrivCrtKey.getPrimeQ(),
|
||||
rsaPrivCrtKey.getPrimeExponentP(),
|
||||
rsaPrivCrtKey.getPrimeExponentQ(),
|
||||
rsaPrivCrtKey.getCrtCoefficient()));
|
||||
} else {
|
||||
newKey = (NativeKey) keyFactory.engineGeneratePrivate
|
||||
(new RSAPrivateKeySpec(mod, pe));
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeyException("RSAPrivateCrtKey required");
|
||||
} catch (InvalidKeySpecException ikse) {
|
||||
throw new InvalidKeyException(ikse);
|
||||
}
|
||||
}
|
||||
init(true, newKey, newSigLength);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -696,6 +696,86 @@ JNIEXPORT void JNICALL Java_com_oracle_security_ucrypto_NativeKey_nativeFree
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeKey_nativeFree(id, numOfComponents);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: com_oracle_security_ucrypto_NativeKey_RSAPrivate
|
||||
* Method: nativeInit
|
||||
* Signature: ([B[B)J
|
||||
*/
|
||||
jlong JavaCritical_com_oracle_security_ucrypto_NativeKey_00024RSAPrivate_nativeInit
|
||||
(int modLen, jbyte* jMod, int privLen, jbyte* jPriv) {
|
||||
|
||||
unsigned char *mod, *priv;
|
||||
crypto_object_attribute_t* pKey = NULL;
|
||||
|
||||
pKey = calloc(2, sizeof(crypto_object_attribute_t));
|
||||
if (pKey == NULL) {
|
||||
return 0L;
|
||||
}
|
||||
mod = priv = NULL;
|
||||
mod = malloc(modLen);
|
||||
priv = malloc(privLen);
|
||||
if (mod == NULL || priv == NULL) {
|
||||
free(pKey);
|
||||
free(mod);
|
||||
free(priv);
|
||||
return 0L;
|
||||
} else {
|
||||
memcpy(mod, jMod, modLen);
|
||||
memcpy(priv, jPriv, privLen);
|
||||
}
|
||||
|
||||
// NOTE: numOfComponents should be 2
|
||||
pKey[0].oa_type = SUN_CKA_MODULUS;
|
||||
pKey[0].oa_value = (char*) mod;
|
||||
pKey[0].oa_value_len = (size_t) modLen;
|
||||
pKey[1].oa_type = SUN_CKA_PRIVATE_EXPONENT;
|
||||
pKey[1].oa_value = (char*) priv;
|
||||
pKey[1].oa_value_len = (size_t) privLen;
|
||||
|
||||
return (jlong) pKey;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_oracle_security_ucrypto_NativeKey_00024RSAPrivate_nativeInit
|
||||
(JNIEnv *env, jclass jCls, jbyteArray jMod, jbyteArray jPriv) {
|
||||
|
||||
int modLen, privLen;
|
||||
jbyte *bufMod, *bufPriv;
|
||||
crypto_object_attribute_t* pKey = NULL;
|
||||
|
||||
bufMod = bufPriv = NULL;
|
||||
|
||||
modLen = (*env)->GetArrayLength(env, jMod);
|
||||
bufMod = getBytes(env, jMod, 0, modLen);
|
||||
if ((*env)->ExceptionCheck(env)) goto cleanup;
|
||||
|
||||
privLen = (*env)->GetArrayLength(env, jPriv);
|
||||
bufPriv = getBytes(env, jPriv, 0, privLen);
|
||||
if ((*env)->ExceptionCheck(env)) goto cleanup;
|
||||
|
||||
// proceed if no error; otherwise free allocated memory
|
||||
pKey = calloc(2, sizeof(crypto_object_attribute_t));
|
||||
if (pKey == NULL) {
|
||||
throwOutOfMemoryError(env, NULL);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// NOTE: numOfComponents should be 2
|
||||
pKey[0].oa_type = SUN_CKA_MODULUS;
|
||||
pKey[0].oa_value = (char*) bufMod;
|
||||
pKey[0].oa_value_len = (size_t) modLen;
|
||||
pKey[1].oa_type = SUN_CKA_PRIVATE_EXPONENT;
|
||||
pKey[1].oa_value = (char*) bufPriv;
|
||||
pKey[1].oa_value_len = (size_t) privLen;
|
||||
return (jlong) pKey;
|
||||
|
||||
cleanup:
|
||||
free(bufMod);
|
||||
free(bufPriv);
|
||||
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: com_oracle_security_ucrypto_NativeKey_RSAPrivateCrt
|
||||
* Method: nativeInit
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8029849
|
||||
* @bug 8029849 8132082
|
||||
* @summary Make sure signing via encrypt and verifying via decrypt are not
|
||||
* supported by OracleUcrypto provider.
|
||||
* @author Anthony Scarpino
|
||||
@ -31,12 +31,10 @@
|
||||
*/
|
||||
|
||||
import java.util.Random;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyPair;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.security.spec.RSAPrivateKeySpec;
|
||||
import javax.crypto.Cipher;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
|
||||
public class CipherSignNotSupported extends UcryptoTest {
|
||||
|
||||
@ -69,27 +67,43 @@ public class CipherSignNotSupported extends UcryptoTest {
|
||||
c.init(Cipher.ENCRYPT_MODE, kp.getPublic());
|
||||
ct = c.doFinal(pt);
|
||||
// Decryption
|
||||
c.init(Cipher.DECRYPT_MODE, kp.getPrivate());
|
||||
c.doFinal(ct);
|
||||
// Sign
|
||||
try {
|
||||
c.init(Cipher.ENCRYPT_MODE, kp.getPrivate());
|
||||
ct = c.doFinal(pt);
|
||||
throw new RuntimeException("Encrypt operation should have failed.");
|
||||
} catch (InvalidKeyException e) {
|
||||
if (e.getMessage().compareTo("RSAPublicKey required for " +
|
||||
"encryption") != 0) {
|
||||
System.out.println("Wrong exception thrown.");
|
||||
throw e;
|
||||
PrivateKey[] privKeys = new PrivateKey[2];
|
||||
privKeys[0] = kp.getPrivate();
|
||||
if (privKeys[0] instanceof RSAPrivateCrtKey) {
|
||||
RSAPrivateCrtKey k = (RSAPrivateCrtKey) privKeys[0];
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
privKeys[1] = kf.generatePrivate
|
||||
(new RSAPrivateKeySpec(k.getModulus(), k.getPrivateExponent()));
|
||||
} else {
|
||||
privKeys = new PrivateKey[] {privKeys[0]};
|
||||
}
|
||||
|
||||
for (PrivateKey pk : privKeys) {
|
||||
System.out.println("Testing " + pk);
|
||||
c.init(Cipher.DECRYPT_MODE, pk);
|
||||
c.doFinal(ct);
|
||||
|
||||
// Sign
|
||||
try {
|
||||
c.init(Cipher.ENCRYPT_MODE, pk);
|
||||
ct = c.doFinal(pt);
|
||||
throw new RuntimeException("Encrypt operation should have failed.");
|
||||
} catch (InvalidKeyException e) {
|
||||
if (e.getMessage().compareTo("RSAPublicKey required for " +
|
||||
"encryption") != 0) {
|
||||
System.out.println("Wrong exception thrown.");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify
|
||||
try {
|
||||
c.init(Cipher.DECRYPT_MODE, kp.getPublic());
|
||||
c.doFinal(ct);
|
||||
throw new RuntimeException("Decrypt operation should have failed.");
|
||||
} catch (InvalidKeyException e) {
|
||||
if (e.getMessage().compareTo("RSAPrivateCrtKey required for " +
|
||||
if (e.getMessage().compareTo("RSAPrivateKey required for " +
|
||||
"decryption") != 0) {
|
||||
System.out.println("Wrong exception thrown.");
|
||||
throw e;
|
||||
|
Loading…
Reference in New Issue
Block a user