Регистрация  |  Вход

REST API Certificate Authentication

Есть внешний сервис у которого я должен дергать ручки, сервис находится за прокси с аутентификацией по сертификату. Можно ли подписать запрос сертификатом напрямую в APEX без импортирования сертификатов в SF из JKS?

Есть внешний сервис у которого я должен дергать ручки, сервис находится за прокси с аутентификацией по сертификату. Можно ли подписать запрос сертификатом напрямую в APEX без импортирования сертификатов в SF из JKS?

Интересная тема! Активно работаю со всякими интеграциями, но никогда не сталкивался с таким. Тоже с удовольствием послушаю про решение.
С ходу нагугл вот такое
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts_client_certs_http.htm
Но не уверен что это твой случай.
Я так понимаю что под "без импортирования сертификатов в SF из JKS" ты имеешь в виду то что говорится в доке по ссылке в пункте "1. Generate a certificate. Note the Unique Name of the certificate."?
Если получится сделать, не кидай тему, напиши здесь про решение!

Интересная тема! Активно работаю со всякими интеграциями, но никогда не сталкивался с таким. Тоже с удовольствием послушаю про решение.
С ходу нагугл вот такое 
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts_client_certs_http.htm
Но не уверен что это твой случай. 
Я так понимаю что под [i]"без импортирования сертификатов в SF из JKS"[/i] ты имеешь в виду то что говорится в доке по ссылке в пункте "1. Generate a certificate. Note the Unique Name of the certificate."?
Если получится сделать, не кидай тему, напиши здесь про решение!

Да, вся суть в том, чтобы использовать req.setClientCertificateName('DocSampleCert'); нужно имортировать JKS, а для его создания надо приватный ключ, а его по понятным причинам раздавать налево и направо никто не будет.

Да, вся суть в том, чтобы использовать req.setClientCertificateName('DocSampleCert'); нужно имортировать JKS, а для его создания надо приватный ключ, а его по понятным причинам раздавать налево и направо никто не будет.

А попросить чтобы на том прокси дали возможность авторизироваться через Basic или Token авторизацию и добавлять их в хедер?

А попросить чтобы на том прокси дали возможность авторизироваться через Basic или Token авторизацию и добавлять их в хедер?

А там Basic тоже есть ) Это требования безопасников, так как у SF большой рендж IP с которых он может стучаться (https://help.salesforce.com/articleView?id=000321501&type=1&mode=1), то вместо добавления этих адресов в iptables, они просто ставят прокси, который принимает запросы только от подсетей SF.

А может у SF есть возможность слать запросы с одного IP, никто не сталкивался?

А там Basic тоже есть ) Это требования безопасников, так как у SF большой рендж IP с которых он может стучаться (https://help.salesforce.com/articleView?id=000321501&type=1&mode=1), то вместо добавления этих адресов в iptables, они просто ставят прокси, который принимает запросы только от подсетей SF.

А может у SF есть возможность слать запросы с одного IP, никто не сталкивался? 

Нет, нету. Я когда-то давно тоже пробовал нагуглить как не прописывать весь диапазон IP адресов. Не нашел.

Нет, нету. Я когда-то давно тоже пробовал нагуглить как не прописывать весь диапазон IP адресов. Не нашел.

В итоге выпустили нам отдельный сертификат. Letsencrypt не стали, чтобы каждые 3 месяца не проделывать следующие шаги, к которым пришел через боль и страдания Поэтому сделал себе заметку, которой делюсь.

Итак имеем PEM сертификат mycert.crt и приватный ключ myserver.key,
а также установленные openssl и java keytool.

Необходимо сделать 3 шага:

1. Подготовить в виде PEM полную цепочку сертификатов.

2. Конвертировать эту цепочку из PEM в PKCS12.

3. Конвертировать цепочку из PKCS12 в JKS.


1.
Для начала проверим что ключ от нашего сертификата:

openssl x509 -noout -modulus -in mycert.crt | openssl md5
openssl rsa -noout -modulus -in myserver.key | openssl md5

хеши должны совпадать.

Конечный JKS должен содержать в себе всю цепочку сертификатов до корневого(они обычно в общем доступе),
корневой должен быть одним из этого списка:
https://cs32.salesforce.com/cacerts.jsp

команда для проверки что наш серт из этой цепочки:

openssl verify -verbose -CAfile Root.crt -untrusted Intermediate.crt ssl_certificate.crt

но этого недостаточно, поэтому проверяем еще так:

openssl x509 -noout -in mycert.crt -text | grep -A1 'Authority Key Identifier'
openssl x509 -noout -in Intermediate.crt -text | grep -A1 'Subject Key Identifier'

и так идем до корневого где для каждого сертификата его 'Authority Key Identifier'
должен быть равен 'Subject Key Identifier' сертификата, которым он подписан.


Связываем промежуточные и корневой сертификаты от дочернего к родительскому:

cat intermediate1.crt intermediate2.crt rootca.crt > cabundle.crt

2.
Делаем PKCS12:

openssl pkcs12 -export -chain -inkey myserver.key -in mycert.crt -name "MySuperCert" -caname "Intermediate_1" -caname "Intermediate_2" -caname "Root_CA" -CAfile cabundle.crt -out mychain.p12

Обратите внимание на ключ "-chain". Без него это будет просто разрозненная пачка каких-то сертификатов в контейнере. А нам нужно, чтобы имела место быть явная связь между ними.


3.
PKCS12 в JKS:

./keytool -importkeystore -destkeypass secret -deststorepass secret -destkeystore container.jks -srckeystore mychain.p12 -srcstoretype PKCS12 -srcstorepass secret -alias "MySuperCert"

-deststorepass - пароль от JKS контейнера, Salesforce требует длину от 6 до 8 символов
-alias - допустимы цифры буквы и подчеркивания(кроме двойных, конечных и начальных)

Теперь можно загрузить JKS в Salesforce и делать красиво:

request.setClientCertificateName('MySuperCert');

Cертификат не загружается в SF?
Если у вас стоит последний DataLoader, то скорее всего выполнив команду:

java -version

вы увидите следующее:
openjdk version "12" 2019-03-19
OpenJDK Runtime Environment Zulu12.1+3-CA (build 12+33)
OpenJDK 64-Bit Server VM Zulu12.1+3-CA (build 12+33, mixed mode, sharing)

То тут есть засада, Salesforce принимает JKS сделанный только в java 8 ¯\_(ツ)_/¯
Ищем подходящий keytool и повторяем последний шаг.

sudo find / -name keytool

я нашел у себя
./jdk1.8.0_201.jdk/Contents/Home/jre/bin/keytool
и сним все заработало

В итоге выпустили нам отдельный сертификат. Letsencrypt не стали, чтобы каждые 3 месяца не проделывать следующие шаги, к которым пришел через боль и страдания ;) Поэтому сделал себе заметку, которой делюсь.

