Cryptographic communication software Signal Server version 3.21 Compilation and installation Totem Notes (Ubuntu 20.04.2 LTS)
Published: 2023-09-07 10:27:59

# Compile

To get back to the point, continue talking about the Signal server. Firstly, the current code for Signal Server is version 2.92. Since there is an engineering file pom.xml, it should be compiled using Maven. The warehouse address is https://github.com/signalapp/Signal-Server/ .


```
mvn install -DskipTests
```

Compiling this version of code requires JDK 11+. If you use a lower version of JDK (such as in Ubuntu 16.04), Maven will report an error related to the number 11. If you are not familiar with it, this seemingly inexplicable error message may make you scratching your head for a while and become a pit. The final product of compilation is TextSecureServer-3.21. jar.

## Guide to Finding Others' Installation Experience

Compiled, now we need to deploy. First, Google the keywords Signal Server+Install/Deploy, and the most frequently cited one is the 1.88 version of the installation guide: "Signal Server Installation Guide". I guess this student may have used this post as a diversion to collect some consulting fees, so I didn't write it in that detail. But in the absence of official documents, this guide is also a good start (applause here), so we can only follow this for now.

## The main entrance for configuration: template file sample.yml

Take out the configuration template file sample.yaml from the Signal Server code repository and rename it to config.yml. Follow the above guide to configure, and you will find some changes to the configuration items in section 3.21. The later facts will prove that the yml template in the code repository is an old version that does not match the code at all, and it is almost useless to follow this configuration.

Account application for third-party interfaces

First, search for the third-party interface accounts that need to be applied for in yml and apply one by one.

## List of accounts to apply for

1. Twilio           
2. S3               minIO can replace
3. SQS             
4. reCAPTCHA  
5. GCM            
6. APN  
           
## Configure the domain name of the server

If you don't have a domain name, buy one. HTTPS/WSS (WebSocket Secure) services require verifying the certificate corresponding to the domain name, and the reverse connection of the Twilio service also requires configuring the domain name.

## Install Redis:

```
sudo apt-get install -y redis-server
```

Edit config:

```
sudo vi /etc/redis/redis.conf
```
reboot Redis：

```
sudo systemctl restart redis.service
```
## Install PostgreSQL

init data tables：

```
sudo apt-get install postgresql postgresql-contrib -y
sudo -u postgres psql -c "CREATE DATABASE accountsdb"
sudo -u postgres psql -c "CREATE DATABASE messagedb"
sudo -u postgres psql -c "CREATE DATABASE abusedb"
sudo -u postgres psql -c "CREATE DATABASE readdb"
sudo -u postgres psql -c "CREATE USER signal \
WITH ENCRYPTED PASSWORD 'password'"
```
edit config：

sudo vi /etc/postgresql/13/main/postgresql.conf
1、 listen_addresses='localhost'  to  listen_addresses='*'

2、add at last row ：
     host all all 0.0.0.0/0 md5

reboot database：

```
sudo systemctl restart postgresql
```
## Install coturn
a real-time voice chat based on the webrtc protocol

Enable port 3478 for firewall TCP and UDP, and also enable UDP.

install:

```
apt-get install libevent-dev

apt-get install libevent2
```

download

https://github.com/coturn/coturn

Install COTURN and generate certificate：

```
openssl req -x509 -newkey rsa:2048 -keyout /etc/turn_server_pkey.pem -out /etc/turn_server_cert.pem -days 99999 -nodes 
```

edit config file：

```
sudo vi /etc/turnserver.conf
```
edit field below：

```
$ sudo find / -name ssl-dhparams.pem
/usr/lib/python3/dist-packages/certbot/ssl-dhparams.pem
listening-ip=
external-ip=
use-auth-secret
static-auth-secret=
user=user1:password1
realm=www.yourdomain.com
cert=/etc/turn_server_cert.pem
pkey=/etc/turn_server_pkey.pem
cli-password=
dh-file=/usr/lib/python3/dist-packages/certbot/ssl-dhparams.pem
```
startup:

