4

在 Eclipse Milo 中,客户端是否可以使用以下身份验证参数连接到服务器:“证书 + 私钥”?还有参数“安全策略”和“消息安全模式”?

(如在UAExpert客户端:http ://documentation.unified-automation.com/uaexpert/1.4.0/html/connect.html )

如果是,那么如何?


我有我的处置:

  • 一个“.perm”文件格式的私钥;
  • 一份“.der”文件格式的证书;
  • 一个“.der”文件格式的服务器CA;
  • 以及4096位“.der”文件格式的服务器CA;
4

2 回答 2

2

是的,虽然它不像现在使用用户名/密码那样“简单”,但目前是可能的。

客户端 SDK 公开了一个称为接口的接口,该接口IdentityProvider在客户端连接时委托给该接口,并获得端点和服务器随机数。它返回一个包含 aUserIdentityToken和 a的 2 元组SignatureData

您需要为X509IdentityToken案例实现此接口并返回您的证书(在 中X509IdentityToken)以及证明您拥有它的密钥(在 中SignatureData)。

一旦你有了这个IdentityProvider,你只需setIdentityProvider在构建OpcUaClientConfig对象时调用它来告诉客户端在你配置它时使用它。

由于这有点繁琐,而且 SDK 的目的是减轻用户的负担,我也会将此作为 Milo 的功能票。如果你不能自己实现它,我可以在这周完成它。

于 2016-09-13T14:13:55.030 回答
1

在Kevin Herron的帮助下,我使用他的类X509IdentityProvider解决了我的问题。

这是解决方案代码:

PemFile.java

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

public class PemFile {

    private PemObject pemObject;

    public PemFile(String filename) throws FileNotFoundException, IOException {
        PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream(filename)));
        try {
            this.pemObject = pemReader.readPemObject();
        } finally {
            pemReader.close();
        }
    }

    public PemObject getPemObject() {
        return pemObject;
    }
}