Итак имеем PEM сертификат mycert.crt и приватный ключ myserver.key,
а также установленные openssl и java keytool. 

Необходимо сделать 3 шага:

1. Подготовить в виде PEM полную цепочку сертификатов.

2. Конвертировать эту цепочку из PEM в PKCS12.

3. Конвертировать цепочку из PKCS12 в JKS.


1.
Для начала проверим что ключ от нашего сертификата:
[code]
openssl x509 -noout -modulus -in mycert.crt | openssl md5
openssl rsa -noout -modulus -in myserver.key | openssl md5
[/code]
хеши должны совпадать.

Конечный JKS должен содержать в себе всю цепочку сертификатов до корневого(они обычно в общем доступе), 
корневой должен быть одним из этого списка:
https://cs32.salesforce.com/cacerts.jsp

команда для проверки что наш серт из этой цепочки:
[code]
openssl verify -verbose -CAfile Root.crt -untrusted Intermediate.crt ssl_certificate.crt
[/code]

но этого недостаточно, поэтому проверяем еще так:
[code]
openssl x509 -noout -in mycert.crt -text | grep -A1 'Authority Key Identifier'
openssl x509 -noout -in Intermediate.crt -text | grep -A1 'Subject Key Identifier'
[/code]
и так идем до корневого где для каждого сертификата его 'Authority Key Identifier' 
должен быть равен 'Subject Key Identifier' сертификата, которым он подписан.


Связываем промежуточные и корневой сертификаты от дочернего к родительскому:
[code]
cat intermediate1.crt intermediate2.crt rootca.crt > cabundle.crt
[/code]

2.
Делаем PKCS12:
[code]
openssl pkcs12 -export -chain -inkey myserver.key -in mycert.crt -name "MySuperCert" -caname "Intermediate_1" -caname "Intermediate_2" -caname "Root_CA" -CAfile cabundle.crt -out mychain.p12
[/code]

Обратите внимание на ключ "-chain". Без него это будет просто разрозненная пачка каких-то сертификатов в контейнере. А нам нужно, чтобы имела место быть явная связь между ними.


3.
PKCS12 в JKS:
[code]
./keytool -importkeystore -destkeypass secret -deststorepass secret -destkeystore container.jks -srckeystore mychain.p12 -srcstoretype PKCS12 -srcstorepass secret -alias "MySuperCert"
[/code]