```
turnserver  -a -f -user=zjf:123456 -r www.lxrtalk.com 
```
 

Then use the following page to test the basic functions of COTURN (using Google Chrome may report error 701, it is recommended to use Firefox browser):

https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/

Note: There are two authentication mechanisms for COTURN, one for username and password, and the other for shared key (secret). The shared key is used by the REST interface, and the Signal Server requires the shared key. The page used to test the basic functions of COTURN only supports username+password authentication, and COTURN prioritizes the use of shared keys. That is to say, if both shared keys and username+password are provided, it cannot be tested on the above test page. So you need to disable the setting of the shared key first, and then restore the configuration of the shared key after passing the test on this page above.

 

After configuring the shared key, start the daemon:

```
sudo turnserver -a -o -v -n -u test:test -r "www.youerdomain.com"
```
Install Apache 2 (can be replaced by nginx)

Open port 80 of the server to the public on the firewall, as obtaining a free certificate requires connecting back to port 80 for verification.



Next, use a2enmod to open the required Apache proxy modules, use Certbot to obtain the free HTTPS certificate corresponding to the domain name, and finally prompt to redirect port 80 to port 443.

 

```
sudo apt-get install apache2
sudo a2enmod proxy proxy_http proxy_wstunnel
sudo add-apt-repository ppa:certbot/certbot -y
sudo apt-get update -y
sudo apt-get install python-certbot-apache -y
sudo certbot --authenticator standalone --installer apache -d "www.yourdomain.com" --pre-hook "systemctl stop apache2" --post-hook "systemctl start apache2"
```
 
 
Edit the configuration file of Apache's HTTPS site:



Sudo vi/etc/apache2/sites available/000-default le ssl. conf



Set up a reverse proxy to reverse proxy client requests from Apache's 443 port to Signal Server's 8080 port. 443's wss://reverse proxy to 8080's wss://, 443's https://reverse proxy to 8080's http://. In addition to using port 8080 to listen to ws://and http://, Signal Server also has an 8081 internal management port that can be opened using a browser http://127.0.0.1:8081 View.



The configuration sample of HTTPS site is as follows:

 

```

<IfModule mod_ssl.c>
<VirtualHost *:443>
　　ServerAdmin webmaster@excmple.com
　　DocumentRoot /var/www/html

　　AllowEncodedSlashes NoDecode

　　RewriteEngine on 
　　RewriteCond %{HTTP:Upgrade} websocket [NC]
　　RewriteCond %{HTTP:Connection} upgrade [NC]
　　RewriteRule .* "ws://localhost:8080$0" [P,L]

　　ProxyRequests off
　　ProxyPass / http://localhost:8080/
　　ProxyPassReverse / http://localhost:8080/

　　#LogLevel trace6

　　ErrorLog ${APACHE_LOG_DIR}/error.log
　　CustomLog ${APACHE_LOG_DIR}/access.log combined

　　ServerName yourdomain
　　SSLCertificateFile /etc/letsencrypt/live/www.yourdomain.com/fullchain.pem
　　SSLCertificateKeyFile /etc/letsencrypt/live/www.yourdomain.com/privkey.pem
　　Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
```
 
The HTTPS certificate is also required by the Signal client.



The above rules for reverse proxy are copied from "How to Reverse Proxy Websockets with Apache 2.4" through Google. This configuration first uses RewriteEngine to match the protocol promotion field in the client's wss://protocol. If there is a match, it will be forwarded to ws://, and if there is no match, ProxyPass will be used to reverse proxy to http://. However, there were three errors in the configuration in the original post that resulted in unsuccessful communication between Signal's client and server. The information provided here has been tampered with and corrected (interested parties can compare the errors themselves).



Since we encounter problems here, we need to use the two major tools of code farming: logs and debuggers. By modifying the log configuration field of the above site to LogLevel trace6, open the detailed logs of Apache and restart Apache. Then, analyze/var/log/access. log,/var/log/error. log to determine whether it is a problem with Apache or the Signal Server on port 8080.



