Mutual authentication(two-way authentication) Callin

Callout 時の Mutual authentication については、サーバ編クライアント編 を参照。

Callin(外部から Salesforce への API 接続)時には、Salesforce のサーバ認証とは別に、クライアント認証も行えるようになっている(Salesforce.com Winter ’14 リリースノート/相互認証)。注意点としては、Callout 時のサーバ認証時と同様に Salesforce で信頼される認証局での署名である必要がある。対応している証明書は Outbound Messaging SSL CA Certificates にリストされている。

クライアント証明書のアップロードと、接続ユーザの SSL/TLS 相互認証を適用 が有効になれば設定は完了となる。テストとしては、cURL で以下の通り実行し疎通できれば良い。

$ curl -k https://ap.salesforce.com:8443/services/Soap/u/33.0 -H "Content-Type: text/xml; charset=UTF-8" -H "SOAPAction: login" -d @login.xml --cert mycrient.crt --key mycrient.key
<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Body>
    <n1:login xmlns:n1="urn:partner.soap.sforce.com">
      <n1:username>my username</n1:username>
      <n1:password>my password</n1:password>
    </n1:login>
  </env:Body>
</env:Envelope>

Mutual authentication(two-way authentication) Callout (クライアント認証 編)

サーバ編はこちら

Callout の構成としては以下の通り。

  • Server: Nginx (ssl.mydomain.com という Virtual ドメイン)
  • Client : Salesforce
  • Salesforce を Client とする場合、クライアント認証で利用できる証明書は、自己署名証明書と認証機関署名証明書の 2 種類となる。自己署名証明書は Salesforce が署名しており、認証機関署名証明書は他の CA が署名した証明書となる。認証機関署名証明書 を作成するには、画面から CSR を作成し CA で署名した証明書(CRT)を再度インポートする必要がある。自己署名証明書は、Salesforce が署名しているので、利用するにはサーバ側でその旨の設定をする必要がある。今回は、自己署名証明書を利用する。

    自己署名証明書の作成

    [管理者設定] | [セキュリティコントロール] | [証明書と鍵の管理] より、”自己署名証明書の作成” をクリックしフォームを入力することで作成できる。その後、画面上からダウンロードできるようになる。

    サーバ側の設定

    証明書の中身を確認する。

    $ openssl x509 -text -noout -in SFDCSelfSignedCert.crt
    

    自己署名証明書は自身の鍵で署名されているので、そのままサーバ側で配置する。

    # vi /etc/nginx/sites-available/ssl.mydomain.com
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      # for SSL(Client certificate)
      ssl_verify_client on;
      ssl_client_certificate /etc/nginx/ssl/SFDCSelfSignedCert.crt;
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    # service nginx restart
    

    クライアント側の設定

    クライアント側から明示的に証明書を利用するように、Callout を実施する。

    String endPoint = 'https://ssl.mydomain.com/index.html';
    HttpRequest req = new HttpRequest();
    
    req.setEndpoint(endPoint);
    req.setMethod('GET');
    req.setTimeout(10000);
    req.setClientCertificateName('SFDCSelfSignedCert');
    
    Http http = new Http();
    HttpResponse res = http.send(req);
    System.debug('zzz getStatusCode: ' + res.getStatusCode());
    System.debug('zzz getBody: ' + res.getBody());
    

    クライアント証明書を指定しないと、以下のエラーとなる。

    400 Bad Request
    No required SSL certificate was sent
    

    サーバー側のログ(/var/log/nginx/ssl-error.log info)でも確認できる。

    2015/03/11 16:34:55 [info] 17277#0: *2 client sent no required SSL certificate while reading client request headers, client: xx.xx.xx.xx, server: ssl.mydomain.com, request: "GET /index.html HTTP/1.1", host: "ssl.mydomain.com"
    

    証明書を利用した成功時のログ。

    2015/03/11 16:46:08 [info] 17279#0: *5 client sent invalid header line: "SFDC_STACK_DEPTH: 1" while reading client request headers, client: xx.xx.xx.xx, server: ssl.mydomain.com, request: "GET /index.html HTTP/1.1"
    

    Mutual authentication(two-way authentication) Callout (サーバ認証 編)

    Callout の構成としては以下の通り。

  • Server: Nginx (ssl.mydomain.com という Virtual ドメイン)
  • Client : Salesforce
  • Salesforce の Callout の場合、サーバ側の SSL 証明書は Salesforce で信頼される認証局の証明書である必要がある。対応している証明書は Outbound Messaging SSL CA Certificates にリストされている。そのうち、今回は以下のような証明書。

  • ルート証明書(Root CA): Add Trust External CA
  • 中間証明書(Intermediate CA): “COMODO RSA Add Trust CA”
  • 中間証明書(Intermediate CA): “COMODO RSA Domain Validation Secure Server CA”
  • サーバ証明書の鍵と CSR(Certificate Signing Request) 作成

    # cd /root
    # mkdir .ssl_certification
    
    # openssl genrsa -out ssl.mydomain.com.key 2048
    # openssl req -new -key ssl.mydomain.com.key -out ssl.mydomain.com.csr -sha1
      Country Name (2 letter code) [AU]:JP
      State or Province Name (full name) [Some-State]:Tokyo
      Locality Name (eg, city) []:Adachiku
      Organization Name (eg, company) [Internet Widgits Pty Ltd]:MYDOMAIN
      Organizational Unit Name (eg, section) []:
      Common Name (e.g. server FQDN or YOUR name) []:ssl.mydomain.com
      Email Address []:
    
      Please enter the following 'extra' attributes
      to be sent with your certificate request
      A challenge password []:
      An optional company name []:
    
    # openssl req -in server.csr -text -noout
    

    サーバ証明書の作成

    今回は namecheap で [Security] | [SSL Certificates] から “Domain Validation” の “COMODO PositiveSSL” を利用する。ウィザードに従って、上記で作成した CSR をアップロードすると、時間経過後にルート/中間証明書の一式が ZIP で送付される。

    サーバ証明書の作成と Nginx の設定

    サーバ証明書、ルート証明書、中間証明書のマージ。

    # cat ssl_mydomain_com.crt COMODORSAAddTrustCA.crt COMODORSADomainValidationSecureServerCA.crt AddTrustExternalCARoot.crt > ssl.mydomain.com.crt
    

    証明書の配置と Nginx の設定変更。

    # cd /etc/nginx
    # mkdir ssl
    # cp ssl.mydomain.com.key ssl/
    # cp ssl.mydomain.com.crt ssl/
    
    # vi sites-available/ssl.domain.com
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    server {
      listen 80;
      listen 443;
      server_name ssl.mydomain.com;
    
      access_log  /var/log/nginx/ssl-access.log;
      error_log   /var/log/nginx/ssl-error.log;
    
      # for SSL
      ssl on;
      ssl_certificate     /etc/nginx/ssl/ssl.mydomain.com.crt;
      ssl_certificate_key /etc/nginx/ssl/ssl.mydomain.com.key;
      ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    
      location / {
        root   /home/ssl/public_html;
        index  index.html index.htm;
      }
    }
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    # ln -s /etc/nginx/sites-available/ssl.mydomain.com /etc/nginx/sites-enabled/ssl.mydomain.com
    
    # service nginx restart
    

    Callout での疎通確認

    事前に [管理者設定] | [セキュリティのコントロール] | [リモートサイトの設定] で Callout 先を登録する。

  • リモートサイト名: Callout endpoint
  • リモートサイトの URL: https://ssl.mydomain.com
  • 開発者コンソール等から、以下を実行。

    HttpRequest req = new HttpRequest();
    req.setEndpoint('https://ssl.mydomain.com');
    req.setMethod('GET');
    Http http = new Http();
    HttpResponse res = http.send(req);
    System.debug('zzz getStatusCode: ' + res.getStatusCode());
    System.debug('zzz getBody: ' + res.getBody());
    System.debug('zzz response: ' + res);
    

    中間証明書がない場合は以下のエラーとなる。

    System.CalloutException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    

    Hello Account (Visualforce Remote Objects)

    Visualforce Remote Objects で Hello Account。

    <apex:page>
      <apex:remoteObjects >
        <apex:remoteObjectModel name="Account" jsShorthand="Acct" fields="Id, Name">
          <apex:remoteObjectField name="Text1__c" jsShorthand="txt"/>
        </apex:remoteObjectModel>
      </apex:remoteObjects>
    
      <script>
        var doGetAccounts = function() {
          new SObjectModel.Acct().retrieve({ limit: 5 }, function(err, records, event) {
            if(err) {
              console.log(err.message);
            } else {
              records.forEach(function(record) {
                console.log(record.get("Name"));
              });
            }
          });
        };
      </script>
    
    <button onclick="doGetAccounts()">Hello Account</button>
    
    </apex:page>
    

    Hello Account (Ruby)

    Ruby(gem:databasedotcom) で Hello Account。

    $ rails console
    
    [1] pry(main)> client = Databasedotcom::Client.new(host: "test.salesforce.com", client_secret: <client secret>, client_id: <client id>)
    [2] pry(main)> client.authenticate(username: <user name>, password: <password>)
    [3] pry(main)> result = client.query("SELECT Id, Name FROM Account")
    [4] pry(main)> result.each do |record| ; p record ; end