-deststorepass - пароль от JKS контейнера, Salesforce требует длину от 6 до 8 символов
-alias - допустимы цифры буквы и подчеркивания(кроме двойных, конечных и начальных)

Теперь можно загрузить JKS в Salesforce и делать красиво:
[code]
request.setClientCertificateName('MySuperCert');
[/code]

Cертификат не загружается в SF?
Если у вас стоит последний DataLoader, то скорее всего выполнив команду:
[code]
java -version
[/code]
вы увидите следующее:
openjdk version "12" 2019-03-19
OpenJDK Runtime Environment Zulu12.1+3-CA (build 12+33)
OpenJDK 64-Bit Server VM Zulu12.1+3-CA (build 12+33, mixed mode, sharing)

То тут есть засада, Salesforce принимает JKS сделанный только в java 8 ¯\_(ツ)_/¯
Ищем подходящий keytool и повторяем последний шаг.
[code]
sudo find / -name keytool
[/code]
я нашел у себя
./jdk1.8.0_201.jdk/Contents/Home/jre/bin/keytool
и сним все заработало



Низкий поклон за столь подробное описание решения!

Низкий поклон за столь подробное описание решения! 

[img]http://memesmix.net/media/created/jhntve.jpg[/img]

о, хорошую тему подняли
у меня безопасники настаивают на использовании нашего само-подписанного сертификата
они прочитали сэйлсфорс хелп и тыкают в эту статью
https://help.salesforce.com/articleView?language=en_US&type=1&mode=1&id=000326722
When using mutual authentication/2-way SSL, Salesforce.com can present a self-signed certificate to the target host (that must present a CA signed certificate to Salesforce)

flow который они требуют такой:
сэйлсфорс шлет наш self signed сертификат и сервер ответит с CA-signed сертификатом

Я не могу им доказать что это невозможно. Я так понял сэйлсфорс acting as a client нельзя заставить выслать не CA-Signed сертифкат, ни при помощи mTls, ни при помощи обычного сертификата. И self-signed на который они ссылаются в доке выше сэйлсфорс имеет ввиду только Salesforce-self-signed а не 3rd party, но к сожалению это прямо не указано.

Сертификат я сделал правильно, при загрузке сэйлсфорс видит что подписан моей конторой (self signed)в опциях это видно, но! тип всё равно ставится CA-Signed. И при попытке использовать летит эксепшен
System.CalloutException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
я так понимаю это потому что чейн обрывается (self signed) и нет пути до CA-root, что логично.
Единственный вариант когда при загрузке сертификата сэйлсфорс корректно ставит тип "self-signed" это когда сертификат self-signed самим сэйлсфорсом.
Пробовали использовать mTLS, тут еще немного труднее. Я так понял эта настройка только для связи С сэйлсфорсом, и сэйлсфорс когда выступает как клиент не шлет сертификат.

Если у кого есть информация на тему буду рад выслушать.

о, хорошую тему подняли
у меня безопасники настаивают на использовании нашего само-подписанного сертификата
они прочитали сэйлсфорс хелп и тыкают в эту статью
https://help.salesforce.com/articleView?language=en_US&type=1&mode=1&id=000326722
When using mutual authentication/2-way SSL, Salesforce.com can present a self-signed certificate to the target host (that must present a CA signed certificate to Salesforce)

flow который они требуют такой:
сэйлсфорс шлет наш self signed сертификат и сервер ответит с CA-signed сертификатом

Я не могу им доказать что это невозможно. Я так понял сэйлсфорс acting as a client нельзя заставить выслать не CA-Signed сертифкат, ни при помощи mTls, ни при помощи обычного сертификата. И self-signed на который они ссылаются в доке выше сэйлсфорс имеет ввиду только Salesforce-self-signed а не 3rd party, но к сожалению это прямо не указано.

Сертификат я сделал правильно, при загрузке сэйлсфорс видит что подписан моей конторой (self signed)в опциях это видно, но! тип всё равно ставится CA-Signed. И при попытке использовать летит эксепшен
System.CalloutException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
я так понимаю это потому что чейн обрывается (self signed) и нет пути до CA-root, что логично.
Единственный вариант когда при загрузке сертификата сэйлсфорс корректно ставит тип "self-signed"  это когда сертификат self-signed самим сэйлсфорсом.
Пробовали использовать mTLS, тут еще немного труднее. Я так понял эта настройка только для связи С сэйлсфорсом, и сэйлсфорс когда выступает как клиент не шлет сертификат.

Если у кого есть информация на тему буду рад выслушать.