AllowEncodedSlashes NoDecode is the sentence that needs to be added based on log analysis, as the URI sent by the iOS client to the Signal Server contains special characters such as slashes% 2F, plus signs% 2B, etc. If this sentence is not present, Apache may directly return 404 for these URIs without forwarding them to the Signal Server's 8080 port.



## Install CDS

Signal's CDS (contact discovery service, mobile phone contact book contact query function, which is more complex for security reasons). CDS is generally not deployed on the same machine as Signal Server, but uses independent special hardware to ensure its security, namely Intel SGX. SGX can create a hardware isolation area called enclave (Chinese name "enclave"). Currently, only Microsoft Azure and Intel have collaborated among cloud service providers to provide a trial environment for SGX on the cloud, with geographical limitations.



Interested parties can refer to the guide written by those who have been involved in CDS, and this guide is no longer maintained:
https://github.com/on-premise-signal/signal-setup-guide。

CDS code：https://github.com/signalapp/ContactDiscoveryService。

cds needs /intel/linux-sgx  https://github.com/intel/linux-sgx/tree/sgx_2.1.3#install-the-intelr-sgx-psw

## Signal Server The Virgin Run

Initialize the database and create tables (we haven't delved into why we haven't processed a database named readdb yet)：

```
java -jar TextSecureServer-3.21.jar  messagedb  migrate  config/config.yml
java -jar TextSecureServer-3.21.jar  accountdb  migrate  config/config.yml
java -jar TextSecureServer-3.21.jar  abusedb    migrate  config/config.yml
```
After filling in the fields of config.yml, try running it：

```
java -jar target/TextSecureServer-3.21.jar server config/config.yml
```
The result was not unexpected: it couldn't run. Fortunately, the printed error message clearly indicates which configuration item has the problem, and we can only dig the code (because Signal developers have said that if there is code, there is everything). From now on, we need to start the cycle of "viewing error messages ->Google search for errors/viewing code/debugging code ->changing configuration". This is the fate of coders in the era of relying on Google for development, and we need to prepare a relatively hidden one A fast ladder ensures that Google is online 24 hours a day.

## New configuration template file config.yml
Fortunately, the configuration item parsing code for Signal Server is concentrated in the file service/src/main/java/org/Whispersystems/textsecuregcm/WhisperServerConfiguration. java. Open the project using IDEA, browse to this file, find the type declaration and parsing code for each configuration item, extract them uniformly, and create a new config.yml template：

