diff --git a/cfca/pkcs10.go b/cfca/pkcs10.go index 4f0b9c0..4dc6353 100644 --- a/cfca/pkcs10.go +++ b/cfca/pkcs10.go @@ -11,6 +11,8 @@ import ( "github.com/emmansun/gmsm/smx509" ) +type CertificateRequest = smx509.CertificateRequestCFCA + // CreateCertificateRequest creates a new certificate request based on a template. // The following members of template are used: Subject. // The certPriv is the private key for the certificate, and the tmpPriv is the temporary private key for returning encryption key decryption. @@ -18,3 +20,8 @@ import ( func CreateCertificateRequest(rand io.Reader, template *x509.CertificateRequest, certPriv, tmpPriv any, challengePassword string) ([]byte, error) { return smx509.CreateCFCACertificateRequest(rand, template, certPriv, tmpPriv, challengePassword) } + +// ParseCertificateRequest parses a certificate request from the given DER data. +func ParseCertificateRequest(der []byte) (*CertificateRequest, error) { + return smx509.ParseCFCACertificateRequest(der) +} diff --git a/cfca/pkcs10_test.go b/cfca/pkcs10_test.go index 35686cf..9ced083 100644 --- a/cfca/pkcs10_test.go +++ b/cfca/pkcs10_test.go @@ -13,7 +13,6 @@ import ( "testing" "github.com/emmansun/gmsm/sm2" - "github.com/emmansun/gmsm/smx509" ) func TestCreateCertificateRequest(t *testing.T) { @@ -57,11 +56,20 @@ func TestCreateCertificateRequest(t *testing.T) { if err != nil { t.Fatal(err) } - csr, err := smx509.ParseCertificateRequest(csrDer) + csr, err := ParseCertificateRequest(csrDer) if err != nil { t.Fatal(err) } if csr.Subject.CommonName != "certRequisition" { t.Fatal("common name not match") } + if csr.Subject.CommonName != "certRequisition" { + t.Fatal("common name not match") + } + if csr.ChallengePassword != "111111" { + t.Fatal("challenge password not match") + } + if csr.TmpPublicKey == nil { + t.Fatal("tmp public key not match") + } } diff --git a/smx509/cfca_csr.go b/smx509/cfca_csr.go index ec2f76a..5355383 100644 --- a/smx509/cfca_csr.go +++ b/smx509/cfca_csr.go @@ -5,6 +5,7 @@ package smx509 import ( + "bytes" "crypto" "crypto/ecdsa" "crypto/x509" @@ -162,3 +163,56 @@ func buildTmpPublicKeyAttr(rawAttributes []asn1.RawValue, tmpPriv any) ([]asn1.R return append(rawAttributes, rawValue), nil } + +// CertificateRequestCFCA represents a CFCA certificate request. +type CertificateRequestCFCA struct { + CertificateRequest + ChallengePassword string + TmpPublicKey any +} + +// ParseCFCACertificateRequest parses a CFCA certificate request from the given DER data. +func ParseCFCACertificateRequest(asn1Data []byte) (*CertificateRequestCFCA, error) { + var csr certificateRequest + + rest, err := asn1.Unmarshal(asn1Data, &csr) + if err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, asn1.SyntaxError{Msg: "trailing data"} + } + + inner, err := parseCertificateRequest(&csr) + if err != nil { + return nil, err + } + out := &CertificateRequestCFCA{ + CertificateRequest: *inner, + } + parseCFCAAttributes(out, csr.TBSCSR.RawAttributes) + return out, nil +} + +func parseCFCAAttributes(out *CertificateRequestCFCA, rawAttributes []asn1.RawValue) { + var value struct { + Type asn1.ObjectIdentifier + Value asn1.RawValue + } + for _, attr := range rawAttributes { + if _, err := asn1.Unmarshal(attr.FullBytes, &value); err != nil { + continue + } + switch { + case value.Type.Equal(oidChallengePassword): + asn1.Unmarshal(value.Value.FullBytes, &out.ChallengePassword) + case value.Type.Equal(oidTmpPublicKey): + var keyBytes []byte + asn1.Unmarshal(value.Value.FullBytes, &keyBytes) + if len(keyBytes) == 136 && bytes.Equal(tmpPublicKeyPrefix, keyBytes[:8]) { + // parse the public key + copy(keyBytes[40:72], keyBytes[72:104]) + out.TmpPublicKey, _ = sm2.NewPublicKey(keyBytes[8:72]) + } + } + } +} diff --git a/smx509/cfca_csr_test.go b/smx509/cfca_csr_test.go index c800cee..4df658e 100644 --- a/smx509/cfca_csr_test.go +++ b/smx509/cfca_csr_test.go @@ -56,11 +56,17 @@ func TestCreateCFCACertificateRequest(t *testing.T) { if err != nil { t.Fatal(err) } - csr, err := ParseCertificateRequest(csrDer) + csr, err := ParseCFCACertificateRequest(csrDer) if err != nil { t.Fatal(err) } if csr.Subject.CommonName != "certRequisition" { t.Fatal("common name not match") } + if csr.ChallengePassword != "111111" { + t.Fatal("challenge password not match") + } + if csr.TmpPublicKey == nil { + t.Fatal("tmp public key not match") + } }