X509IdentityProvider.java

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
import org.eclipse.milo.opcua.stack.core.types.enumerated.UserTokenType;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.eclipse.milo.opcua.stack.core.types.structured.SignatureData;
import org.eclipse.milo.opcua.stack.core.types.structured.UserIdentityToken;
import org.eclipse.milo.opcua.stack.core.types.structured.UserTokenPolicy;
import org.eclipse.milo.opcua.stack.core.types.structured.X509IdentityToken;
import org.eclipse.milo.opcua.stack.core.util.SignatureUtil;
import org.jooq.lambda.tuple.Tuple2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class X509IdentityProvider implements IdentityProvider {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final X509Certificate certificate;
    private final PrivateKey privateKey;

    public X509Certificate getCertificate() {
        return certificate;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public X509IdentityProvider(X509Certificate certificate, PrivateKey privateKey) {
        this.certificate = certificate;
        this.privateKey = privateKey;
    }

    public X509IdentityProvider(String certificate, String privateKey) {
        this.certificate = loadCertificateFromDerFile(certificate);

        Security.addProvider(new BouncyCastleProvider());
        KeyFactory kf;
        PrivateKey privateKeyTmp = null;
        try {
            kf = KeyFactory.getInstance("RSA", "BC");
            privateKeyTmp = loadPrivateKeyFromPemFile(kf, privateKey);
        } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | IOException e) {
            e.printStackTrace();
        }
        this.privateKey = privateKeyTmp;

    }

    @Override
    public Tuple2<UserIdentityToken, SignatureData> getIdentityToken(EndpointDescription endpoint,
            ByteString serverNonce) throws Exception {
        UserTokenPolicy tokenPolicy = Arrays.stream(endpoint.getUserIdentityTokens())
                .filter(t -> t.getTokenType() == UserTokenType.Certificate).findFirst()
                .orElseThrow(() -> new Exception("no x509 certificate token policy found"));
        String policyId = tokenPolicy.getPolicyId();
        SecurityPolicy securityPolicy = SecurityPolicy.Basic256;
        String securityPolicyUri = tokenPolicy.getSecurityPolicyUri();
        try {
            if (securityPolicyUri != null && !securityPolicyUri.isEmpty()) {
                securityPolicy = SecurityPolicy.fromUri(securityPolicyUri);
            } else {
                securityPolicyUri = endpoint.getSecurityPolicyUri();
                securityPolicy = SecurityPolicy.fromUri(securityPolicyUri);
            }
        } catch (Throwable t) {
            logger.warn("Error parsing SecurityPolicy for uri={}", securityPolicyUri);
        }
        X509IdentityToken token = new X509IdentityToken(policyId, ByteString.of(certificate.getEncoded()));
        SignatureData signatureData;
        ByteString serverCertificate = endpoint.getServerCertificate();
        byte[] serverCertificateBytes = serverCertificate.isNotNull() ? serverCertificate.bytes() : new byte[0];
        byte[] serverNonceBytes = serverNonce.isNotNull() ? serverNonce.bytes() : new byte[0];
        assert serverCertificateBytes != null;
        assert serverNonceBytes != null;
        byte[] signature = SignatureUtil.sign(securityPolicy.getAsymmetricSignatureAlgorithm(), privateKey,
                ByteBuffer.wrap(serverCertificateBytes), ByteBuffer.wrap(serverNonceBytes));
        signatureData = new SignatureData(securityPolicy.getAsymmetricSignatureAlgorithm().getUri(),
                ByteString.of(signature));
        return new Tuple2<>(token, signatureData);
    }


    private static X509Certificate loadCertificateFromDerFile(String filename) {
        InputStream in;
        X509Certificate cert = null;
        try {
            in = new FileInputStream(filename);

            CertificateFactory factory = CertificateFactory.getInstance("X.509");
             cert = (X509Certificate) factory.generateCertificate(in);
        } catch (FileNotFoundException | CertificateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return cert;
    }

    private static PrivateKey loadPrivateKeyFromPemFile(KeyFactory factory, String filename)
            throws InvalidKeySpecException, FileNotFoundException, IOException {
        PemFile pemFile = new PemFile(filename);
        byte[] content = pemFile.getPemObject().getContent();
        PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
        return factory.generatePrivate(privKeySpec);
    }

}

ClientRunner.java

...
...

private OpcUaClient createClient() throws Exception {
        SecurityPolicy securityPolicy = clientExample.getSecurityPolicy(); // For example : SecurityPolicy.Basic256
        String securityMode = clientExample.getSecurityMode(); // For example : "SignAndEncrypt"

        EndpointDescription[] endpoints = UaTcpStackClient.getEndpoints(endpointUrl).get();

        EndpointDescription endpoint = Arrays.stream(endpoints)
                .filter(e -> e.getSecurityPolicyUri().equals(securityPolicy.getSecurityPolicyUri()))//
                .filter(e -> e.getSecurityMode().toString().compareTo(securityMode) == 0)//
                .findFirst()//
                .orElseThrow(() -> new Exception("no desired endpoints returned"));

        logger.info("Using endpoint: {} [{}]", endpoint.getEndpointUrl(), securityPolicy);

        loader.load();

        // Mode : securityPolicy == SecurityPolicy.Basic256 && securityMode.compareTo("SignAndEncrypt") == 0)
        X509IdentityProvider x509IdentityProvider = new X509IdentityProvider("/certificate.der",
                "/privateKey.pem");
        X509Certificate cert = x509IdentityProvider.getCertificate();
        KeyPair keyPair = new KeyPair(cert.getPublicKey(), x509IdentityProvider.getPrivateKey());
        OpcUaClientConfig config = OpcUaClientConfig.builder().setApplicationName(LocalizedText.english("opc-ua client"))//
                .setApplicationUri("urn:opcua client")//
                .setCertificate(cert)//
                .setKeyPair(keyPair)//
                .setEndpoint(endpoint)//
                .setIdentityProvider(x509IdentityProvider)//
                .setIdentityProvider(clientExample.getIdentityProvider())//
                .setRequestTimeout(uint(5000))//
                .build();


        return new OpcUaClient(config);
    }
于 2016-09-15T15:53:48.117 回答