```
twilio:
　　accountId:
　　accountToken:
　　numbers: # array
　　localDomain:
　　messagingServicesId:

push:
　　queueSize: 200

attachments:
　　accessKey:
　　accessSecret:
　　bucket:
　　region:

cdn:
　　accessKey:
　　accessSecret:
　　bucket:
　　region:

cache:
　　url:
　　replicaUrls: # array

directory:
　　redis:
　　　　url:
　　　　replicaUrls: # array
　　sqs:
　　　　accessKey:
　　　　accessSecret:
　　　　queueUrl:
　　　　region: us-east-1
　　client:
　　　　userAuthenticationTokenSharedSecret: # hex string
　　　　userAuthenticationTokenUserIdSecret: # hex string
　　server:
　　　　replicationUrl:
　　　　replicationPassword:
　　　　replicationCaCertificate:

accountDatabaseCrawler:
　　chunkSize: 1000
　　chunkIntervalMs: 8000

pushScheduler:
　　url:
　　replicaUrls: # array

messageCache:
　　redis:
　　　　url:
　　　　replicaUrls: # array
　　persistDelayMinutes: 10

messageStore:
　　driverClass: org.postgresql.Driver
　　user:
　　password:
　　url: # example: jdbc:postgresql://127.0.0.1:5432/messagedb

abuseDatabase:
　　driverClass: org.postgresql.Driver
　　user:
　　password:
　　url: # example: jdbc:postgresql://127.0.0.1:5432/abusedb

testDevices: # array: [ { "number" => "", "code" => "" }, { "number" => "", "code" => "" }, ... ]

maxDevices: # array: [ { "number" => "", "count" => "" }, { "number" => "", "count" => "" }, ... ]

keysDatabase:
　　driverClass: org.postgresql.Driver
　　user:
　　password:
　　url: # example: jdbc:postgresql://127.0.0.1:5432/keysdb

accountsDatabase:
　　driverClass: org.postgresql.Driver
　　user:
　　password:
　　url: # example: jdbc:postgresql://127.0.0.1:5432/accountdb

read_database:
　　driverClass: org.postgresql.Driver
　　user:
　　password:
　　url: # example: jdbc:postgresql://127.0.0.1:5432/readdb

#limits: #use default values in source code

#httpClient: #use default values in source code

#webSocket: #use default values in source code

turn:
　　secret:
　　uris: # array

gcm:
　　senderId:
　　apiKey:

apn:
　　pushCertificate:
　　pushKey:
　　bundleId:
　　sandbox: false

unidentifiedDelivery:
　　certificate:
　　privateKey:
　　expiresDays:

voiceVerification:
　　url:
　　locales: # array

recaptcha:
　　secret:

storageService:
　　userAuthenticationTokenSharedSecret: # hex string

backupService:
　　userAuthenticationTokenSharedSecret: # hex string

transparentDataIndex: # Map<String, String>

#server: #use default values in source code
#logging: #use default values in source code
#metrics: #use default values in source code
```
 
Fill in the various fields of this config.yml again. Don't be too happy too early, it still won't run because some fields have strict requirements that are required in the code through Java annotations. At the beginning, it must have been taken for granted to fill in according to one's own literal understanding, which cannot meet the requirements.



For example, the configuration item 'testDevices' is written in WhisperServerConfiguration. java as follows：

```
@Valid
@NotNull
@JsonProperty
private List<TestDeviceConfiguration> testDevices = new LinkedList<>();
```
Note that testDevices is in JSON format, and the code directly gives new an empty set as the default value, which can be left unconfigured. However, if you configure this option in the yml file, it must have a value, which means it must be assigned at least one {number, code} pair. So it's better to comment it out in yml. Configuration items such as maxDevices are also similar. So why is it important to pay attention to Java annotations and corresponding usage code when sorting out these fields at the beginning.

 
For example, in the userAuthenticationTokenSharedSecret field under storageService and backupService, the parsing code is as follows. Obviously, this is a field in hexadecimal string format and cannot be an empty string.

```
@NotEmpty
@JsonProperty
private String userAuthenticationTokenSharedSecret;

public byte[] getUserAuthenticationTokenSharedSecret() throws DecoderException {
return Hex.decodeHex(userAuthenticationTokenSharedSecret.toCharArray());
```

## Configure APN certificate - ios push
A slightly more troublesome configuration is the APN certificate. First, you need to apply for a certificate file using an Apple Developer account, convert the certificate file to PEM format, and then paste the string from the PEM file into the yml file. Note that the path to the PEM file is not configured in yml. Why? Read The F * cking Code! The code directly reads the content of the PEM string instead of reading the PEM file, unless the code is changed to read from the PEM file (someone has already done so in the comments on the 1.88 installation guide post).


　　1：aps.cer 
　　2：push-pro.p12 

1、Convert APN certificate to PEM format：

openssl x509 -in aps.cer -inform der -out aps.pem
2、Convert APN private key to PEM format：

Enter the password for the. p12 file, and then set a PEM password to convert to PEM format：

```
openssl pkcs12 -nocerts -out push-pro.pem -in push-pro.p12
```

Enter the PEM password set in the previous step and remove the password：

```
openssl rsa -in push-pro.pem -out push-pro-key-noenc.pem
```
 
edit config.yml:

```
apn:
　　pushCertificate: |
　　　　-----BEGIN CERTIFICATE-----
　　　　MIIGCDCCBPCgAwIBAgIId9kHAiU6ZxIwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
　　　　...
　　　　...
mYm1yVxw4lLirPPyva9YOqX626RzyKQ0mcOiV0vURjALRiAkNklfpgr877wilvdS
　　　　dynVUqLbErSibtAh
　　　　-----END CERTIFICATE-----
```
 
## Configure the key for Sealed Sender

More troublesome is the configuration of the unidentifiedDelivery feature. This function is also known as Sealed Sender, which means anonymous chat messages are sent. Design reference for functions https://signal.org/blog/sealed-sender/ . This feature requires certificate and private key privateKey, but it is not in standard formats such as PEM. Still flipping through the relevant parsing code, you will find that Signal Server comes with command line parameters to generate this data. The specific code can be found in the file service/src/main/java/org/whistlersystems/textsecuregcm/workers/CertificateCommand. java. From this code, it can be seen that there are two steps required.



### Step 1: Generate the CA certificate and private key. This CA certificate (note that it is not the private key of the CA) may also be used in the Signal client, and the client can access the server through HTTPS proxy.

```
java -jar target/TextSecureServer-3.21.jar certificate --ca
```

### Step 2: Use the private key of the CA to generate the server's certificate。

```
java -jar target/TextSecureServer-3.21.jar certificate --key XXX --id NNNN

```
XXX is the private key of CA, and NNNN is a numerical number determined by oneself.

```
java -jar target/TextSecureServer-3.21.jar certificate --ca

Public key : BVttrBH0mkQ5QVh2BX1CMbCyTNdHANg6BVjo+RxDgJgX
Private key: 0MFfk96SBom3rYAU7p861yz/gZCbgXjeYAfWDSL6LW8=
ubuntu@ip-172-31-33-23:~/Signal-Server/service$ java -jar target/TextSecureServer-3.21.jar certificate --key 0MFfk96SBom3rYAU7p861yz/gZCbgXjeYAfWDSL6LW8= --id 1
Certificate: CiUIARIhBdRco3McNmr8yy1UIrX3fFqPqBv3CxqC0IJObRB7SKgZEkB+vzBUqH1I6O+SKZSqDllFLkj/DA2qYuqXWvF+KSLSKMIq3xVAciHBtpGOLE19fmJHHIukUVSp0MIPK7y4l24A
Private key: AIvy5XjaaWiuQgN1omJ/v18DrG9bpBePC5jEei6HWlc=
```
 


 The final configuration file config.yml

The final content of config.yml obtained. With this configuration, only the initial registration of the mobile client can be completed, and many other functions still need to be tested. The Signal client also has some configurations to modify. Let's get here first, no further ado.

 

```



twilio:
    accountId: ACf1111111111111111111111111111
    accountToken: 3e22222222222222222222222222
    numbers:
        - +1923456789
    localDomain: www.youerdomain.com
    messagingServicesId: MG3333333333333333333333333333

push:
    queueSize: 200

attachments:
    accessKey: AKIAAAAAABBBBBCCCCCCC
    accessSecret: QhABCCCCCCCCCCCCCDDDDDDDDDDDDDDDD
    bucket: fake
    region: ap-southeast-1

cdn:
    accessKey: AKIAAAAAABBBBBCCCCCCC
    accessSecret: QhABCCCCCCCCCCCCCDDDDDDDDDDDDDDDD
    bucket: fake
    region: ap-southeast-1

cache:
    url: http://127.0.0.1:6379
    replicaUrls: [ "http://127.0.0.1:6379" ]

directory:
    redis:
        url: http://127.0.0.1:6379
        replicaUrls: [ "http://127.0.0.1:6379" ]
    sqs:
        accessKey: AKIAAAAAABBBBBCCCCCCC
        accessSecret: QhABCCCCCCCCCCCCCDDDDDDDDDDDDDDDD
        queueUrl: https://sqs.ap-southeast-1.amazonaws.com/17093374723/fake
        region: ap-southeast-1
    client:
        userAuthenticationTokenSharedSecret: 33445566 #fake
        userAuthenticationTokenUserIdSecret: 22117788 #fake
    server:
        replicationUrl: http://127.0.0.1 #fake
        replicationPassword: fake
        replicationCaCertificate: |   #fake
                -----BEGIN CERTIFICATE-----
                MIIGCDCCBPCgAwIBAgIId9kHAiU6ZxIwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
                mYm1yVxw4lLirPPyva9YOqX626RzyKQ0mcOiV0vURjALRiAkNklfpgr877wilvdS
                dynVUqLbErSibtAh
                -----END CERTIFICATE-----

accountDatabaseCrawler:
    chunkSize: 1000
    chunkIntervalMs: 80000000

pushScheduler:
    url: http://127.0.0.1:6379
    replicaUrls: [ "http://127.0.0.1:6379" ]

messageCache:
    redis:
        url: http://127.0.0.1:6379
        replicaUrls: [ "http://127.0.0.1:6379" ]
    persistDelayMinutes: 10

messageStore:
    driverClass: org.postgresql.Driver
    user: signal
    password: password
    url: jdbc:postgresql://127.0.0.1:5432/messagedb

abuseDatabase:
    driverClass: org.postgresql.Driver
    user: signal
    password: password
    url: jdbc:postgresql://127.0.0.1:5432/abusedb

keysDatabase:
    driverClass: org.postgresql.Driver
    user: signal
    password: password
    url: jdbc:postgresql://127.0.0.1:5432/keysdb

accountsDatabase:
    driverClass: org.postgresql.Driver
    user: signal
    password: password
    url: jdbc:postgresql://127.0.0.1:5432/accountdb

read_database:
    driverClass: org.postgresql.Driver
    user: signal
    password: password
    url: jdbc:postgresql://127.0.0.1:5432/readdb

#limits: #use default values in source code

#httpClient: #use default values in source code

#webSocket: #use default values in source code
#viedo call
turn:
    secret: turn 
    uris:
        - turn:127.0.0.1:3478?transport=udp

gcm:
    senderId: 71722022434122
    apiKey: AAAfffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaakkkkkkkkkkkkkkkkeeeeeeeeeeeee

apn:
    bundleId: fake
    pushCertificate: |
        -----BEGIN CERTIFICATE-----
        MIIGCDCCBPCgAwIBAgIId9kHAiU6ZxIwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
        mYm1yVxw4lLirPPyva9YOqX626RzyKQ0mcOiV0vURjALRiAkNklfpgr877wilvdS
        dynVUqLbErSibtAh
        -----END CERTIFICATE-----
    pushKey: |
        -----BEGIN RSA PRIVATE KEY-----
        MIIEpAIBAAKCAQEAsH9uXZaGp2DZ0sfsSI9ovrv4hI2OjEDbzefv5+ZWy0kVdL/r
        2UtkH7KXehd9wueaIaZ5+j9PKy3t6x4GPZE4b/fkuXToCijOsbCfhA==
        -----END RSA PRIVATE KEY-----

unidentifiedDelivery:
    certificate: |
        CiYI5A8SIQUO+IlR4c9wwDNmYkD4RRJAob3q9UG4vwkaV1k9iiBPhAXv5liN85Q8vFwEJawOWxIng6WwXo/hoXNFb/TinsRPlI5XuSfQs23FDUVRVXZggQ== #fake
    privateKey: |
        iCTXE4i2P1t5Y7Vy7SujSQmFfW1E4=   #fake
    expiresDays: 365

voiceVerification:
    url: http://127.0.0.1 #fake
    locales: [ "en-US" ]  #fake

recaptcha:
    secret: 6LfFFFFFFAAAAAAKKKKKKEEEEEEgJ7kX-pSL

storageService:
    userAuthenticationTokenSharedSecret: 112233445566 #fake

backupService:
    userAuthenticationTokenSharedSecret: 778899AABBCC #fake

transparentDataIndex: # Map<String, String>

#server: #use default values in source code
#logging: #use default values in source code
#metrics: #use default values in source code
 
### The issue of mismatched server and client code versions
```

Finally, there is one more pitfall to note: currently, on GitHub (2020 03 16), the latest code for the Master branch of Signal Server does not match the latest code for the Master branch of iOS client. What about the evidence? It is still through the two major tools of logs and debuggers.

 

### generagte zkParams:

```
java -jar  target/TextSecureServer-3.21.jar zkparams
```

### edit config.yml :

```
zkConfig: 
  serverSecret: JyH42NTp10znCdBYdvQw9wGuXTk0zjh1PxwxFPlbOAjp4MYv7Lw5FkCNJyidCjknLr1QPB9MD4SHn1t8VtV3DFYAd16B+vQvtHvwdG6N1yw4axE13fOjnK88412ibz5x+JBjg4DlxIkuTQIvYwNqanG3mBNOwxdVsSIUeJeiuggGN3QiKjXY1ON7AOVdlqBVl2oBsayuQXmPxQd02zGVCMnOkeExqF7n2yZjLKcJVStbSLJUYu6JLkUEayEFEKYIrTEbYXmqJ+LGCgj2yTL0XqdrY8RINIWiW6uNVH2azwFM22tTMUppcI9FbeCjeCtZXi68ZiIz923ghAd1m5PzCSZ2UhcYCJvjJzwAOV6YZl7bUlE6BGkloZPfnc+FH4sAoi79doKTj+FAK4geClr3fPZEh8igLt4QxGEpJrn1bwQLgrJWBqwXZlAYZ31w2IaHauPclyNm5cgA/76S1jvtC24cOSMNisn1LK0qLuSH7ucwPj9tYuKUn0gMe7zZf9Fo9h+Zpu03LHhXl34ECGZt/3c8xMlpHwMDuxyt4vTdJR1FT/IDQL6gJT+gVV+TbOzb+q4E3JjmQqpmOvscr8tvCQqz3340pRiW353GPVRJrPgikM0VEgRL1Pzcg5Nc5lMKpplO/sCEylBGltp3740MtacXzDWMi1CkXpFe0ARqrhedb6NLlwAVkxV+cdYJtJA0UGhR0fuI172mZPhtC/VJDLxQQOOdK+ZQ88bIILDZm9zdek8FEtRk2iXTD4tVKvIJRcnq3pn5RdO21pVmWfKtQ1tNT7l5JqAAUomxjK8aIgygFlv58gAtNpeDa3kVSDsHJHiHy01psIHsaHLvM1TQA7xd/3vHpYFOgLHQGXqcYg2wuWQ1Ld0j9uUM+8yFVEQN4LRY4KeKtnpa/v1o4eNA+uIm/CNXcbXVifS71ch+uA5b/1fBpgBsmS0easpeQZB2PLLEJFtvJbL2ln2Uc6ZrBtpvXGCNEEATIbuJscTlBqMrv9CC21A6iPBRQZAf0WkFmviwr3C06gyJ8tiwriYWSTivH5LgJcIL24F3vUf2J1ke5dO6ZIjJxPm9MEl4YQqnjHESFsuv1BXzQRsn9lvwEZZygL/292ktQqaZ7RovScLM9AgZbNxkjeuhD9dbxIEI8BMyEnTr82yZnXE5MSEJy38gVloVHm4L+PLHJZwcXVQ
  serverPublic: bhw5Iw2KyfUsrSou5Ifu5zA+P21i4pSfSAx7vNl/0Wj2H5mm7TcseFeXfgQIZm3/dzzEyWkfAwO7HK3i9N0lHZr4sK9wtOoMifLYsK4mFkk4rx+S4CXCC9uBd71H9idZHuXTumSIycT5vTBJeGEKp4xxEhbLr9QV80EbJ/Zb8BHwEzISdOvzbJmdcTkxIQnLfyBWWhUebgv48sclnBxdVA
  enabled: ture
```

### serverPublic base64，edit ios config

```
serverPublicParamsBase64="Ymh3NUl3Mkt5ZlVzclNvdTVJZnU1ekErUDIxaTRwU2ZTQXg3dk5sLzBXajJINW1tN1Rjc2VGZVhmZ1FJWm0zL2R6ekV5V2tmQXdPN0hLM2k5TjBsSFpyNHNLOXd0T29NaWZMWXNLNG1Ga2s0cngrUzRDWENDOXVCZDcxSDlpZFpIdVhUdW1TSXljVDV2VEJKZUdFS3A0eHhFaGJMcjlRVjgwRWJKL1piOEJId0V6SVNkT3Z6YkptZGNUa3hJUW5MZnlCV1doVWViZ3Y0OHNjbG5CeGRWQQ=="
```

### upgrade zkgoup-java to 0.7.0 ,generate again.

```
Public: APYNGrlVtwuNGiQSgYK/CduMeLLbPVejvNeS/vATQ9hOfipj8mR94eeS28PCVRXUpSRNMRc0WAjqurHi82XfIX+W4lcDbAljgGucmQYyZ7W5zZsuHSbmH3b77p7nXGP/asSFJoOJXAGi8m2iXhVybpQ0AeyOi36an5E6vSOzzdEy5kMLWKjHdeTJOsCCezA0CAa7eLJA+UXp60liO73wZy4
Private: ACyAUqsm7WRUIWlWPkizJ6xAq0ZjZmlNCnDo28GGifMMCjbOnPZIGsmd2ICN5EqL1BjqO/O9m0otsbLjz8CehwaovbaTOKnbJPaXoTPWkSDH9yPcOnmvj6xdm4DOiB37MVTut6DL9yHVdg7S/XnR2amQ6WoyGRI+MwPCFspVimwHKd4oYHDrKTjCndhZnKx2oQcgXVp05JtJfsIRBrBRNg8PEr6FAcIRWeCG6quLxIqfcuPf3Bdn1AgvQz5II369BZsU5rVKTnwjF6CZmVN8UO++JqaK9kqO9YLP3zpMLB4AEmnezjJ7kHFNQaVwVRm0p9Rt6q42P7UssjavjQXngQzEEMwqHt+K0EJuQcLP6K2OtCw4Ti/fFOZ1sUDFN7DVC/YNGrlVtwuNGiQSgYK/CduMeLLbPVejvNeS/vATQ9hOfipj8mR94eeS28PCVRXUpSRNMRc0WAjqurHi82XfIX+vw1vBwQ3XEK0uHIOtKzCh9QAMrxYJxHIdMjSV5aXIBL4xDBDGUP55J3FtzBn58jEMd8C2cwgfhfJGhcgXxh0A8Ne44mRVHX1jXSdtDQ2b+LSxyGrErI5hPgfImPpF3zqDNt4ddJU8EOv7W5orrnEOb9jCoKdc0lb5SWGthwXTDldR6IeiVdnCn7vQBFei9icAPMQ6O14c/dzZogRNIVEBBLaucQxAeWLjKBYdmleRWhYH6mOOW7nMD7GKBkwS3QAgQyAAqp2pFLsWv2Tn8WdcfieFCMaY/p1DTrP/x3zUCiC3TbjGXd0fi4/kpdW4C6QxS+HjHIEESCGCbnrdlSQKKoYxNSJtu4/NtctgFzxo7anCcfi/o8w2O+yJy5ghdgeW4lcDbAljgGucmQYyZ7W5zZsuHSbmH3b77p7nXGP/asSFJoOJXAGi8m2iXhVybpQ0AeyOi36an5E6vSOzzdEy4MoLOGYEd9irEB1caHrh5kmxUmmYyq4RC6HWu5wjJQvmQwtYqMd15Mk6wIJ7MDQIBrt4skD5RenrSWI7vfBnLg
```


any question send email to 418982099qq@gmail.com